From 9854c4e40e98afb72c5e5863054b8e2b54f59410 Mon Sep 17 00:00:00 2001 From: Pim Snel Date: Fri, 9 Feb 2024 23:38:13 +0100 Subject: [PATCH] adapt workflow to new visual gallery --- .github/workflows/generate-json.yml | 21 +- .github/workflows/getmeta.sh | 4 +- .gitignore | 82 +- LICENSE | 62 +- archetypes/default.md | 6 + archetypes/ssg.md | 7 + archetypes/theme.md | 33 + config.toml | 55 + content/_index.md | 3 + content/archetype/_index.md | 4 + content/archetype/admin/_index.md | 4 + content/archetype/blog/_index.md | 4 + content/archetype/business/_index.md | 4 + content/archetype/documentation/_index.md | 4 + content/archetype/ecommerce/_index.md | 4 + content/archetype/event/_index.md | 4 + content/archetype/personal/_index.md | 4 + content/archetype/portfolio/_index.md | 4 + content/archetype/single-page/_index.md | 4 + content/css/_index.md | 4 + content/css/bootstrap/_index.md | 5 + content/css/bulma/_index.md | 4 + content/css/css/_index.md | 4 + content/css/material-ui/_index.md | 6 + content/css/postcss/_index.md | 5 + content/css/scss/_index.md | 4 + content/css/styled-components/_index.md | 5 + content/css/stylus/_index.md | 5 + content/css/tailwind/_index.md | 5 + content/css/vuetify/_index.md | 5 + content/services/_index.md | 4 + content/services/algolia/_index.md | 5 + content/services/formspree/_index.md | 5 + content/services/formstack/_index.md | 5 + content/services/mailchimp/_index.md | 5 + content/services/shopify/_index.md | 5 + content/services/snipcart/_index.md | 5 + content/theme/hugo-theme-tailwind.md | 36 + data/errors.json | 22 + data/stackbit.json | 1 + data/themes.json | 25 + package.json | 26 + scripts/build.js | 59 + scripts/build/cleanup.js | 23 + scripts/build/config.js | 30 + scripts/build/demo.js | 67 + scripts/build/errors.js | 6 + scripts/build/github.js | 135 + scripts/build/markdown.js | 133 + scripts/build/screenshot.js | 99 + scripts/build/stackbit.js | 91 + scripts/build/utils.js | 20 + scripts/build/write.js | 53 + static/images/banner/bg.svg | 188 + static/images/banner/netlify-create.svg | 1 + static/images/default-large.png | Bin 0 -> 3138 bytes static/images/default.png | Bin 0 -> 773 bytes static/images/favicon/02_QUIQR-icon-logo.png | Bin 0 -> 9857 bytes static/images/favicon/favicon-32x32.png | Bin 0 -> 1390 bytes static/images/icons/admin.svg | 1 + static/images/icons/airtable.svg | 1 + static/images/icons/algolia.svg | 1 + static/images/icons/astro.svg | 4 + static/images/icons/blog.svg | 1 + static/images/icons/bootstrap.svg | 1 + static/images/icons/brunch.svg | 1 + static/images/icons/bulma.svg | 1 + static/images/icons/business.svg | 1 + static/images/icons/cecil.svg | 33 + static/images/icons/contentful.svg | 1 + static/images/icons/cosmic.svg | 1 + static/images/icons/css.svg | 23 + static/images/icons/datocms.svg | 1 + static/images/icons/directus.svg | 1 + static/images/icons/documentation.svg | 1 + static/images/icons/docusaurus.svg | 1 + static/images/icons/ecommerce.svg | 1 + static/images/icons/eleventy.svg | 1 + static/images/icons/event.svg | 1 + static/images/icons/firebase.svg | 1 + static/images/icons/flotiq.svg | 1 + static/images/icons/forestry.svg | 1 + static/images/icons/formspree.svg | 1 + static/images/icons/formstack.svg | 1 + static/images/icons/gatsby-bw.svg | 1 + static/images/icons/gatsby.svg | 1 + static/images/icons/ghost.svg | 1 + static/images/icons/github.svg | 1 + static/images/icons/graphcms.svg | 1 + static/images/icons/gridsome.svg | 1 + static/images/icons/hexo.svg | 1 + static/images/icons/hugo-bw.svg | 1 + static/images/icons/hugo.svg | 1 + static/images/icons/hyde.svg | 24 + static/images/icons/jekyll.svg | 1 + static/images/icons/jigsaw.svg | 1 + static/images/icons/kontent.svg | 1 + static/images/icons/mailchimp.svg | 1 + static/images/icons/material-ui.svg | 9 + static/images/icons/metalsmith.svg | 1 + static/images/icons/middleman.svg | 1 + static/images/icons/mkdocs.svg | 1 + static/images/icons/netlify.svg | 1 + static/images/icons/netlifycms.svg | 1 + static/images/icons/next.svg | 1 + static/images/icons/no-cms.svg | 1 + static/images/icons/nocms.svg | 1 + static/images/icons/notion.svg | 1 + static/images/icons/nuxt.svg | 1 + static/images/icons/pelican.svg | 1 + static/images/icons/personal.svg | 1 + static/images/icons/platframe.svg | 1 + static/images/icons/portfolio.svg | 1 + static/images/icons/postcss.svg | 1 + static/images/icons/prismic.svg | 1 + static/images/icons/sanity.svg | 1 + static/images/icons/sapper.svg | 1 + static/images/icons/sass.svg | 1 + static/images/icons/scss.svg | 1 + static/images/icons/scully.svg | 1 + static/images/icons/shopify.svg | 1 + static/images/icons/single-page.svg | 1 + static/images/icons/snipcart.svg | 1 + static/images/icons/stackbit.svg | 4 + static/images/icons/staticman.svg | 1 + static/images/icons/statiq.svg | 1 + static/images/icons/storyblok.svg | 1 + static/images/icons/styled-components.png | Bin 0 -> 6422 bytes static/images/icons/stylus.svg | 79 + static/images/icons/sveltekit.svg | 7 + static/images/icons/tailwind.svg | 1 + static/images/icons/tina.svg | 4 + static/images/icons/unibit-pink.svg | 1 + static/images/icons/unibit.svg | 1 + static/images/icons/vuepress.svg | 1 + static/images/icons/vuetify.svg | 9 + static/images/icons/w3css.svg | 1 + static/images/icons/wordpress.svg | 1 + static/images/icons/zola.svg | 4 + static/images/jamstackthemes-screenshot.png | Bin 0 -> 312777 bytes static/images/logo-nav.svg | 142 + static/images/social/github.svg | 1 + static/images/social/twitter.svg | 1 + static/images/stackbit.png | Bin 0 -> 2999 bytes static/images/ui/angle-double-left-solid.svg | 1 + static/images/ui/angle-double-right-solid.svg | 1 + static/images/ui/check-solid.svg | 1 + static/images/ui/chevron-down-solid.svg | 1 + static/images/ui/chevron-left-solid.svg | 1 + static/images/ui/chevron-right-solid.svg | 1 + static/images/ui/external-link-alt-solid.svg | 1 + static/images/ui/forks.svg | 1 + static/images/ui/history-solid.svg | 1 + static/images/ui/issues.svg | 1 + static/images/ui/minus-solid.svg | 1 + static/images/ui/minus-square-regular.svg | 1 + static/images/ui/plus-solid.svg | 1 + static/images/ui/plus-square-regular.svg | 1 + static/images/ui/star-solid.svg | 1 + static/images/ui/star.svg | 1 + .../assets/js/filter/filter-groups.js | 46 + .../assets/js/filter/filter-hash.js | 182 + .../assets/js/filter/filter-toggle.js | 11 + .../jamstackthemes/assets/js/filter/filter.js | 116 + .../assets/js/libs/mixitup-multifilter.js | 1256 ++ .../assets/js/libs/mixitup-pagination.js | 1974 +++ .../jamstackthemes/assets/js/libs/mixitup.js | 10682 ++++++++++++++++ themes/jamstackthemes/assets/js/scripts.js | 0 .../assets/scss/_breadcrumbs.scss | 32 + .../jamstackthemes/assets/scss/_buttons.scss | 54 + .../jamstackthemes/assets/scss/_content.scss | 103 + themes/jamstackthemes/assets/scss/_demo.scss | 152 + .../jamstackthemes/assets/scss/_filter.scss | 298 + .../jamstackthemes/assets/scss/_footer.scss | 66 + .../assets/scss/_github-metrics.scss | 30 + .../jamstackthemes/assets/scss/_header.scss | 114 + themes/jamstackthemes/assets/scss/_icon.scss | 44 + themes/jamstackthemes/assets/scss/_intro.scss | 152 + themes/jamstackthemes/assets/scss/_label.scss | 26 + .../jamstackthemes/assets/scss/_layout.scss | 172 + .../assets/scss/_pagination.scss | 62 + themes/jamstackthemes/assets/scss/_star.scss | 30 + themes/jamstackthemes/assets/scss/_terms.scss | 55 + .../assets/scss/_theme-buttons.scss | 67 + .../assets/scss/_theme-card.scss | 122 + .../assets/scss/_theme-detail.scss | 133 + .../assets/scss/bootstrap/_alert.scss | 51 + .../assets/scss/bootstrap/_badge.scss | 54 + .../assets/scss/bootstrap/_breadcrumb.scss | 42 + .../assets/scss/bootstrap/_button-group.scss | 163 + .../assets/scss/bootstrap/_buttons.scss | 139 + .../assets/scss/bootstrap/_card.scss | 278 + .../assets/scss/bootstrap/_carousel.scss | 197 + .../assets/scss/bootstrap/_close.scss | 41 + .../assets/scss/bootstrap/_code.scss | 48 + .../assets/scss/bootstrap/_custom-forms.scss | 521 + .../assets/scss/bootstrap/_dropdown.scss | 191 + .../assets/scss/bootstrap/_forms.scss | 338 + .../assets/scss/bootstrap/_functions.scss | 134 + .../assets/scss/bootstrap/_grid.scss | 69 + .../assets/scss/bootstrap/_images.scss | 42 + .../assets/scss/bootstrap/_input-group.scss | 191 + .../assets/scss/bootstrap/_jumbotron.scss | 17 + .../assets/scss/bootstrap/_list-group.scss | 158 + .../assets/scss/bootstrap/_media.scss | 8 + .../assets/scss/bootstrap/_mixins.scss | 47 + .../assets/scss/bootstrap/_modal.scss | 239 + .../assets/scss/bootstrap/_nav.scss | 120 + .../assets/scss/bootstrap/_navbar.scss | 324 + .../assets/scss/bootstrap/_pagination.scss | 73 + .../assets/scss/bootstrap/_popover.scss | 170 + .../assets/scss/bootstrap/_print.scss | 141 + .../assets/scss/bootstrap/_progress.scss | 46 + .../assets/scss/bootstrap/_reboot.scss | 482 + .../assets/scss/bootstrap/_root.scss | 20 + .../assets/scss/bootstrap/_spinners.scss | 55 + .../assets/scss/bootstrap/_tables.scss | 185 + .../assets/scss/bootstrap/_toasts.scss | 44 + .../assets/scss/bootstrap/_tooltip.scss | 115 + .../assets/scss/bootstrap/_transitions.scss | 20 + .../assets/scss/bootstrap/_type.scss | 125 + .../assets/scss/bootstrap/_utilities.scss | 17 + .../assets/scss/bootstrap/_variables.scss | 1143 ++ .../assets/scss/bootstrap/bootstrap-grid.scss | 29 + .../scss/bootstrap/bootstrap-reboot.scss | 12 + .../assets/scss/bootstrap/bootstrap.scss | 44 + .../assets/scss/bootstrap/mixins/_alert.scss | 13 + .../bootstrap/mixins/_background-variant.scss | 22 + .../assets/scss/bootstrap/mixins/_badge.scss | 17 + .../scss/bootstrap/mixins/_border-radius.scss | 63 + .../scss/bootstrap/mixins/_box-shadow.scss | 20 + .../scss/bootstrap/mixins/_breakpoints.scss | 123 + .../scss/bootstrap/mixins/_buttons.scss | 110 + .../assets/scss/bootstrap/mixins/_caret.scss | 62 + .../scss/bootstrap/mixins/_clearfix.scss | 7 + .../scss/bootstrap/mixins/_deprecate.scss | 10 + .../assets/scss/bootstrap/mixins/_float.scss | 14 + .../assets/scss/bootstrap/mixins/_forms.scss | 177 + .../scss/bootstrap/mixins/_gradients.scss | 45 + .../bootstrap/mixins/_grid-framework.scss | 71 + .../assets/scss/bootstrap/mixins/_grid.scss | 69 + .../assets/scss/bootstrap/mixins/_hover.scss | 37 + .../assets/scss/bootstrap/mixins/_image.scss | 36 + .../scss/bootstrap/mixins/_list-group.scss | 21 + .../assets/scss/bootstrap/mixins/_lists.scss | 7 + .../scss/bootstrap/mixins/_nav-divider.scss | 11 + .../scss/bootstrap/mixins/_pagination.scss | 22 + .../scss/bootstrap/mixins/_reset-text.scss | 17 + .../assets/scss/bootstrap/mixins/_resize.scss | 6 + .../scss/bootstrap/mixins/_screen-reader.scss | 34 + .../assets/scss/bootstrap/mixins/_size.scss | 7 + .../scss/bootstrap/mixins/_table-row.scss | 39 + .../scss/bootstrap/mixins/_text-emphasis.scss | 17 + .../scss/bootstrap/mixins/_text-hide.scss | 11 + .../scss/bootstrap/mixins/_text-truncate.scss | 8 + .../scss/bootstrap/mixins/_transition.scss | 16 + .../scss/bootstrap/mixins/_visibility.scss | 8 + .../scss/bootstrap/utilities/_align.scss | 8 + .../scss/bootstrap/utilities/_background.scss | 19 + .../scss/bootstrap/utilities/_borders.scss | 75 + .../scss/bootstrap/utilities/_clearfix.scss | 3 + .../scss/bootstrap/utilities/_display.scss | 26 + .../scss/bootstrap/utilities/_embed.scss | 39 + .../scss/bootstrap/utilities/_flex.scss | 51 + .../scss/bootstrap/utilities/_float.scss | 11 + .../scss/bootstrap/utilities/_overflow.scss | 5 + .../scss/bootstrap/utilities/_position.scss | 32 + .../bootstrap/utilities/_screenreaders.scss | 11 + .../scss/bootstrap/utilities/_shadows.scss | 6 + .../scss/bootstrap/utilities/_sizing.scss | 20 + .../scss/bootstrap/utilities/_spacing.scss | 73 + .../bootstrap/utilities/_stretched-link.scss | 19 + .../scss/bootstrap/utilities/_text.scss | 72 + .../scss/bootstrap/utilities/_visibility.scss | 13 + .../assets/scss/bootstrap/vendor/_rfs.scss | 204 + .../assets/scss/pygments/github.scss | 62 + themes/jamstackthemes/assets/scss/style.scss | 89 + themes/jamstackthemes/layouts/404.html | 5 + .../layouts/_default/baseof.html | 76 + .../jamstackthemes/layouts/_default/list.html | 10 + .../layouts/_default/single.html | 7 + .../layouts/_default/taxonomy.html | 48 + .../layouts/_default/terms.html | 24 + themes/jamstackthemes/layouts/index.html | 85 + themes/jamstackthemes/layouts/index.json | 7 + themes/jamstackthemes/layouts/index.rss.xml | 74 + .../layouts/partials/breadcrumbs.html | 18 + .../layouts/partials/filters.html | 119 + .../layouts/partials/footer.html | 38 + .../layouts/partials/header.html | 11 + .../partials/icons/icon-link-card.html | 3 + .../partials/icons/icon-link-inline-svg.html | 8 + .../layouts/partials/icons/icon-link.html | 3 + .../layouts/partials/icons/icon.html | 3 + .../layouts/partials/labels/label-author.html | 4 + .../partials/labels/label-last-commit.html | 4 + .../layouts/partials/labels/label-ssg.html | 5 + .../layouts/partials/labels/label-stars.html | 5 + .../partials/labels/label-submitted-on.html | 4 + .../layouts/partials/main-menu.html | 17 + .../layouts/partials/metrics/ga.html | 11 + .../layouts/partials/metrics/segment.html | 12 + .../layouts/partials/pagination.html | 115 + .../layouts/partials/sort-buttons.html | 7 + .../jamstackthemes/layouts/partials/tags.html | 34 + .../layouts/partials/term-card.html | 7 + .../layouts/partials/theme-card.html | 160 + .../layouts/partials/toggle-icon.html | 3 + .../layouts/partials/toggle-more.html | 8 + themes/jamstackthemes/layouts/theme/list.html | 60 + .../layouts/theme/single.demo.html | 102 + .../jamstackthemes/layouts/theme/single.html | 147 + themes/jamstackthemes/theme.toml | 8 + 313 files changed, 27114 insertions(+), 50 deletions(-) create mode 100644 archetypes/default.md create mode 100644 archetypes/ssg.md create mode 100644 archetypes/theme.md create mode 100644 config.toml create mode 100644 content/_index.md create mode 100644 content/archetype/_index.md create mode 100644 content/archetype/admin/_index.md create mode 100644 content/archetype/blog/_index.md create mode 100644 content/archetype/business/_index.md create mode 100644 content/archetype/documentation/_index.md create mode 100644 content/archetype/ecommerce/_index.md create mode 100644 content/archetype/event/_index.md create mode 100644 content/archetype/personal/_index.md create mode 100644 content/archetype/portfolio/_index.md create mode 100644 content/archetype/single-page/_index.md create mode 100644 content/css/_index.md create mode 100644 content/css/bootstrap/_index.md create mode 100644 content/css/bulma/_index.md create mode 100644 content/css/css/_index.md create mode 100644 content/css/material-ui/_index.md create mode 100644 content/css/postcss/_index.md create mode 100644 content/css/scss/_index.md create mode 100644 content/css/styled-components/_index.md create mode 100644 content/css/stylus/_index.md create mode 100644 content/css/tailwind/_index.md create mode 100644 content/css/vuetify/_index.md create mode 100644 content/services/_index.md create mode 100644 content/services/algolia/_index.md create mode 100644 content/services/formspree/_index.md create mode 100644 content/services/formstack/_index.md create mode 100644 content/services/mailchimp/_index.md create mode 100644 content/services/shopify/_index.md create mode 100644 content/services/snipcart/_index.md create mode 100644 content/theme/hugo-theme-tailwind.md create mode 100644 data/errors.json create mode 100644 data/stackbit.json create mode 100644 data/themes.json create mode 100644 package.json create mode 100644 scripts/build.js create mode 100644 scripts/build/cleanup.js create mode 100644 scripts/build/config.js create mode 100644 scripts/build/demo.js create mode 100644 scripts/build/errors.js create mode 100755 scripts/build/github.js create mode 100644 scripts/build/markdown.js create mode 100644 scripts/build/screenshot.js create mode 100755 scripts/build/stackbit.js create mode 100644 scripts/build/utils.js create mode 100644 scripts/build/write.js create mode 100644 static/images/banner/bg.svg create mode 100644 static/images/banner/netlify-create.svg create mode 100644 static/images/default-large.png create mode 100644 static/images/default.png create mode 100644 static/images/favicon/02_QUIQR-icon-logo.png create mode 100755 static/images/favicon/favicon-32x32.png create mode 100644 static/images/icons/admin.svg create mode 100755 static/images/icons/airtable.svg create mode 100644 static/images/icons/algolia.svg create mode 100644 static/images/icons/astro.svg create mode 100644 static/images/icons/blog.svg create mode 100644 static/images/icons/bootstrap.svg create mode 100644 static/images/icons/brunch.svg create mode 100644 static/images/icons/bulma.svg create mode 100644 static/images/icons/business.svg create mode 100644 static/images/icons/cecil.svg create mode 100644 static/images/icons/contentful.svg create mode 100644 static/images/icons/cosmic.svg create mode 100644 static/images/icons/css.svg create mode 100644 static/images/icons/datocms.svg create mode 100644 static/images/icons/directus.svg create mode 100644 static/images/icons/documentation.svg create mode 100644 static/images/icons/docusaurus.svg create mode 100644 static/images/icons/ecommerce.svg create mode 100644 static/images/icons/eleventy.svg create mode 100644 static/images/icons/event.svg create mode 100644 static/images/icons/firebase.svg create mode 100644 static/images/icons/flotiq.svg create mode 100644 static/images/icons/forestry.svg create mode 100644 static/images/icons/formspree.svg create mode 100644 static/images/icons/formstack.svg create mode 100644 static/images/icons/gatsby-bw.svg create mode 100755 static/images/icons/gatsby.svg create mode 100644 static/images/icons/ghost.svg create mode 100644 static/images/icons/github.svg create mode 100644 static/images/icons/graphcms.svg create mode 100644 static/images/icons/gridsome.svg create mode 100755 static/images/icons/hexo.svg create mode 100644 static/images/icons/hugo-bw.svg create mode 100644 static/images/icons/hugo.svg create mode 100644 static/images/icons/hyde.svg create mode 100644 static/images/icons/jekyll.svg create mode 100644 static/images/icons/jigsaw.svg create mode 100644 static/images/icons/kontent.svg create mode 100644 static/images/icons/mailchimp.svg create mode 100644 static/images/icons/material-ui.svg create mode 100644 static/images/icons/metalsmith.svg create mode 100644 static/images/icons/middleman.svg create mode 100644 static/images/icons/mkdocs.svg create mode 100644 static/images/icons/netlify.svg create mode 100644 static/images/icons/netlifycms.svg create mode 100644 static/images/icons/next.svg create mode 100644 static/images/icons/no-cms.svg create mode 100644 static/images/icons/nocms.svg create mode 100644 static/images/icons/notion.svg create mode 100644 static/images/icons/nuxt.svg create mode 100644 static/images/icons/pelican.svg create mode 100644 static/images/icons/personal.svg create mode 100644 static/images/icons/platframe.svg create mode 100644 static/images/icons/portfolio.svg create mode 100644 static/images/icons/postcss.svg create mode 100644 static/images/icons/prismic.svg create mode 100644 static/images/icons/sanity.svg create mode 100644 static/images/icons/sapper.svg create mode 100644 static/images/icons/sass.svg create mode 100644 static/images/icons/scss.svg create mode 100644 static/images/icons/scully.svg create mode 100644 static/images/icons/shopify.svg create mode 100644 static/images/icons/single-page.svg create mode 100644 static/images/icons/snipcart.svg create mode 100644 static/images/icons/stackbit.svg create mode 100644 static/images/icons/staticman.svg create mode 100644 static/images/icons/statiq.svg create mode 100644 static/images/icons/storyblok.svg create mode 100644 static/images/icons/styled-components.png create mode 100644 static/images/icons/stylus.svg create mode 100644 static/images/icons/sveltekit.svg create mode 100644 static/images/icons/tailwind.svg create mode 100644 static/images/icons/tina.svg create mode 100644 static/images/icons/unibit-pink.svg create mode 100644 static/images/icons/unibit.svg create mode 100644 static/images/icons/vuepress.svg create mode 100644 static/images/icons/vuetify.svg create mode 100644 static/images/icons/w3css.svg create mode 100644 static/images/icons/wordpress.svg create mode 100644 static/images/icons/zola.svg create mode 100644 static/images/jamstackthemes-screenshot.png create mode 100644 static/images/logo-nav.svg create mode 100755 static/images/social/github.svg create mode 100755 static/images/social/twitter.svg create mode 100644 static/images/stackbit.png create mode 100644 static/images/ui/angle-double-left-solid.svg create mode 100644 static/images/ui/angle-double-right-solid.svg create mode 100644 static/images/ui/check-solid.svg create mode 100644 static/images/ui/chevron-down-solid.svg create mode 100644 static/images/ui/chevron-left-solid.svg create mode 100644 static/images/ui/chevron-right-solid.svg create mode 100644 static/images/ui/external-link-alt-solid.svg create mode 100644 static/images/ui/forks.svg create mode 100644 static/images/ui/history-solid.svg create mode 100644 static/images/ui/issues.svg create mode 100644 static/images/ui/minus-solid.svg create mode 100644 static/images/ui/minus-square-regular.svg create mode 100644 static/images/ui/plus-solid.svg create mode 100644 static/images/ui/plus-square-regular.svg create mode 100644 static/images/ui/star-solid.svg create mode 100644 static/images/ui/star.svg create mode 100644 themes/jamstackthemes/assets/js/filter/filter-groups.js create mode 100644 themes/jamstackthemes/assets/js/filter/filter-hash.js create mode 100644 themes/jamstackthemes/assets/js/filter/filter-toggle.js create mode 100644 themes/jamstackthemes/assets/js/filter/filter.js create mode 100755 themes/jamstackthemes/assets/js/libs/mixitup-multifilter.js create mode 100755 themes/jamstackthemes/assets/js/libs/mixitup-pagination.js create mode 100755 themes/jamstackthemes/assets/js/libs/mixitup.js create mode 100644 themes/jamstackthemes/assets/js/scripts.js create mode 100644 themes/jamstackthemes/assets/scss/_breadcrumbs.scss create mode 100644 themes/jamstackthemes/assets/scss/_buttons.scss create mode 100644 themes/jamstackthemes/assets/scss/_content.scss create mode 100644 themes/jamstackthemes/assets/scss/_demo.scss create mode 100644 themes/jamstackthemes/assets/scss/_filter.scss create mode 100644 themes/jamstackthemes/assets/scss/_footer.scss create mode 100644 themes/jamstackthemes/assets/scss/_github-metrics.scss create mode 100644 themes/jamstackthemes/assets/scss/_header.scss create mode 100644 themes/jamstackthemes/assets/scss/_icon.scss create mode 100644 themes/jamstackthemes/assets/scss/_intro.scss create mode 100644 themes/jamstackthemes/assets/scss/_label.scss create mode 100644 themes/jamstackthemes/assets/scss/_layout.scss create mode 100644 themes/jamstackthemes/assets/scss/_pagination.scss create mode 100644 themes/jamstackthemes/assets/scss/_star.scss create mode 100644 themes/jamstackthemes/assets/scss/_terms.scss create mode 100644 themes/jamstackthemes/assets/scss/_theme-buttons.scss create mode 100644 themes/jamstackthemes/assets/scss/_theme-card.scss create mode 100644 themes/jamstackthemes/assets/scss/_theme-detail.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_alert.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_badge.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_breadcrumb.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_button-group.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_buttons.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_card.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_carousel.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_close.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_code.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_custom-forms.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_dropdown.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_forms.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_functions.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_grid.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_images.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_input-group.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_jumbotron.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_list-group.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_media.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_mixins.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_modal.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_nav.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_navbar.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_pagination.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_popover.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_print.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_progress.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_reboot.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_root.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_spinners.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_tables.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_toasts.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_tooltip.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_transitions.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_type.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_utilities.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/_variables.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/bootstrap-grid.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/bootstrap-reboot.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/bootstrap.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_alert.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_background-variant.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_badge.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_border-radius.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_box-shadow.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_breakpoints.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_buttons.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_caret.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_clearfix.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_deprecate.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_float.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_forms.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_gradients.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_grid-framework.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_grid.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_hover.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_image.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_list-group.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_lists.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_nav-divider.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_pagination.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_reset-text.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_resize.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_screen-reader.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_size.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_table-row.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_text-emphasis.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_text-hide.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_text-truncate.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_transition.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/mixins/_visibility.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/utilities/_align.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/utilities/_background.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/utilities/_borders.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/utilities/_clearfix.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/utilities/_display.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/utilities/_embed.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/utilities/_flex.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/utilities/_float.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/utilities/_overflow.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/utilities/_position.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/utilities/_screenreaders.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/utilities/_shadows.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/utilities/_sizing.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/utilities/_spacing.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/utilities/_stretched-link.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/utilities/_text.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/utilities/_visibility.scss create mode 100644 themes/jamstackthemes/assets/scss/bootstrap/vendor/_rfs.scss create mode 100644 themes/jamstackthemes/assets/scss/pygments/github.scss create mode 100755 themes/jamstackthemes/assets/scss/style.scss create mode 100644 themes/jamstackthemes/layouts/404.html create mode 100644 themes/jamstackthemes/layouts/_default/baseof.html create mode 100644 themes/jamstackthemes/layouts/_default/list.html create mode 100644 themes/jamstackthemes/layouts/_default/single.html create mode 100644 themes/jamstackthemes/layouts/_default/taxonomy.html create mode 100644 themes/jamstackthemes/layouts/_default/terms.html create mode 100644 themes/jamstackthemes/layouts/index.html create mode 100644 themes/jamstackthemes/layouts/index.json create mode 100644 themes/jamstackthemes/layouts/index.rss.xml create mode 100644 themes/jamstackthemes/layouts/partials/breadcrumbs.html create mode 100644 themes/jamstackthemes/layouts/partials/filters.html create mode 100644 themes/jamstackthemes/layouts/partials/footer.html create mode 100644 themes/jamstackthemes/layouts/partials/header.html create mode 100644 themes/jamstackthemes/layouts/partials/icons/icon-link-card.html create mode 100644 themes/jamstackthemes/layouts/partials/icons/icon-link-inline-svg.html create mode 100644 themes/jamstackthemes/layouts/partials/icons/icon-link.html create mode 100644 themes/jamstackthemes/layouts/partials/icons/icon.html create mode 100644 themes/jamstackthemes/layouts/partials/labels/label-author.html create mode 100644 themes/jamstackthemes/layouts/partials/labels/label-last-commit.html create mode 100644 themes/jamstackthemes/layouts/partials/labels/label-ssg.html create mode 100644 themes/jamstackthemes/layouts/partials/labels/label-stars.html create mode 100644 themes/jamstackthemes/layouts/partials/labels/label-submitted-on.html create mode 100644 themes/jamstackthemes/layouts/partials/main-menu.html create mode 100644 themes/jamstackthemes/layouts/partials/metrics/ga.html create mode 100644 themes/jamstackthemes/layouts/partials/metrics/segment.html create mode 100644 themes/jamstackthemes/layouts/partials/pagination.html create mode 100644 themes/jamstackthemes/layouts/partials/sort-buttons.html create mode 100644 themes/jamstackthemes/layouts/partials/tags.html create mode 100644 themes/jamstackthemes/layouts/partials/term-card.html create mode 100644 themes/jamstackthemes/layouts/partials/theme-card.html create mode 100644 themes/jamstackthemes/layouts/partials/toggle-icon.html create mode 100644 themes/jamstackthemes/layouts/partials/toggle-more.html create mode 100644 themes/jamstackthemes/layouts/theme/list.html create mode 100644 themes/jamstackthemes/layouts/theme/single.demo.html create mode 100644 themes/jamstackthemes/layouts/theme/single.html create mode 100644 themes/jamstackthemes/theme.toml diff --git a/.github/workflows/generate-json.yml b/.github/workflows/generate-json.yml index eae447e..8ad8628 100644 --- a/.github/workflows/generate-json.yml +++ b/.github/workflows/generate-json.yml @@ -26,6 +26,15 @@ jobs: with: fetch-depth: 0 + - name: Setup Hugo + uses: peaceiris/actions-hugo@v2 + with: + hugo-version: '0.81.0' + extended: true + + - name: Build + run: hugo --minify --baseURL https://quiqr.github.io/quiqr-community-templates/ + - name: Extract Links id: extralinks uses: mipmip/github-action-markdown-link-extract-to-json@v1.4 @@ -40,14 +49,14 @@ jobs: - name: parse and flatten repo meta run: ./.github/workflows/getmeta.sh - - name: download md2html - run: cd /tmp && wget -c https://github.com/nocd5/md2html/releases/download/v2.0.0/md2html_linux_amd64.zip && unzip md2html_linux_amd64.zip - - - name: convert README.md with md2html - run: /tmp/md2html README.md -o index.html +# - name: download md2html +# run: cd /tmp && wget -c https://github.com/nocd5/md2html/releases/download/v2.0.0/md2html_linux_amd64.zip && unzip md2html_linux_amd64.zip +# +# - name: convert README.md with md2html +# run: /tmp/md2html README.md -o index.html - name: Deploy uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: . + publish_dir: ./public diff --git a/.github/workflows/getmeta.sh b/.github/workflows/getmeta.sh index cc72fa9..a83ac62 100755 --- a/.github/workflows/getmeta.sh +++ b/.github/workflows/getmeta.sh @@ -43,6 +43,6 @@ do done rm -f templates_tmp.json -jq -s 'flatten' _*.json > templates.json +jq -s 'flatten' _*.json > public/static/templates.json rm -f _*.json -cat templates.json +cat public/static/templates.json diff --git a/.gitignore b/.gitignore index d003be3..e7407c5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,80 @@ -.temp.html -.temp.seed +# Hugo Theme +resources +public + +# OSX +.DS_Store + +# Jetbrains +.idea + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ +package-lock.json + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# next.js build output +.next + +# Test Content +content/test + +# Visual Studio Code project settings +.vscode +# Local Netlify folder +.netlify diff --git a/LICENSE b/LICENSE index 3ad65fd..fd115a8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,40 +1,22 @@ -CC0 1.0 Universal - -Statement of Purpose - -The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). - -Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. - -For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. - -Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: -i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; - -ii. moral rights retained by the original author(s) and/or performer(s); - -iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; - -iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; - -v. rights protecting the extraction, dissemination, use and reuse of data in a Work; - -vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and - -vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. - -Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. - -Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. - -Limitations and Disclaimers. - -a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. - -b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. - -c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. - -d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. - -For more information, please see https://creativecommons.org/publicdomain/zero/1.0 +MIT License + +Copyright (c) 2019 jamstackthemes.dev - rob@stackbit.com +Copyright (c) 2021-2024 quiqr.org - post@pimsnel.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/archetypes/default.md b/archetypes/default.md new file mode 100644 index 0000000..00e77bd --- /dev/null +++ b/archetypes/default.md @@ -0,0 +1,6 @@ +--- +title: "{{ replace .Name "-" " " | title }}" +date: {{ .Date }} +draft: true +--- + diff --git a/archetypes/ssg.md b/archetypes/ssg.md new file mode 100644 index 0000000..111df5b --- /dev/null +++ b/archetypes/ssg.md @@ -0,0 +1,7 @@ +--- +title: "{{ replace .Name "-" " " | title }}" +icon: images/icons/docusaurus.svg +official_url: https://docusaurus.io/ +vitalstats_url: https://www.staticgen.com/docusaurus +taxonomy: ssg +--- diff --git a/archetypes/theme.md b/archetypes/theme.md new file mode 100644 index 0000000..6456e1a --- /dev/null +++ b/archetypes/theme.md @@ -0,0 +1,33 @@ +--- +title: "{{ replace .Name "-" " " | title }}" +github: https://github.com/username/repo +github_branch: master +demo: https://www.demo.com +author: Github Author +ssg: + - Gridsome +cms: + - No CMS +css: + - Bootstrap +archetype: + - Blog + - Portfolio +date: 2019-06-11 +description: This theme is a lightweight Gridsome starter kit which is perfect for a blog or a portfolio +--- + +# A simple starter kit for Gridsome + +This theme is a lightweight starter kit. It also gives you a well organised starting point to extend it for yourself. + +## Features + +* Customisable design tokens to make it your own +* Customisable global data and navigation +* Tags and tag archives +* Progressively enhanced, semantic and accessible +* _Super_ lightweight front-end +* Sass powered CSS system with utility class generator +* Service worker that caches pages so people can read your articles offline +* An RSS feed for your posts diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..c422c35 --- /dev/null +++ b/config.toml @@ -0,0 +1,55 @@ +baseURL = "/" +languageCode = "en-us" +title = "Quiqr Templates" +theme = "jamstackthemes" +paginate = 18 +summaryLength = 10 +# enableGitInfo = false + +pygmentsCodeFences = true +pygmentsCodefencesGuessSyntax = true +pygmentsUseClasses = true + +[params] + description = "Templates and starters for the Quiqr Static website CMS." + stackbit_button = true + iframe_demo = true + assets_url = "https://assets.jamstackthemes.dev" + + [theme_card] + show_archetype_icons = false + show_services_icons = false + +[taxonomies] + ssg = "ssg" + cms = "cms" + css = "css" + archetype = "archetype" + services = "services" + +[outputFormats] + [outputFormats.demo] + name = "demo" + mediaType = "text/html" + isHTML = "true" + path = "demo" + permalinkable = "true" + +[outputs] + home = ["HTML","RSS","JSON"] + section = ["HTML","RSS"] + page = ["HTML", "demo"] + +[frontmatter] + lastmod = [":git", "lastmod", ":fileModTime", ":default"] + +# Main Menu +[[menu.main]] + name = "New Themes" + url = "/" + weight = 1 + +[[menu.main]] + name = "All Themes" + url = "/theme/" + weight = 2 diff --git a/content/_index.md b/content/_index.md new file mode 100644 index 0000000..5bea243 --- /dev/null +++ b/content/_index.md @@ -0,0 +1,3 @@ +--- +title: 'Jamstack Themes' +--- diff --git a/content/archetype/_index.md b/content/archetype/_index.md new file mode 100644 index 0000000..42b9ea3 --- /dev/null +++ b/content/archetype/_index.md @@ -0,0 +1,4 @@ +--- +title: Archetype +weight: 4 +--- \ No newline at end of file diff --git a/content/archetype/admin/_index.md b/content/archetype/admin/_index.md new file mode 100644 index 0000000..544a47b --- /dev/null +++ b/content/archetype/admin/_index.md @@ -0,0 +1,4 @@ +--- +title: "Admin" +icon: images/icons/admin.svg +--- diff --git a/content/archetype/blog/_index.md b/content/archetype/blog/_index.md new file mode 100644 index 0000000..603f54f --- /dev/null +++ b/content/archetype/blog/_index.md @@ -0,0 +1,4 @@ +--- +title: "Blog" +icon: images/icons/blog.svg +--- \ No newline at end of file diff --git a/content/archetype/business/_index.md b/content/archetype/business/_index.md new file mode 100644 index 0000000..b00ecda --- /dev/null +++ b/content/archetype/business/_index.md @@ -0,0 +1,4 @@ +--- +title: "Business" +icon: images/icons/business.svg +--- \ No newline at end of file diff --git a/content/archetype/documentation/_index.md b/content/archetype/documentation/_index.md new file mode 100644 index 0000000..14c7d40 --- /dev/null +++ b/content/archetype/documentation/_index.md @@ -0,0 +1,4 @@ +--- +title: "Documentation" +icon: images/icons/documentation.svg +--- \ No newline at end of file diff --git a/content/archetype/ecommerce/_index.md b/content/archetype/ecommerce/_index.md new file mode 100644 index 0000000..97145b0 --- /dev/null +++ b/content/archetype/ecommerce/_index.md @@ -0,0 +1,4 @@ +--- +title: "Ecommerce" +icon: images/icons/ecommerce.svg +--- diff --git a/content/archetype/event/_index.md b/content/archetype/event/_index.md new file mode 100644 index 0000000..bf88eb7 --- /dev/null +++ b/content/archetype/event/_index.md @@ -0,0 +1,4 @@ +--- +title: "Event" +icon: images/icons/event.svg +--- diff --git a/content/archetype/personal/_index.md b/content/archetype/personal/_index.md new file mode 100644 index 0000000..1b2bdf2 --- /dev/null +++ b/content/archetype/personal/_index.md @@ -0,0 +1,4 @@ +--- +title: "Personal" +icon: images/icons/personal.svg +--- \ No newline at end of file diff --git a/content/archetype/portfolio/_index.md b/content/archetype/portfolio/_index.md new file mode 100644 index 0000000..8438bff --- /dev/null +++ b/content/archetype/portfolio/_index.md @@ -0,0 +1,4 @@ +--- +title: "Portfolio" +icon: images/icons/portfolio.svg +--- \ No newline at end of file diff --git a/content/archetype/single-page/_index.md b/content/archetype/single-page/_index.md new file mode 100644 index 0000000..e049b64 --- /dev/null +++ b/content/archetype/single-page/_index.md @@ -0,0 +1,4 @@ +--- +title: "Single Page" +icon: images/icons/single-page.svg +--- diff --git a/content/css/_index.md b/content/css/_index.md new file mode 100644 index 0000000..4838274 --- /dev/null +++ b/content/css/_index.md @@ -0,0 +1,4 @@ +--- +title: CSS +weight: 3 +--- \ No newline at end of file diff --git a/content/css/bootstrap/_index.md b/content/css/bootstrap/_index.md new file mode 100644 index 0000000..0e79ed6 --- /dev/null +++ b/content/css/bootstrap/_index.md @@ -0,0 +1,5 @@ +--- +title: "Bootstrap" +icon: images/icons/bootstrap.svg +official_url: https://getbootstrap.com/ +--- \ No newline at end of file diff --git a/content/css/bulma/_index.md b/content/css/bulma/_index.md new file mode 100644 index 0000000..65262cf --- /dev/null +++ b/content/css/bulma/_index.md @@ -0,0 +1,4 @@ +--- +title: "Bulma" +icon: images/icons/bulma.svg +--- \ No newline at end of file diff --git a/content/css/css/_index.md b/content/css/css/_index.md new file mode 100644 index 0000000..3c6e73a --- /dev/null +++ b/content/css/css/_index.md @@ -0,0 +1,4 @@ +--- +title: "CSS" +icon: images/icons/css.svg +--- \ No newline at end of file diff --git a/content/css/material-ui/_index.md b/content/css/material-ui/_index.md new file mode 100644 index 0000000..a2b7c9e --- /dev/null +++ b/content/css/material-ui/_index.md @@ -0,0 +1,6 @@ +--- +title: "Material Ui" +icon: images/icons/material-ui.svg +official_url: https://mui.com/ +taxonomy: css +--- \ No newline at end of file diff --git a/content/css/postcss/_index.md b/content/css/postcss/_index.md new file mode 100644 index 0000000..a44b686 --- /dev/null +++ b/content/css/postcss/_index.md @@ -0,0 +1,5 @@ +--- +title: "PostCSS" +icon: images/icons/postcss.svg +official_url: https://postcss.org/ +--- diff --git a/content/css/scss/_index.md b/content/css/scss/_index.md new file mode 100644 index 0000000..c1038d2 --- /dev/null +++ b/content/css/scss/_index.md @@ -0,0 +1,4 @@ +--- +title: "SCSS" +icon: images/icons/scss.svg +--- \ No newline at end of file diff --git a/content/css/styled-components/_index.md b/content/css/styled-components/_index.md new file mode 100644 index 0000000..5f10d09 --- /dev/null +++ b/content/css/styled-components/_index.md @@ -0,0 +1,5 @@ +--- +title: "Styled Components" +icon: images/icons/styled-components.png +official_url: https://styled-components.com/ +--- diff --git a/content/css/stylus/_index.md b/content/css/stylus/_index.md new file mode 100644 index 0000000..419a50d --- /dev/null +++ b/content/css/stylus/_index.md @@ -0,0 +1,5 @@ +--- +title: "Stylus" +icon: images/icons/stylus.svg +official_url: https://stylus-lang.com/ +--- diff --git a/content/css/tailwind/_index.md b/content/css/tailwind/_index.md new file mode 100644 index 0000000..ebb00a9 --- /dev/null +++ b/content/css/tailwind/_index.md @@ -0,0 +1,5 @@ +--- +title: "Tailwind" +icon: images/icons/tailwind.svg +official_url: https://tailwindcss.com/ +--- \ No newline at end of file diff --git a/content/css/vuetify/_index.md b/content/css/vuetify/_index.md new file mode 100644 index 0000000..522568c --- /dev/null +++ b/content/css/vuetify/_index.md @@ -0,0 +1,5 @@ +--- +title: "Vuetify" +icon: images/icons/vuetify.svg +official_url: https://vuetifyjs.com/ +--- \ No newline at end of file diff --git a/content/services/_index.md b/content/services/_index.md new file mode 100644 index 0000000..a62afd8 --- /dev/null +++ b/content/services/_index.md @@ -0,0 +1,4 @@ +--- +title: Services +weight: 3 +--- \ No newline at end of file diff --git a/content/services/algolia/_index.md b/content/services/algolia/_index.md new file mode 100644 index 0000000..f76c7c1 --- /dev/null +++ b/content/services/algolia/_index.md @@ -0,0 +1,5 @@ +--- +title: "Algolia" +icon: images/icons/algolia.svg +official_url: https://https://www.algolia.com/ +--- \ No newline at end of file diff --git a/content/services/formspree/_index.md b/content/services/formspree/_index.md new file mode 100644 index 0000000..b3445c6 --- /dev/null +++ b/content/services/formspree/_index.md @@ -0,0 +1,5 @@ +--- +title: "Formspree" +icon: images/icons/formspree.svg +official_url: https://formspree.io/ +--- \ No newline at end of file diff --git a/content/services/formstack/_index.md b/content/services/formstack/_index.md new file mode 100644 index 0000000..08cec3c --- /dev/null +++ b/content/services/formstack/_index.md @@ -0,0 +1,5 @@ +--- +title: "Formstack" +icon: images/icons/formstack.svg +official_url: https://formstack.com/ +--- \ No newline at end of file diff --git a/content/services/mailchimp/_index.md b/content/services/mailchimp/_index.md new file mode 100644 index 0000000..7c9466d --- /dev/null +++ b/content/services/mailchimp/_index.md @@ -0,0 +1,5 @@ +--- +title: "Mailchimp" +icon: images/icons/mailchimp.svg +official_url: https://mailchimp.com/ +--- \ No newline at end of file diff --git a/content/services/shopify/_index.md b/content/services/shopify/_index.md new file mode 100644 index 0000000..ba2334c --- /dev/null +++ b/content/services/shopify/_index.md @@ -0,0 +1,5 @@ +--- +title: "Shopify" +icon: images/icons/shopify.svg +official_url: https://shopify.com/ +--- \ No newline at end of file diff --git a/content/services/snipcart/_index.md b/content/services/snipcart/_index.md new file mode 100644 index 0000000..f533e5a --- /dev/null +++ b/content/services/snipcart/_index.md @@ -0,0 +1,5 @@ +--- +title: "Snipcart" +icon: images/icons/snipcart.svg +official_url: https://snipcart.com/ +--- \ No newline at end of file diff --git a/content/theme/hugo-theme-tailwind.md b/content/theme/hugo-theme-tailwind.md new file mode 100644 index 0000000..c802fdd --- /dev/null +++ b/content/theme/hugo-theme-tailwind.md @@ -0,0 +1,36 @@ +--- +title: Tailwind +github: https://github.com/tomowang/hugo-theme-tailwind +demo: https://hugo-theme-tailwind.tomo.dev/ +author: Xiaoliang Wang +author_link: https://tomo.dev +date: 2023-12-09 +ssg: + - Hugo +cms: + - No CMS +css: + - Tailwind +archetype: + - Blog +description: Clean card Hugo theme for blog, created by using tailwindcss +--- + +# Hugo theme tailwind + +Hugo theme build using [Tailwind CSS](https://tailwindcss.com/), mostly for bloggers. + +Check [https://hugo-theme-tailwind.tomo.dev/](https://hugo-theme-tailwind.tomo.dev/) for demo. + +## Features + + * High Lighthouse score + * Dark mode - switch between light and dark mode, or default by system prefers. + * SVG icons thanks to [tabler icons](https://tabler-icons.io/) + * Google Analystics - add your Google Analytics ID to `hugo.toml` + * Comments settings for [disqus](https://disqus.com/) and [giscus](https://giscus.app/) + * Social media link data settings + * Additional short code: bilibili, asciinema + * Responsive design + * Multilingual + * Default image process for lazy load and srcset diff --git a/data/errors.json b/data/errors.json new file mode 100644 index 0000000..7016f33 --- /dev/null +++ b/data/errors.json @@ -0,0 +1,22 @@ +[ + { + "theme_key": "obaez-dentistsmile", + "file": "dentistsmile.md", + "repoUrl": "https://github.com/obaez/dentistsmile", + "error": "Github repo not found, status: 404" + }, + { + "theme_key": "hugo-sid-hugo-blog-awesome", + "file": "hugo-blog-awesome.md", + "repoUrl": "https://github.com/hugo-sid/hugo-blog-awesome", + "demoUrl": "https://hugo-blog-awesome.netlify.app/", + "error": "demo url not found" + }, + { + "theme_key": "obaez-dentistsmile", + "file": "dentistsmile.md", + "repoUrl": "https://github.com/obaez/dentistsmile", + "demoUrl": "https://obaez.com/dentistsmile/", + "error": "error checking demo url" + } +] \ No newline at end of file diff --git a/data/stackbit.json b/data/stackbit.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/data/stackbit.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/data/themes.json b/data/themes.json new file mode 100644 index 0000000..72b4377 --- /dev/null +++ b/data/themes.json @@ -0,0 +1,25 @@ +{ + "zzzmisa-hugo-theme-doors": { + "theme_key": "zzzmisa-hugo-theme-doors", + "file": "hugo-theme-doors.md", + "name": "hugo-theme-doors", + "title": "Doors", + "github_username": "zzzmisa", + "repo": "zzzmisa/hugo-theme-doors", + "branch": "master", + "default_branch": "master", + "github_url": "https://github.com/zzzmisa/hugo-theme-doors", + "demo_url": "https://zzzmisa.com/", + "stars": 13, + "forks": 9, + "open_issues": 0, + "last_commit": "2022-02-05T06:06:44Z", + "created_at": "2019-08-05T14:40:43Z", + "description": "🚪Single page theme for links to your works", + "images": { + "hires": "https://www.jamstackthemes.dev/capture/zzzmisa-hugo-theme-doors.png", + "thumbnail": "https://www.jamstackthemes.dev/images/theme/thumbnail/zzzmisa-hugo-theme-doors.jpg", + "screenshot": "https://www.jamstackthemes.dev/images/theme/thumbnail/2x/zzzmisa-hugo-theme-doors-2x.jpg" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..3274d02 --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "scripts": { + "build": "node ./scripts/build.js && hugo --minify", + "deploy": "node ./scripts/build.js --latest && hugo --minify", + "stackbit": "node ./scripts/build.js --github=false --demos=false --images=false && hugo --minify", + "images": "node ./scripts/build.js --github=false --demos=false --stackbit=false && hugo --minify", + "deploy:branch": "npm run deploy && echo 'User-agent: *\nDisallow: /' > public/robots.txt" + }, + "dependencies": { + "axios": "^0.21.1", + "axios-rate-limit": "^1.3.0", + "cli-spinners": "^2.5.0", + "date-fns": "^2.16.1", + "front-matter": "^4.0.2", + "fs-extra": "^9.1.0", + "js-yaml": "^4.0.0", + "ora": "^5.3.0", + "pageres": "^6.1.0", + "parse-github-url": "^1.0.2", + "promise.allsettled": "^1.0.4", + "sharp": "^0.30.5", + "url-slug": "^3.0.1", + "yaml-front-matter": "^4.1.1", + "yargs": "^16.2.0" + } +} diff --git a/scripts/build.js b/scripts/build.js new file mode 100644 index 0000000..e92a21f --- /dev/null +++ b/scripts/build.js @@ -0,0 +1,59 @@ +#!/usr/bin/env node +const yargs = require('yargs/yargs') +const { hideBin } = require('yargs/helpers') +const argv = yargs(hideBin(process.argv)).argv +const {generateGithubData} = require('./build/github'); +const {generateMarkdownData} = require('./build/markdown'); +const {testDemoUrls} = require('./build/demo'); +const {writeThemesFile, writeErrorFile, writeStackbitFile} = require('./build/write') +const {generateStackbitData} = require('./build/stackbit') +const {generateScreenshots, generateThumbnails} = require('./build/screenshot') +const config = require('./build/config'); +const {errorLog} = require('./build/errors'); + +const build = async (options) => { + const themesMarkdown = await generateMarkdownData(config.themesMarkdownFiles, options) + + if (options.github) { + // Updates themes.json + const themesGithub = await generateGithubData(themesMarkdown) + writeThemesFile(themesGithub) + } + if (options.stackbit) { + // Updates stackbit.json + const stackbit = generateStackbitData(themesMarkdown) + writeStackbitFile(stackbit); + } + + if (options.demos) { + // Visits each demo URL with a headless browser and if the site cannot be reached sets the markdown file to disabled=true + await testDemoUrls(themesMarkdown) + } + + if (options.images) { + await generateScreenshots(themesMarkdown, options.recaptureImage) + await generateThumbnails(themesMarkdown, options.regenerateImage) + } + + writeErrorFile(errorLog) + console.log("Build Successful") + console.log("Error Log") + process.exit(0) +} + +const options = { + github: argv.github !== "false", // generates `data/themes.json` which is used for github stars, commits meta data etc + stackbit: argv.stackbit !== "false", // generates `data/stackbit.json` which is used for the "create site" button + demos: argv.demos !== "false", // tests the themes demo url using headless browser and disables the theme if the url does not resolve + images: argv.images !== "false", // captures screenshots and generates thumbnails + + disabled: argv.disabled !== "false", // Skip processing themes that have front-matter `disabled: true` + draft: argv.draft !== "false", // Skip processing themes that have front-matter `draft: true` + latest: argv.latest || false, // Only process themes which don't already exist in `data/themes.json` + file: argv.file || false, // Only process a single file ie --file=gatsby-starter-advanced.md + all: argv.all || false, // process all themes + recaptureImage: argv.recaptureImage || false, + regenerateImage: argv.regenerateImage || false +} + +build(options); \ No newline at end of file diff --git a/scripts/build/cleanup.js b/scripts/build/cleanup.js new file mode 100644 index 0000000..886e93e --- /dev/null +++ b/scripts/build/cleanup.js @@ -0,0 +1,23 @@ +#!/usr/bin/env node +const fs = require('fs'); +const path = require('path'); +const yamlFront = require('yaml-front-matter'); +const yaml = require('js-yaml'); +const isBefore = require('date-fns/isBefore'); +const parseISO = require('date-fns/parseISO'); +const subYears = require('date-fns/subYears'); +const {getThemeKey} = require('./utils'); +const {themeMap} = require('./write'); +const {generateMarkdownData} = require('./markdown') +const config = require('./config'); +const staleBeforeDate = subYears(new Date(), 1); + +const deleteOldThemes = async (themesMarkdown, themesGithub) => { + const themesMarkdown = await generateMarkdownData(config.themesMarkdownFiles) + const themesMarkdownMap = themeMap(themesMarkdown) +} + +module.exports = { + deleteOldThemes +} + diff --git a/scripts/build/config.js b/scripts/build/config.js new file mode 100644 index 0000000..e48700c --- /dev/null +++ b/scripts/build/config.js @@ -0,0 +1,30 @@ +#!/usr/bin/env node +const fs = require('fs'); +const path = require('path'); +const subYears = require('date-fns/subYears'); + +const errorJsonFile = path.join(process.cwd(), '/data/errors.json'); +const stackbitJsonFile = path.join(process.cwd(), '/data/stackbit.json'); +const stackbitJsonData = fs.existsSync(stackbitJsonFile) ? JSON.parse(fs.readFileSync(stackbitJsonFile)) : {}; +const themesMarkdownFolder = path.join(process.cwd(), '/content/theme') +const themesMarkdownFiles = fs.readdirSync(themesMarkdownFolder).map(relFilename => path.resolve(themesMarkdownFolder, relFilename)) +const themesJsonFile = path.join(process.cwd(), '/data/themes.json'); +const themesJsonData = fs.existsSync(themesJsonFile) ? JSON.parse(fs.readFileSync(themesJsonFile)) : {}; +const hiresImagesFolder = path.join(process.cwd(), '/static/capture'); +const thumbnailImagesFolder = path.join(process.cwd(), '/static/images/theme/thumbnail') +const thumbnailImagesFolder2x = path.join(process.cwd(), '/static/images/theme/thumbnail/2x') +const staleBeforeDate = subYears(new Date(), 1); + +module.exports = { + errorJsonFile, + themesJsonFile, + themesMarkdownFolder, + themesMarkdownFiles, + themesJsonData, + stackbitJsonFile, + stackbitJsonData, + hiresImagesFolder, + thumbnailImagesFolder, + thumbnailImagesFolder2x, + staleBeforeDate +} \ No newline at end of file diff --git a/scripts/build/demo.js b/scripts/build/demo.js new file mode 100644 index 0000000..3ffb388 --- /dev/null +++ b/scripts/build/demo.js @@ -0,0 +1,67 @@ +#!/usr/bin/env node +const axios = require('axios'); +const allSettled = require('promise.allsettled'); +const {errorLog} = require('./errors') +const ora = require('ora'); +const {getThemeKey, getRepoName} = require('./utils'); +const {updateFrontmatter} = require('./markdown'); +const spinner = ora('Loading') + +const testDemo = (frontmatter) => { + const themeKey = getThemeKey(frontmatter.github) + + return axios.get(frontmatter.demo).then((res) => { + spinner.text = `${frontmatter.demo} => checking Demo URL - ${res.status}`; + if (frontmatter.disabled) { + updateFrontmatter(frontmatter.file, { + disabled: false, + disabled_reason: "" + }) + } + return true; + }).catch(err => { + let error = "error checking demo url" + if (err) { + if (err.code === "ENOTFOUND") { + error = "demo url not found" + } + if (err.code === "ECONNREFUSED") { + error = "demo url connection refused" + } + if (err.response) { + if (err.response.status === 404) { + error = "demo url not found" + } + if (err.response.data) { + if (err.response.data.message) { + error = err.response.data.message; + } + } + + } + } + updateFrontmatter(frontmatter.file, { + disabled: true, + disabled_reason: error + }) + spinner.text = `${frontmatter.demo} => checking Demo URL - ${error}`; + errorLog.push({ + theme_key: themeKey, + file: frontmatter.file, + repoUrl: frontmatter.github, + demoUrl: frontmatter.demo, + error + }) + }); +} + +const testDemoUrls = async (themesMarkdown) => { + spinner.start("Testing Demo URL's"); + await allSettled(themesMarkdown.map(theme => testDemo(theme))) + spinner.succeed("Success - Testing Demo URL's"); +} + + +module.exports = { + testDemoUrls +} \ No newline at end of file diff --git a/scripts/build/errors.js b/scripts/build/errors.js new file mode 100644 index 0000000..d807214 --- /dev/null +++ b/scripts/build/errors.js @@ -0,0 +1,6 @@ +#!/usr/bin/env node +let errorLog = [] + +module.exports = { + errorLog +} diff --git a/scripts/build/github.js b/scripts/build/github.js new file mode 100755 index 0000000..31a0811 --- /dev/null +++ b/scripts/build/github.js @@ -0,0 +1,135 @@ +#!/usr/bin/env node +const axios = require('axios'); +const allSettled = require('promise.allsettled'); +const rateLimit = require('axios-rate-limit'); +const isBefore = require('date-fns/isBefore'); +const parseISO = require('date-fns/parseISO'); +const formatDistanceToNow = require('date-fns/formatDistanceToNow') +const ora = require('ora'); +const {errorLog} = require('./errors'); +const {getThemeKey, getRepoName} = require('./utils'); +const {updateFrontmatter} = require('./markdown') +const config = require('./config'); + +if (!process.env.GITHUB_TOKEN) { + throw new Error( + 'Cannot access Github API - environment variable "GITHUB_TOKEN" is missing' + ) +} + +const token = process.env.GITHUB_TOKEN; +const axiosLimit = rateLimit(axios.create(), {maxRequests: 2, perMilliseconds: 200}) +const spinner = ora('Loading') + +const fetchRepoData = async (frontmatter) => { + + const themeKey = getThemeKey(frontmatter.github) + const repoName = getRepoName(frontmatter.github) + + try { + const res = await axiosLimit.get( + `https://api.github.com/repos/${repoName}`, + { + headers: { + Authorization: `Token ${token}`, + }, + }) + spinner.text = `${frontmatter.file} => ${res.data.html_url} - ${res.status}` + const lastCommit = await fetchBranchData(repoName, res.data.default_branch); + updateFrontmatter(frontmatter.file, { + stale: isBefore(parseISO(lastCommit), config.staleBeforeDate) + }) + if (frontmatter.disabled) { + updateFrontmatter(frontmatter.file, { + disabled: false, + disabled_reason: "" + }) + } + + return { + theme_key: themeKey, + file: frontmatter.file, + name: res.data.name, + title: frontmatter.title, + github_username: res.data.owner.login, + repo: res.data.full_name, + branch: res.data.default_branch, + default_branch: res.data.default_branch, + github_url: res.data.html_url, + demo_url: frontmatter.demo, + stars: res.data.stargazers_count, + forks: res.data.forks_count, + open_issues: res.data.open_issues_count, + last_commit: lastCommit, + created_at: res.data.created_at, + description: res.data.description, + images: { + hires: `https://www.jamstackthemes.dev/capture/${themeKey}.png`, + thumbnail: `https://www.jamstackthemes.dev/images/theme/thumbnail/${themeKey}.jpg`, + screenshot: `https://www.jamstackthemes.dev/images/theme/thumbnail/2x/${themeKey}-2x.jpg` + } + } + } catch (err) { + const status = err.response?.status; + let error = `Github repo not found, status: ${status}`; + if (status === 404) { + updateFrontmatter(frontmatter.file, { + disabled: true, + disabled_reason: error + }) + } + spinner.text = `${frontmatter.file} => ${error}` + errorLog.push({ + theme_key: themeKey, + file: frontmatter.file, + repoUrl: frontmatter.github, + error + }) + throw err + } +} + +const fetchBranchData = (repo, branch) => { + return axiosLimit.get( + `https://api.github.com/repos/${repo}/branches/${branch}`, + { + headers: { + Authorization: `Token ${token}`, + }, + }).then((res) => { + const lastCommit = res.data.commit.commit.author.date; + const lastCommitToNow = formatDistanceToNow(parseISO(lastCommit)); + spinner.text = `${repo} => last commit to branch '${branch}' ${lastCommitToNow}` + return lastCommit; + }).catch((err) => { + throw err + }); +} + +const generateGithubData = async (markdownData, themesJsonData) => { + spinner.start("Fetching Github Data"); + const initalThemes = config.themesJsonData; + const update = await allSettled(markdownData.map(frontmatter => fetchRepoData(frontmatter))) + const updatedThemesArray = update.filter(res => res.status === 'fulfilled').map(res => res.value) + const mergedThemesMap = updatedThemesArray.reduce((accumulator, theme) => { + if (theme === undefined) { + // TODO find root cause + console.log("undefined theme detected"); + return { + ...accumulator + }; + } + const themeKey = getThemeKey(theme.github_url) + return { + ...accumulator, + [themeKey]: theme + }; + }, initalThemes); + + spinner.succeed("Success - Fetching Github Data"); + return mergedThemesMap +} + +module.exports = { + generateGithubData +} \ No newline at end of file diff --git a/scripts/build/markdown.js b/scripts/build/markdown.js new file mode 100644 index 0000000..aa53276 --- /dev/null +++ b/scripts/build/markdown.js @@ -0,0 +1,133 @@ +#!/usr/bin/env node +const fs = require('fs'); +const path = require('path'); +const yamlFront = require('yaml-front-matter'); +const yaml = require('js-yaml'); +const {getThemeKey} = require('./utils'); +const config = require('./config'); + +const updateFrontmatter = (file, update = {}) => { + const absFilepath = path.resolve(config.themesMarkdownFolder, file) + const fileData = fs.existsSync(absFilepath) ? fs.readFileSync(absFilepath) : null; + + let frontmatter = yamlFront.loadFront(fileData); + let content = frontmatter.__content; + delete frontmatter.__content; + + Object.keys(update).forEach((key, index) => { + frontmatter[key] = update[key] + }); + + const fm = `---\n${yaml.dump(frontmatter)}---${content}`; + + fs.writeFileSync(absFilepath, fm); +} + +const loadThemeFrontMatter = absFilename => { + const fileData = fs.readFileSync(absFilename); + const frontmatter = yamlFront.loadFront(fileData); + + if (!frontmatter.github) { + const file = path.parse(absFilename).base; + if (file === ".DS_Store") { + fs.unlinkSync(absFilename) + throw new Error(`${absFilename} invalid file. This file was deleted, try again...`) + } + throw new Error(`${absFilename} invalid github frontmatter`) + } + + let title = frontmatter.title; + let description = frontmatter.description; + let draft = frontmatter.draft; + let disabled = frontmatter.disabled; + let github = frontmatter.github; + let demo = frontmatter.demo; + let github_branch = frontmatter.github_branch; + let file = path.parse(absFilename).base; + let absFile = absFilename + let ssg = frontmatter.ssg + let cms = frontmatter.cms + return { + title, + description, + draft, + disabled, + github, + demo, + github_branch, + file, + absFile, + ssg, + cms + } + +}; + +const generateMarkdownData = async (markdownFiles, options = {}) => { + + let filterCounts = { + draft: 0, + disabled: 0, + latest: 0, + skipped: 0 + } + + const markdownData = markdownFiles.map(absFilename => loadThemeFrontMatter(absFilename)).filter(frontmatter => { + + if (options.file) { + // if the cli command --file=hugo-swift-theme.md is used, only fetch that specific theme + if (options.file !== frontmatter.file) { + filterCounts.skipped += 1 + return false + } + return true; + } + + if (options.latest) { + // if the cli command --latest is used, only fetch new themes which don't already exist in `data/themes.json` + const themeKey = getThemeKey(frontmatter.github) + if (config.themesJsonData[themeKey]) { + filterCounts.latest += 1 + return false + } + return true + } + + if (options.disabled) { + if (frontmatter.disabled) { + filterCounts.disabled += 1 + return false; + } + } + if (options.draft) { + if (frontmatter.draft) { + filterCounts.draft += 1 + return false; + } + } + + return true; + }); + + + if (options.file) { + console.log(`Processing single theme - ${options.file}`) + } else if (options.latest) { + console.log(`Processing latest themes`) + } else { + console.log(`Processing all themes`) + console.log(`Total ${markdownFiles.length} themes`) + console.log(`Skipping ${filterCounts.disabled} disabled themes`) + console.log(`Skipping ${filterCounts.draft} draft themes`) + console.log(`Skipping ${filterCounts.latest} existing themes`) + console.log(`Processing ${markdownData.length}/${markdownFiles.length} themes...`) + } + + return markdownData; +} + +module.exports = { + generateMarkdownData, + updateFrontmatter +} + diff --git a/scripts/build/screenshot.js b/scripts/build/screenshot.js new file mode 100644 index 0000000..56bb0ce --- /dev/null +++ b/scripts/build/screenshot.js @@ -0,0 +1,99 @@ +#!/usr/bin/env node +const fs = require('fs-extra'); +const path = require('path'); +const Pageres = require('pageres'); +const sharp = require('sharp') +const {getThemeKey} = require('./utils'); +const config = require('./config'); +const ora = require('ora'); +const spinner = ora('Loading') + +const captureScreenshot = async (frontmatter, overwrite = false) => { + const themeKey = getThemeKey(frontmatter.github) + const themeImage = `${themeKey}.png` + + if (!overwrite && fs.existsSync(path.join(config.hiresImagesFolder, themeImage))) { + return false + } + + try { + const page = await new Pageres({delay: 2, filename: themeKey}) + .src(frontmatter.demo, ['1280x960'], {crop: true}) + .dest(config.hiresImagesFolder) + .run(); + spinner.text = `${frontmatter.demo} => capturing` + return page + } catch { + spinner.text = `${frontmatter.demo} => failed capturing` + return false; + } +}; + +const generateThumbnail = async (frontmatter, overwrite = false) => { + const themeKey = getThemeKey(frontmatter.github) + const hiresImage = path.join(config.hiresImagesFolder, `${themeKey}.png`) + const imageName = path.parse(hiresImage).name + const outputImage = path.join(config.thumbnailImagesFolder, `${imageName}.jpg`) + const outputImage2x = path.join(config.thumbnailImagesFolder2x, `${imageName}-2x.jpg`) + + if (!hiresImage) { + return false; + } + if (!overwrite && fs.existsSync(outputImage) && fs.existsSync(outputImage2x)) { + return false; + } + fs.ensureDirSync(config.thumbnailImagesFolder); + fs.ensureDirSync(config.thumbnailImagesFolder2x); + + try { + spinner.text = `${imageName} => processing thumbnail` + await sharp(hiresImage) + .resize({ + width: 280, + height: 210, + fit: 'cover', + position: 'top', + }) + .jpeg({ + quality: 85, + }) + .toFile(outputImage) + + spinner.text = `${imageName} => processing thumbnail@2x` + await sharp(hiresImage) + .resize({ + width: 560, + height: 420, + fit: 'cover', + position: 'top', + }) + .jpeg({ + quality: 85, + }) + .toFile(outputImage2x) + } catch { + spinner.text = `${imageName} => processing failed` + return false; + } +}; + +const generateThumbnails = async (markdownData, overwrite) => { + spinner.start("Generating Thumbnails"); + for (const theme of markdownData) { + await generateThumbnail(theme, overwrite) + } + spinner.succeed("Success - Generating Thumbnails"); +}; + +const generateScreenshots = async (markdownData, overwrite) => { + spinner.start("Capturing Screenshots"); + for (const theme of markdownData) { + await captureScreenshot(theme, overwrite) + } + spinner.succeed("Success - Capturing Screenshots"); +} + +module.exports = { + generateScreenshots, + generateThumbnails +} \ No newline at end of file diff --git a/scripts/build/stackbit.js b/scripts/build/stackbit.js new file mode 100755 index 0000000..1ac4936 --- /dev/null +++ b/scripts/build/stackbit.js @@ -0,0 +1,91 @@ +#!/usr/bin/env node +const urlSlug = require('url-slug'); +const {getThemeKey, getRepoName} = require('./utils'); +const ora = require('ora'); +const spinner = ora('Loading') +const allowedSsg = [] +const allowedCms = [] + +const generateStackbit = (frontmatter) => { + const themeKey = getThemeKey(frontmatter.github) + + spinner.text = `${frontmatter.file}` + + let stackbitData = { + theme_key: themeKey + }; + + const ssgArray = frontmatter.ssg || [] + const cmsArray = frontmatter.cms || [] + + if (ssgArray.some(ssg => allowedSsg.includes(urlSlug(ssg))) && cmsArray.some(cms => allowedCms.includes(urlSlug(cms)))) { + if (ssgArray.length > 1) { + stackbitData.createUrl = `https://app.stackbit.com/create?theme=${frontmatter.github}` + } else { + stackbitData.createUrl = `https://app.stackbit.com/create?theme=${frontmatter.github}&ssg=${urlSlug(ssgArray)}` + } + } else { + stackbitData.createUrl = null; + } + + // manual overrides + const manualDisabled = [ + 'https://github.com/runbytech/gatsby-theme-ultronele', + 'https://github.com/YoussefRaafatNasry/portfolYOU', + 'https://github.com/qwtel/hydejack', + 'https://github.com/joshgerdes/jekyll-uno', + 'https://github.com/GDGToulouse/devfest-theme-hugo', + 'https://github.com/h-enk/doks', + 'https://github.com/ahmadiqbal1/jekyll-webpack-boilerplate', + 'https://github.com/SupunKavinda/jekyll-theme-leaf.git', + 'https://github.com/techonomics69/gatsby-netlify-form-example', + 'https://github.com/lawrencecchen/headless-comments', + 'https://github.com/Joy3Luo/Joy3luo.github.io', + 'https://github.com/chrisbobbe/jekyll-theme-prologue', + 'https://github.com/adityatelange/hugo-PaperMod/', + 'https://github.com/Kentico/kontent-jekyll-blog', + 'https://github.com/longpdo/neumorphism', + 'https://github.com/puresyntax71/hugo-theme-chunky-poster', + 'https://github.com/sharadcodes/jekyll-theme-dark-reader', + 'https://github.com/wkocjan/gatsby-theme-intro', + 'https://github.com/jameshamann/jekyll-material-theme', + 'https://github.com/fncnt/vncnt-hugo', + 'https://github.com/wizlee/gatsby-portfolio', + 'https://github.com/EmaSuriano/gatsby-starter-mate', + 'https://github.com/guangmean/Niello', + 'https://github.com/fabien0102/gatsby-starter', + 'https://github.com/netlify-templates/one-click-hugo-cms' + + ]; + manualDisabled.forEach(url => { + if (url === frontmatter.github) { + if (stackbitData.createUrl) { + delete stackbitData.createUrl + } + } + }) + const manualEnabled = ['https://github.com/stackbit-themes/minimal-nextjs-theme'] + manualEnabled.forEach(url => { + if (url === frontmatter.github) { + stackbitData.createUrl = `https://app.stackbit.com/create?theme=${frontmatter.github}&ssg=${urlSlug(frontmatter.ssg)}&cms=${urlSlug(frontmatter.cms)}` + } + }) + if (stackbitData.createUrl) { + return stackbitData; + } + return false; + +}; + +const generateStackbitData = (markdownData) => { + spinner.start("Fetching Stackbit Data"); + const stackbitData = markdownData.map(theme => { + return generateStackbit(theme) + }).filter(stackbit => stackbit.createUrl); + spinner.succeed("Success - Fetching Stackbit Data"); + return stackbitData; +}; + +module.exports = { + generateStackbitData +} \ No newline at end of file diff --git a/scripts/build/utils.js b/scripts/build/utils.js new file mode 100644 index 0000000..301e492 --- /dev/null +++ b/scripts/build/utils.js @@ -0,0 +1,20 @@ +#!/usr/bin/env node +const parseGithubUrl = require('parse-github-url'); + +const getRepoName = (repoUrl) => { + return parseGithubUrl(repoUrl).repo; // stackbithq/stackbit-theme-fresh +} + +const getThemeKey = (repoUrl) => { + if (!repoUrl) { + throw Error("Unable to generate themeKey") + } + const repoName = getRepoName(repoUrl) + const themeKey = repoName.replace("/", "-").toLowerCase(); + return themeKey +} + +module.exports = { + getThemeKey, + getRepoName +} \ No newline at end of file diff --git a/scripts/build/write.js b/scripts/build/write.js new file mode 100644 index 0000000..124b409 --- /dev/null +++ b/scripts/build/write.js @@ -0,0 +1,53 @@ +#!/usr/bin/env node +const fs = require('fs'); +const config = require('./config') +const {getThemeKey} = require('./utils'); + +const writeThemesFile = (themes) => { + // Load existing themes.json and update it + let themesData = themes + // themes.forEach(theme => { + // themesData[theme.theme_key] = theme; + // }) + let sortedThemesData = {} + Object.keys(themesData).sort().forEach(key => { + sortedThemesData[key] = themesData[key]; + }); + console.log(`Writing ${config.themesJsonFile}...`); + fs.writeFileSync(config.themesJsonFile, JSON.stringify(sortedThemesData, null, 2)); +} + +const writeErrorFile = (errorLog) => { + console.log(`Writing ${config.errorJsonFile}...`); + fs.writeFileSync(config.errorJsonFile, JSON.stringify(errorLog, null, 2)); +} + +const writeStackbitFile = (stackbit) => { + let stackbitData = config.stackbitJsonData + stackbit.forEach(theme => { + stackbitData[theme.theme_key] = theme; + }) + let sortedStackbitData = {} + Object.keys(stackbitData).sort().forEach(key => { + sortedStackbitData[key] = stackbitData[key]; + }); + console.log(`Writing ${config.stackbitJsonFile}...`); + fs.writeFileSync(config.stackbitJsonFile, JSON.stringify(sortedStackbitData, null, 2)); +} + +const themeMap = (themes) => { + return themes.reduce((accumulator, theme) => { + const themeKey = getThemeKey(theme.github) + return { + ...accumulator, + [themeKey]: theme + }; + }); +} + +module.exports = { + writeThemesFile, + writeErrorFile, + writeStackbitFile, + themeMap +} diff --git a/static/images/banner/bg.svg b/static/images/banner/bg.svg new file mode 100644 index 0000000..67d2e63 --- /dev/null +++ b/static/images/banner/bg.svg @@ -0,0 +1,188 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/banner/netlify-create.svg b/static/images/banner/netlify-create.svg new file mode 100644 index 0000000..196f932 --- /dev/null +++ b/static/images/banner/netlify-create.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/default-large.png b/static/images/default-large.png new file mode 100644 index 0000000000000000000000000000000000000000..8b93be9c8c94f410cf1687f58684b312f9a237de GIT binary patch literal 3138 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 literal 0 HcmV?d00001 diff --git a/static/images/favicon/favicon-32x32.png b/static/images/favicon/favicon-32x32.png new file mode 100755 index 0000000000000000000000000000000000000000..cfa1db9487c373863d50b634c23b3b1613f33c89 GIT binary patch literal 1390 zcmV-!1(EuRP)w|1c+AYqCs^JsFNltrnVFfHnVGqv-!3z^-ONnd|C^lOPokfHDIL>} zWGNln&u?sxld{V3hVysIKZw9_`q$;-wQF0+$`keLi&KopKDpHR?(OT1c^}^L1&81n zd(gqA9ZuyqxQW|%pxc+^T>kBicQhsc~$+gE8o?OE-_FSED z2W;Wf1P(E;?TEnnjuTA?efOj=9!i?}N(xZ-q;_WMweDhJYa4QA>Gf{x1@tS({vr4R z<2MmF(fM>pQtA^^!YBZgPS!)GoJ;ab&iwJT&u1O{jHwQ^A$Z0fbg+SKuMhbWk%_?i zRRrLWFx68DY&nUI6Or7LsOY=zGxNQ{QfC`;HLeIXZbPIR&tnfd*uWM(@J*W9@}$sD z0cJ-8kTRUy4^C1{nA-Rgo&0d<9f!rLTJ#K|#v}!IAb6Iv2OVr+n~=tM__`zri!#9o zyDfo)DB=Wgbc0v4BZGCc6t)?i{zn9sYE*&XS!nSepzGH^TszVyfpVZHS`t9#Qo0`U z8wn>526xV9QK3aor-0pbYR=W@WQzpP?q2k?^T{Si0-5m2Q3v+u<)4piN&wdf-R?Ef zJ$~B#<#e%4sNO&E-rC1L5#O_QAySKdf<39lf3(OZ=mQL3xtK)6>N>D|oPJ%100xM6 z&+F*qL?yp_zu@n&c-1|3<`zl9q88uo2^(>x_9J0P)pyvnGoxECUCKPTHeoC=I1d3DoU&C9L zoIl*Fea*e&Eai4~>jfJt&(iR5eWRxW%fE({-R}yM^~W0zBHlNM?p%8HXz9K2XwrQw zeqigd4i{{N-wR4y0an{VV&I zUq)xFe63zAY-P8pefwTe++F|{Few8MwtN}EadPGBJzkifoo8wT?UFjg$R>xx+}1g*{B{wq)WDmu?SkzjQ=0%^ zU;wM6jhJ2{?3M&DxiItbwyS7E>JKeEN2W}z?~_cppJdGfLpQ0C-yHW z<**-S?XsOpf@kbqOgh-W7C!KOfG~gsOitK{(U8EZ6V&U#kQ%2O#w2yF4ycy3A$D0E z>Od;mP#eDqutqxEa|yvS_Mn4}Z;#)=02VNTjqio>@oM-Qn$w?%|2(8Jpcdz07*qoM6N<$f;G*i=Kufz literal 0 HcmV?d00001 diff --git a/static/images/icons/admin.svg b/static/images/icons/admin.svg new file mode 100644 index 0000000..c431d6d --- /dev/null +++ b/static/images/icons/admin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/airtable.svg b/static/images/icons/airtable.svg new file mode 100755 index 0000000..2d9fb2e --- /dev/null +++ b/static/images/icons/airtable.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/algolia.svg b/static/images/icons/algolia.svg new file mode 100644 index 0000000..e7a5933 --- /dev/null +++ b/static/images/icons/algolia.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/astro.svg b/static/images/icons/astro.svg new file mode 100644 index 0000000..851e911 --- /dev/null +++ b/static/images/icons/astro.svg @@ -0,0 +1,4 @@ + + + + diff --git a/static/images/icons/blog.svg b/static/images/icons/blog.svg new file mode 100644 index 0000000..5618ad8 --- /dev/null +++ b/static/images/icons/blog.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/bootstrap.svg b/static/images/icons/bootstrap.svg new file mode 100644 index 0000000..f64b10a --- /dev/null +++ b/static/images/icons/bootstrap.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/brunch.svg b/static/images/icons/brunch.svg new file mode 100644 index 0000000..c7f931d --- /dev/null +++ b/static/images/icons/brunch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/bulma.svg b/static/images/icons/bulma.svg new file mode 100644 index 0000000..badd355 --- /dev/null +++ b/static/images/icons/bulma.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/business.svg b/static/images/icons/business.svg new file mode 100644 index 0000000..74e13dc --- /dev/null +++ b/static/images/icons/business.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/cecil.svg b/static/images/icons/cecil.svg new file mode 100644 index 0000000..3ce9daa --- /dev/null +++ b/static/images/icons/cecil.svg @@ -0,0 +1,33 @@ + \ No newline at end of file diff --git a/static/images/icons/contentful.svg b/static/images/icons/contentful.svg new file mode 100644 index 0000000..0802a23 --- /dev/null +++ b/static/images/icons/contentful.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/cosmic.svg b/static/images/icons/cosmic.svg new file mode 100644 index 0000000..00d5f9d --- /dev/null +++ b/static/images/icons/cosmic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/css.svg b/static/images/icons/css.svg new file mode 100644 index 0000000..e407677 --- /dev/null +++ b/static/images/icons/css.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/static/images/icons/datocms.svg b/static/images/icons/datocms.svg new file mode 100644 index 0000000..c9d4e1b --- /dev/null +++ b/static/images/icons/datocms.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/directus.svg b/static/images/icons/directus.svg new file mode 100644 index 0000000..0a9437c --- /dev/null +++ b/static/images/icons/directus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/documentation.svg b/static/images/icons/documentation.svg new file mode 100644 index 0000000..51c833e --- /dev/null +++ b/static/images/icons/documentation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/docusaurus.svg b/static/images/icons/docusaurus.svg new file mode 100644 index 0000000..5d13ed9 --- /dev/null +++ b/static/images/icons/docusaurus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/ecommerce.svg b/static/images/icons/ecommerce.svg new file mode 100644 index 0000000..8b4edcc --- /dev/null +++ b/static/images/icons/ecommerce.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/eleventy.svg b/static/images/icons/eleventy.svg new file mode 100644 index 0000000..8b3c7dc --- /dev/null +++ b/static/images/icons/eleventy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/event.svg b/static/images/icons/event.svg new file mode 100644 index 0000000..9fb4228 --- /dev/null +++ b/static/images/icons/event.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/firebase.svg b/static/images/icons/firebase.svg new file mode 100644 index 0000000..4069cf5 --- /dev/null +++ b/static/images/icons/firebase.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/flotiq.svg b/static/images/icons/flotiq.svg new file mode 100644 index 0000000..aaaaf01 --- /dev/null +++ b/static/images/icons/flotiq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/forestry.svg b/static/images/icons/forestry.svg new file mode 100644 index 0000000..c3b2790 --- /dev/null +++ b/static/images/icons/forestry.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/formspree.svg b/static/images/icons/formspree.svg new file mode 100644 index 0000000..1795c5a --- /dev/null +++ b/static/images/icons/formspree.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/formstack.svg b/static/images/icons/formstack.svg new file mode 100644 index 0000000..2a970e7 --- /dev/null +++ b/static/images/icons/formstack.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/gatsby-bw.svg b/static/images/icons/gatsby-bw.svg new file mode 100644 index 0000000..2dde241 --- /dev/null +++ b/static/images/icons/gatsby-bw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/gatsby.svg b/static/images/icons/gatsby.svg new file mode 100755 index 0000000..11ef284 --- /dev/null +++ b/static/images/icons/gatsby.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/ghost.svg b/static/images/icons/ghost.svg new file mode 100644 index 0000000..51717cc --- /dev/null +++ b/static/images/icons/ghost.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/github.svg b/static/images/icons/github.svg new file mode 100644 index 0000000..ab25bc2 --- /dev/null +++ b/static/images/icons/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/graphcms.svg b/static/images/icons/graphcms.svg new file mode 100644 index 0000000..6f2e8cd --- /dev/null +++ b/static/images/icons/graphcms.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/gridsome.svg b/static/images/icons/gridsome.svg new file mode 100644 index 0000000..7808590 --- /dev/null +++ b/static/images/icons/gridsome.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/hexo.svg b/static/images/icons/hexo.svg new file mode 100755 index 0000000..43322e5 --- /dev/null +++ b/static/images/icons/hexo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/hugo-bw.svg b/static/images/icons/hugo-bw.svg new file mode 100644 index 0000000..9d788aa --- /dev/null +++ b/static/images/icons/hugo-bw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/hugo.svg b/static/images/icons/hugo.svg new file mode 100644 index 0000000..fb55f51 --- /dev/null +++ b/static/images/icons/hugo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/hyde.svg b/static/images/icons/hyde.svg new file mode 100644 index 0000000..67add81 --- /dev/null +++ b/static/images/icons/hyde.svg @@ -0,0 +1,24 @@ + \ No newline at end of file diff --git a/static/images/icons/jekyll.svg b/static/images/icons/jekyll.svg new file mode 100644 index 0000000..5e8d293 --- /dev/null +++ b/static/images/icons/jekyll.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/jigsaw.svg b/static/images/icons/jigsaw.svg new file mode 100644 index 0000000..6721365 --- /dev/null +++ b/static/images/icons/jigsaw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/kontent.svg b/static/images/icons/kontent.svg new file mode 100644 index 0000000..0e1f1ec --- /dev/null +++ b/static/images/icons/kontent.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/mailchimp.svg b/static/images/icons/mailchimp.svg new file mode 100644 index 0000000..c1bf3f1 --- /dev/null +++ b/static/images/icons/mailchimp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/material-ui.svg b/static/images/icons/material-ui.svg new file mode 100644 index 0000000..d459d28 --- /dev/null +++ b/static/images/icons/material-ui.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/static/images/icons/metalsmith.svg b/static/images/icons/metalsmith.svg new file mode 100644 index 0000000..31d5094 --- /dev/null +++ b/static/images/icons/metalsmith.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/middleman.svg b/static/images/icons/middleman.svg new file mode 100644 index 0000000..be9676c --- /dev/null +++ b/static/images/icons/middleman.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/mkdocs.svg b/static/images/icons/mkdocs.svg new file mode 100644 index 0000000..fdaa7c6 --- /dev/null +++ b/static/images/icons/mkdocs.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/netlify.svg b/static/images/icons/netlify.svg new file mode 100644 index 0000000..eb958a2 --- /dev/null +++ b/static/images/icons/netlify.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/netlifycms.svg b/static/images/icons/netlifycms.svg new file mode 100644 index 0000000..b307698 --- /dev/null +++ b/static/images/icons/netlifycms.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/next.svg b/static/images/icons/next.svg new file mode 100644 index 0000000..2bf7c1e --- /dev/null +++ b/static/images/icons/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/no-cms.svg b/static/images/icons/no-cms.svg new file mode 100644 index 0000000..faf37ad --- /dev/null +++ b/static/images/icons/no-cms.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/nocms.svg b/static/images/icons/nocms.svg new file mode 100644 index 0000000..faf37ad --- /dev/null +++ b/static/images/icons/nocms.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/notion.svg b/static/images/icons/notion.svg new file mode 100644 index 0000000..815ecd9 --- /dev/null +++ b/static/images/icons/notion.svg @@ -0,0 +1 @@ +Notion icon \ No newline at end of file diff --git a/static/images/icons/nuxt.svg b/static/images/icons/nuxt.svg new file mode 100644 index 0000000..5db0a2c --- /dev/null +++ b/static/images/icons/nuxt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/pelican.svg b/static/images/icons/pelican.svg new file mode 100644 index 0000000..be9cbec --- /dev/null +++ b/static/images/icons/pelican.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/personal.svg b/static/images/icons/personal.svg new file mode 100644 index 0000000..2782104 --- /dev/null +++ b/static/images/icons/personal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/platframe.svg b/static/images/icons/platframe.svg new file mode 100644 index 0000000..3d744c7 --- /dev/null +++ b/static/images/icons/platframe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/portfolio.svg b/static/images/icons/portfolio.svg new file mode 100644 index 0000000..8983bb4 --- /dev/null +++ b/static/images/icons/portfolio.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/postcss.svg b/static/images/icons/postcss.svg new file mode 100644 index 0000000..c416420 --- /dev/null +++ b/static/images/icons/postcss.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/prismic.svg b/static/images/icons/prismic.svg new file mode 100644 index 0000000..5d14f01 --- /dev/null +++ b/static/images/icons/prismic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/sanity.svg b/static/images/icons/sanity.svg new file mode 100644 index 0000000..0836236 --- /dev/null +++ b/static/images/icons/sanity.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/sapper.svg b/static/images/icons/sapper.svg new file mode 100644 index 0000000..8f32c37 --- /dev/null +++ b/static/images/icons/sapper.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/sass.svg b/static/images/icons/sass.svg new file mode 100644 index 0000000..ca32b98 --- /dev/null +++ b/static/images/icons/sass.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/scss.svg b/static/images/icons/scss.svg new file mode 100644 index 0000000..ca32b98 --- /dev/null +++ b/static/images/icons/scss.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/scully.svg b/static/images/icons/scully.svg new file mode 100644 index 0000000..8f71b32 --- /dev/null +++ b/static/images/icons/scully.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/shopify.svg b/static/images/icons/shopify.svg new file mode 100644 index 0000000..5072635 --- /dev/null +++ b/static/images/icons/shopify.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/single-page.svg b/static/images/icons/single-page.svg new file mode 100644 index 0000000..2ef5ae2 --- /dev/null +++ b/static/images/icons/single-page.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/snipcart.svg b/static/images/icons/snipcart.svg new file mode 100644 index 0000000..c81942b --- /dev/null +++ b/static/images/icons/snipcart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/stackbit.svg b/static/images/icons/stackbit.svg new file mode 100644 index 0000000..2a6dec8 --- /dev/null +++ b/static/images/icons/stackbit.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/static/images/icons/staticman.svg b/static/images/icons/staticman.svg new file mode 100644 index 0000000..010d28d --- /dev/null +++ b/static/images/icons/staticman.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/statiq.svg b/static/images/icons/statiq.svg new file mode 100644 index 0000000..d97f433 --- /dev/null +++ b/static/images/icons/statiq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/storyblok.svg b/static/images/icons/storyblok.svg new file mode 100644 index 0000000..9e5c256 --- /dev/null +++ b/static/images/icons/storyblok.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/styled-components.png b/static/images/icons/styled-components.png new file mode 100644 index 0000000000000000000000000000000000000000..e20a0b276688f29d2bcd43a35b18347888d30d1e GIT binary patch literal 6422 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 literal 0 HcmV?d00001 diff --git a/static/images/icons/stylus.svg b/static/images/icons/stylus.svg new file mode 100644 index 0000000..259921c --- /dev/null +++ b/static/images/icons/stylus.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + diff --git a/static/images/icons/sveltekit.svg b/static/images/icons/sveltekit.svg new file mode 100644 index 0000000..e1734cd --- /dev/null +++ b/static/images/icons/sveltekit.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/static/images/icons/tailwind.svg b/static/images/icons/tailwind.svg new file mode 100644 index 0000000..2b1a4d6 --- /dev/null +++ b/static/images/icons/tailwind.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/tina.svg b/static/images/icons/tina.svg new file mode 100644 index 0000000..8b253d3 --- /dev/null +++ b/static/images/icons/tina.svg @@ -0,0 +1,4 @@ + + + + diff --git a/static/images/icons/unibit-pink.svg b/static/images/icons/unibit-pink.svg new file mode 100644 index 0000000..66d6789 --- /dev/null +++ b/static/images/icons/unibit-pink.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/unibit.svg b/static/images/icons/unibit.svg new file mode 100644 index 0000000..bf7d169 --- /dev/null +++ b/static/images/icons/unibit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/vuepress.svg b/static/images/icons/vuepress.svg new file mode 100644 index 0000000..43df408 --- /dev/null +++ b/static/images/icons/vuepress.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/vuetify.svg b/static/images/icons/vuetify.svg new file mode 100644 index 0000000..f6fc82b --- /dev/null +++ b/static/images/icons/vuetify.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/static/images/icons/w3css.svg b/static/images/icons/w3css.svg new file mode 100644 index 0000000..93c936f --- /dev/null +++ b/static/images/icons/w3css.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/wordpress.svg b/static/images/icons/wordpress.svg new file mode 100644 index 0000000..c25a8ff --- /dev/null +++ b/static/images/icons/wordpress.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/zola.svg b/static/images/icons/zola.svg new file mode 100644 index 0000000..e1b899d --- /dev/null +++ b/static/images/icons/zola.svg @@ -0,0 +1,4 @@ + + + + diff --git a/static/images/jamstackthemes-screenshot.png b/static/images/jamstackthemes-screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..502f58bdc45fff9be659a2e24efb8cbfc74acc0d GIT binary patch literal 312777 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

NHAa@Gw)h4cRh@D2iN16hvZZu;Q80 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≫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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/images/social/github.svg b/static/images/social/github.svg new file mode 100755 index 0000000..40076ce --- /dev/null +++ b/static/images/social/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/social/twitter.svg b/static/images/social/twitter.svg new file mode 100755 index 0000000..5299b1d --- /dev/null +++ b/static/images/social/twitter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/stackbit.png b/static/images/stackbit.png new file mode 100644 index 0000000000000000000000000000000000000000..acdf0041ca4431cd98ef55df2ab705b5e303f8ee GIT binary patch literal 2999 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 literal 0 HcmV?d00001 diff --git a/static/images/ui/angle-double-left-solid.svg b/static/images/ui/angle-double-left-solid.svg new file mode 100644 index 0000000..95887a1 --- /dev/null +++ b/static/images/ui/angle-double-left-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/angle-double-right-solid.svg b/static/images/ui/angle-double-right-solid.svg new file mode 100644 index 0000000..998f859 --- /dev/null +++ b/static/images/ui/angle-double-right-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/check-solid.svg b/static/images/ui/check-solid.svg new file mode 100644 index 0000000..f91b74e --- /dev/null +++ b/static/images/ui/check-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/chevron-down-solid.svg b/static/images/ui/chevron-down-solid.svg new file mode 100644 index 0000000..026b7c0 --- /dev/null +++ b/static/images/ui/chevron-down-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/chevron-left-solid.svg b/static/images/ui/chevron-left-solid.svg new file mode 100644 index 0000000..41061c2 --- /dev/null +++ b/static/images/ui/chevron-left-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/chevron-right-solid.svg b/static/images/ui/chevron-right-solid.svg new file mode 100644 index 0000000..6f3ecc4 --- /dev/null +++ b/static/images/ui/chevron-right-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/external-link-alt-solid.svg b/static/images/ui/external-link-alt-solid.svg new file mode 100644 index 0000000..190df50 --- /dev/null +++ b/static/images/ui/external-link-alt-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/forks.svg b/static/images/ui/forks.svg new file mode 100644 index 0000000..c0b53a4 --- /dev/null +++ b/static/images/ui/forks.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/history-solid.svg b/static/images/ui/history-solid.svg new file mode 100644 index 0000000..ddcfda0 --- /dev/null +++ b/static/images/ui/history-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/issues.svg b/static/images/ui/issues.svg new file mode 100644 index 0000000..27647c3 --- /dev/null +++ b/static/images/ui/issues.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/minus-solid.svg b/static/images/ui/minus-solid.svg new file mode 100644 index 0000000..f72db5b --- /dev/null +++ b/static/images/ui/minus-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/minus-square-regular.svg b/static/images/ui/minus-square-regular.svg new file mode 100644 index 0000000..26c05a2 --- /dev/null +++ b/static/images/ui/minus-square-regular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/plus-solid.svg b/static/images/ui/plus-solid.svg new file mode 100644 index 0000000..bfa907d --- /dev/null +++ b/static/images/ui/plus-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/plus-square-regular.svg b/static/images/ui/plus-square-regular.svg new file mode 100644 index 0000000..b9e300d --- /dev/null +++ b/static/images/ui/plus-square-regular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/star-solid.svg b/static/images/ui/star-solid.svg new file mode 100644 index 0000000..ce74416 --- /dev/null +++ b/static/images/ui/star-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/ui/star.svg b/static/images/ui/star.svg new file mode 100644 index 0000000..4834aa7 --- /dev/null +++ b/static/images/ui/star.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/themes/jamstackthemes/assets/js/filter/filter-groups.js b/themes/jamstackthemes/assets/js/filter/filter-groups.js new file mode 100644 index 0000000..0da7d85 --- /dev/null +++ b/themes/jamstackthemes/assets/js/filter/filter-groups.js @@ -0,0 +1,46 @@ +const groups = { + ssg: [ + "eleventy", + "gatsby", + "gridsome", + "hexo", + "hugo", + "jekyll", + "next", + "vuepress", + "brunch", + "docusaurus", + "jigsaw", + "metalsmith", + "middleman", + "mkdocs", + "nuxt", + "pelican", + "platframe", + "sapper", + "scully", + "statiq", + "unibit" + ], + cms: [ + "contentful", + "cosmic", + "datocms", + "forestry", + "ghost", + "kontent", + "netlifycms", + "no-cms", + "airtable", + "directus", + "firebase", + 'graphcms', + "prismic", + "sanity", + "storyblok", + "wordpress", + "notion" + ], + css: ["bootstrap", "scss", "tailwind", "bulma", "postcss", "styled-components"], + archetype: ["blog", "portfolio", "business", "single-page", "ecommerce", "documentation", "personal"], +}; diff --git a/themes/jamstackthemes/assets/js/filter/filter-hash.js b/themes/jamstackthemes/assets/js/filter/filter-hash.js new file mode 100644 index 0000000..3cf1ace --- /dev/null +++ b/themes/jamstackthemes/assets/js/filter/filter-hash.js @@ -0,0 +1,182 @@ +var targetSelector = ".grid"; +var activeHash = ""; +var activePage = -1; + +/** + * Deserializes a hash segment (if present) into in an object. + * + * @return {object|null} + */ + +function deserializeHash() { + var hash = window.location.hash.replace(/^#/g, ""); + var obj = null; + var groups = []; + + if (!hash) return obj; + + obj = {}; + groups = hash.split("&"); + + groups.forEach(function(group) { + var pair = group.split("="); + var groupName = pair[0]; + + obj[groupName] = pair[1].split(","); + }); + + return obj; +} + +/** + * Serializes a uiState object into a string. + * + * @param {object} uiState + * @return {string} + */ + +function serializeUiState(uiState) { + var output = ""; + + for (var key in uiState) { + var values = uiState[key]; + + if (!values.length) continue; + + output += key + "="; + output += values.join(","); + output += "&"; + } + + output = output.replace(/&$/g, ""); + + return output; +} + +/** + * Constructs a `uiState` object using the + * `getFilterGroupSelectors()` API method. + * + * @return {object} + */ + +function getUiState() { + // NB: You will need to rename the object keys to match the names of + // your project's filter groups – these should match those defined + // in your HTML. + + var uiState = { + ssg: mixer.getFilterGroupSelectors("ssg").map(getValueFromSelector), + cms: mixer.getFilterGroupSelectors("cms").map(getValueFromSelector), + css: mixer.getFilterGroupSelectors("css").map(getValueFromSelector), + archetype: mixer.getFilterGroupSelectors("archetype").map(getValueFromSelector) + }; + + return uiState; +} + +/** + * Updates the URL hash whenever the current filter changes. + * + * @param {mixitup.State} state + * @return {void} + */ + +function setHash(state) { + var selector = state.activeFilter.selector; + + // Construct an object representing the current state of each + // filter group + + var uiState = getUiState(); + + // Create a URL hash string by serializing the uiState object + + var newHash = "#" + serializeUiState(uiState); + + if (selector === targetSelector && window.location.href.indexOf("#") > -1) { + // Equivalent to filter "all", and a hash exists, remove the hash + + activeHash = ""; + + history.replaceState(null, document.title, window.location.pathname); + } else if (newHash !== window.location.hash && selector !== targetSelector) { + // Change the hash + + activeHash = newHash; + + history.replaceState( + null, + document.title, + window.location.pathname + newHash + ); + } +} + +/** + * Updates the mixer to a previous UI state. + * + * @param {object|null} uiState + * @param {boolean} [animate] + * @return {Promise} + */ + +function syncMixerWithPreviousUiState(uiState, animate) { + var ssg = uiState && uiState.ssg ? uiState.ssg : []; + var cms = uiState && uiState.cms ? uiState.cms : []; + var css = uiState && uiState.css ? uiState.css : []; + var archetype = uiState && uiState.archetype ? uiState.archetype : []; + + mixer.setFilterGroupSelectors("ssg", ssg.map(getSelectorFromValue)); + mixer.setFilterGroupSelectors("cms", cms.map(getSelectorFromValue)); + mixer.setFilterGroupSelectors("css", css.map(getSelectorFromValue)); + mixer.setFilterGroupSelectors("archetype", archetype.map(getSelectorFromValue)); + + // Parse the filter groups (passing `false` will perform no animation) + + return mixer.parseFilterGroups(animate ? true : false); +} + +/** + * Converts a selector (e.g. '.green') into a simple value (e.g. 'green'). + * + * @param {string} selector + * @return {string} + */ + +function getValueFromSelector(selector) { + return selector.replace(/^./, ""); +} + +/** + * A function for filtering the values of the mixitup command object + * generated by calling the `parseFilterGroups()` method. + * + * @param {object} command + * @return {object} + */ + +function handleParseFilterGroups(command) { + if (activePage > 1) { + // If an activePage greater than 1 has been parsed + // from the URL, update the command with a pagination + // instruction + + command.paginate = activePage; + } + + return command; +} + + +/** + * Converts a simple value (e.g. 'green') into a selector (e.g. '.green'). + * + * @param {string} selector + * @return {string} + */ + +function getSelectorFromValue(value) { + return "." + value; +} + diff --git a/themes/jamstackthemes/assets/js/filter/filter-toggle.js b/themes/jamstackthemes/assets/js/filter/filter-toggle.js new file mode 100644 index 0000000..743e6bb --- /dev/null +++ b/themes/jamstackthemes/assets/js/filter/filter-toggle.js @@ -0,0 +1,11 @@ +document.querySelectorAll(".toggle-icon").forEach((toggle) => { + toggle.addEventListener('click', (e) => { + toggle.parentNode.parentNode.classList.toggle('closed'); + }); +}); + +document.querySelectorAll(".toggle-more").forEach((toggle) => { + toggle.addEventListener('click', (e) => { + toggle.parentNode.classList.toggle('closed'); + }); +}); diff --git a/themes/jamstackthemes/assets/js/filter/filter.js b/themes/jamstackthemes/assets/js/filter/filter.js new file mode 100644 index 0000000..9a8f01e --- /dev/null +++ b/themes/jamstackthemes/assets/js/filter/filter.js @@ -0,0 +1,116 @@ +const pageId = document.querySelector('body').id; +// const sortOrder = pageId === "page-all-themes" ? "stars:desc" : null; +const sortOrder = null; + +const mixer = mixitup('#grids-homepage', { + multifilter: { + enable: true, + logicWithinGroup: 'or', + logicBetweenGroups: 'and' + }, + animation: { + enable: false, + }, + selectors: { + target: '.grid' + }, + load: { + sort: sortOrder + }, + // pagination: { + // limit: 50, + // maintainActivePage: false + // }, + callbacks: { + onMixStart: function(state, futureState) { + let total = futureState.totalShow; + let count = document.querySelector('.count-number'); + count.textContent = total; + window.scrollTo(0,0); + updateFilterCounts(state, futureState); + }, + onMixClick: function(state, originalEvent) { + }, + onParseFilterGroups: handleParseFilterGroups, + onMixEnd: setHash + } +}); + +function getTriggerGroup(event) { + triggerGroup = null; + if (!event) { + return triggerGroup + } else if (event.classList.contains('filter-button')) { + triggerGroup = event.parentNode.parentNode.parentNode.id.slice(13); + }; + return triggerGroup; +} + +function updateFilterCounts(state, futureState) { + + let triggerGroup = getTriggerGroup(futureState.triggerElement) + let totalMatching = futureState.targets.map(theme => theme.className.trim().split(" ")); + let matching = futureState.matching.map(theme => theme.className.trim().split(" ")); + let hasMultipleActiveGroups = checkActiveGroups(); + + // Update Filter Counts + Object.keys(groups).forEach((group) => { + if (hasMultipleActiveGroups) { + resetCount(groups[group], totalMatching); + updateCount(groups[group], matching); + } else { + if (group === triggerGroup) { + resetCount(groups[group], totalMatching); + } else if (!triggerGroup) { + + } else { + updateCount(groups[group], matching); + } + } + }) +} + +function checkActiveGroups() { + let activeGroups = Object.keys(groups).map(group => { + return mixer.getFilterGroupSelectors(group) + }) + + let activeGroupsLength = 0 + activeGroups.forEach(group => { + if (group.length) { + activeGroupsLength += 1 + } + }) + return activeGroupsLength >= 2; +} + +function updateCount(group, matches) { + group.forEach(term => { + let count = matches.filter(match => { + return match.includes(term); + }) + const termDom = document.querySelector(`#filter-count-${term}`) + if (termDom) { + termDom.innerText = count.length + } + }) +} + +function resetCount(group, matches) { + group.forEach(term => { + let count = matches.filter(match => { + return match.includes(term); + }) + const termDom = document.querySelector(`#filter-count-${term}`) + if (termDom) { + termDom.innerText = count.length + } + }) +} + +var uiState = deserializeHash(); + +if (uiState) { + // If a valid uiState object is present on page load, filter the mixer + syncMixerWithPreviousUiState(uiState); +} diff --git a/themes/jamstackthemes/assets/js/libs/mixitup-multifilter.js b/themes/jamstackthemes/assets/js/libs/mixitup-multifilter.js new file mode 100755 index 0000000..77924fc --- /dev/null +++ b/themes/jamstackthemes/assets/js/libs/mixitup-multifilter.js @@ -0,0 +1,1256 @@ +/**! + * MixItUp MultiFilter v3.3.4 + * A UI-builder for powerful multidimensional filtering + * Build 6bbb142d-9851-4ca8-b6d4-f760362d93ec + * + * Requires mixitup.js >= v^3.1.2 + * + * @copyright Copyright 2014-2018 KunkaLabs Limited. + * @author KunkaLabs Limited. + * @link https://www.kunkalabs.com/mixitup-multifilter/ + * + * @license Commercial use requires a commercial license. + * https://www.kunkalabs.com/mixitup-multifilter/licenses/ + * + * Non-commercial use permitted under same terms as license. + * http://creativecommons.org/licenses/by-nc/3.0/ + */ +(function(window) { + 'use strict'; + + var mixitupMultifilter = function(mixitup) { + var h = mixitup.h; + var diacriticsMap; + + diacriticsMap = [ + ['A', /[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g], + ['AA', /[\uA732]/g], + ['AE', /[\u00C6\u01FC\u01E2]/g], + ['AO', /[\uA734]/g], + ['AU', /[\uA736]/g], + ['AV', /[\uA738\uA73A]/g], + ['AY', /[\uA73C]/g], + ['B', /[\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181]/g], + ['C', /[\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E]/g], + ['D', /[\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779]/g], + ['DZ', /[\u01F1\u01C4]/g], + ['Dz', /[\u01F2\u01C5]/g], + ['E', /[\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E]/g], + ['F', /[\u0046\u24BB\uFF26\u1E1E\u0191\uA77B]/g], + ['G', /[\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E]/g], + ['H', /[\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D]/g], + ['I', /[\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197]/g], + ['J', /[\u004A\u24BF\uFF2A\u0134\u0248]/g], + ['K', /[\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2]/g], + ['L', /[\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780]/g], + ['LJ', /[\u01C7]/g], + ['Lj', /[\u01C8]/g], + ['M', /[\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C]/g], + ['N', /[\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4]/g], + ['NJ', /[\u01CA]/g], + ['Nj', /[\u01CB]/g], + ['O', /[\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C]/g], + ['OI', /[\u01A2]/g], + ['OO', /[\uA74E]/g], + ['OU', /[\u0222]/g], + ['P', /[\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754]/g], + ['Q', /[\u0051\u24C6\uFF31\uA756\uA758\u024A]/g], + ['R', /[\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782]/g], + ['S', /[\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784]/g], + ['T', /[\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786]/g], + ['TZ', /[\uA728]/g], + ['U', /[\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244]/g], + ['V', /[\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245]/g], + ['VY', /[\uA760]/g], + ['W', /[\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72]/g], + ['X', /[\u0058\u24CD\uFF38\u1E8A\u1E8C]/g], + ['Y', /[\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE]/g], + ['Z', /[\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762]/g], + ['a', /[\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250]/g], + ['aa', /[\uA733]/g], + ['ae', /[\u00E6\u01FD\u01E3]/g], + ['ao', /[\uA735]/g], + ['au', /[\uA737]/g], + ['av', /[\uA739\uA73B]/g], + ['ay', /[\uA73D]/g], + ['b', /[\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253]/g], + ['c', /[\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184]/g], + ['d', /[\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A]/g], + ['dz', /[\u01F3\u01C6]/g], + ['e', /[\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD]/g], + ['f', /[\u0066\u24D5\uFF46\u1E1F\u0192\uA77C]/g], + ['g', /[\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F]/g], + ['h', /[\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265]/g], + ['hv', /[\u0195]/g], + ['i', /[\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131]/g], + ['j', /[\u006A\u24D9\uFF4A\u0135\u01F0\u0249]/g], + ['k', /[\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3]/g], + ['l', /[\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747]/g], + ['lj', /[\u01C9]/g], + ['m', /[\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F]/g], + ['n', /[\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5]/g], + ['nj', /[\u01CC]/g], + ['o', /[\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275]/g], + ['oi', /[\u01A3]/g], + ['ou', /[\u0223]/g], + ['oo', /[\uA74F]/g], + ['p', /[\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755]/g], + ['q', /[\u0071\u24E0\uFF51\u024B\uA757\uA759]/g], + ['r', /[\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783]/g], + ['s', /[\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B]/g], + ['t', /[\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787]/g], + ['tz', /[\uA729]/g], + ['u', /[\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289]/g], + ['v', /[\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C]/g], + ['vy', /[\uA761]/g], + ['w', /[\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73]/g], + ['x', /[\u0078\u24E7\uFF58\u1E8B\u1E8D]/g], + ['y', /[\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF]/g], + ['z', /[\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763]/g] + ]; + + if ( + !mixitup.CORE_VERSION || + !h.compareVersions(mixitupMultifilter.REQUIRE_CORE_VERSION, mixitup.CORE_VERSION) + ) { + throw new Error( + '[MixItUp Multifilter] MixItUp Multifilter v' + + mixitupMultifilter.EXTENSION_VERSION + + ' requires at least MixItUp v' + + mixitupMultifilter.REQUIRE_CORE_VERSION + ); + } + + /** + * A group of optional callback functions to be invoked at various + * points within the lifecycle of a mixer operation. + * + * @constructor + * @memberof mixitup.Config + * @name callbacks + * @namespace + * @public + * @since 3.0.0 + */ + + mixitup.ConfigCallbacks.registerAction('afterConstruct', 'multifilter', function() { + /** + * A callback function invoked whenever MultiFilter filter groups + * are parsed. This occurs whenever the user interacts with filter + * group UI, or when the `parseFilterGroups()` API method is called, + * but before the resulting filter operation has been triggered. + * + * By default, this generates the appropriate compound selector and + * filters the mixer using a `multimix()` API call internally. This + * callback can be used to transform the multimix command object sent + * to this API call. + * + * This is particularly useful when additional behavior such as sorting + * or pagination must be taken into account when using the MultiFilter API. + * + * The callback receives the generated multimix command object, and must + * also return a valid multimix command object. + * + * @example Example: Overriding the default filtering behavior with `onParseFilterGroups` + * var mixer = mixitup(containerEl, { + * callbacks: { + * onParseFilterGroups: function(command) { + * command.paginate = 3; + * command.sort = 'default:desc'; + * + * return command; + * } + * } + * }); + * + * @name onParseFilterGroups + * @memberof mixitup.Config.callbacks + * @instance + * @type {function} + * @default null + */ + + this.onParseFilterGroups = null; + }); + + /** + * A group of properties defining the behavior of your multifilter UI. + * + * @constructor + * @memberof mixitup.Config + * @name multifilter + * @namespace + * @public + * @since 3.0.0 + */ + + mixitup.ConfigMultifilter = function() { + + /** + * A boolean dictating whether or not to enable multifilter functionality. + * + * If `true`, MixItUp will query the DOM for any elements with a + * `data-filter-group` attribute present on instantiation. + * + * @name enable + * @memberof mixitup.Config.multifilter + * @instance + * @type {boolean} + * @default false + */ + + this.enable = false; + + /** + * A string dictating the logic to use when concatenating selectors within + * individual filter groups. + * + * If set to `'or'` (default), targets will be shown if they match any + * active filter in the group. + * + * If set to `'and'`, targets will be shown only if they match + * all active filters in the group. + * + * @name logicWithinGroup + * @memberof mixitup.Config.multifilter + * @instance + * @type {string} + * @default 'or' + */ + + this.logicWithinGroup = 'or'; + + /** + * A string dictating the logic to use when concatenating each group's + * selectors into one single selector. + * + * If set to `'and'` (default), targets will be shown only if they match + * the combined active selectors of all groups. + * + * If set to `'or'`, targets will be shown if they match the active selectors + * of any individual group. + * + * @name logicBetweenGroups + * @memberof mixitup.Config.multifilter + * @instance + * @type {string} + * @default 'and' + */ + + this.logicBetweenGroups = 'and'; + + /** + * An integer dictating the minimum number of characters at which the value + * of a text input will be included as a multifilter. This prevents short or + * incomplete words with many potential matches from triggering + * filter operations. + * + * @name minSearchLength + * @memberof mixitup.Config.multifilter + * @instance + * @type {number} + * @default 3 + */ + + this.minSearchLength = 3; + + /** + * A string dictating when the parsing of filter groups should occur. + * + * If set to `'change'` (default), the mixer will be filtered whenever the + * filtering UI is interacted with. The mode provides real-time filtering with + * instant feedback. + * + * If set to `'submit'`, the mixer will only be filtered when a submit button is + * clicked (if using a `

` element as a parent). This enables the user to firstly + * make their selection, and then trigger filtering once they have + * finished making their selection. + * + * Alternatively, the `mixer.parseFilterGroups()` method can be called via the API at any + * time to trigger the parsing of filter groups and filter the mixer. + * + * @name parseOn + * @memberof mixitup.Config.multifilter + * @instance + * @type {string} + * @default 'change' + */ + + this.parseOn = 'change'; + + /** + * An integer dictating the duration in ms that must elapse between keyup + * events in order to trigger a change. + * + * Setting a comfortable delay of ~350ms prevents the mixer from being + * thrashed while typing occurs. + * + * @name keyupThrottleDuration + * @memberof mixitup.Config.multifilter + * @instance + * @type {number} + * @default 350 + */ + + this.keyupThrottleDuration = 350; + + h.seal(this); + }; + + /** + * The MixItUp configuration object is extended with properties relating to + * the MultiFilter extension. + * + * For the full list of configuration options, please refer to the MixItUp + * core documentation. + * + * @constructor + * @memberof mixitup + * @name Config + * @namespace + * @public + * @since 2.0.0 + */ + + mixitup.Config.registerAction('beforeConstruct', 'multifilter', function() { + this.multifilter = new mixitup.ConfigMultifilter(); + }); + + mixitup.MultifilterFormEventTracker = function() { + this.form = null; + this.totalBound = 0; + this.totalHandled = 0; + + h.seal(this); + }; + + mixitup.FilterGroupDom = function() { + this.el = null; + this.form = null; + + h.seal(this); + }; + + mixitup.FilterGroup = function() { + this.name = ''; + this.dom = new mixitup.FilterGroupDom(); + this.activeSelectors = []; + this.activeFilters = []; + this.activeToggles = []; + this.handler = null; + this.mixer = null; + this.logic = 'or'; + this.parseOn = 'change'; + this.keyupTimeout = -1; + + h.seal(this); + }; + + h.extend(mixitup.FilterGroup.prototype, { + + /** + * @private + * @param {HTMLELement} el + * @param {mixitup.Mixer} mixer + * @return {void} + */ + + init: function(el, mixer) { + var self = this, + logic = el.getAttribute('data-logic'); + + self.dom.el = el; + + this.name = self.dom.el.getAttribute('data-filter-group') || ''; + + self.cacheDom(); + + if (self.dom.form) { + self.enableButtons(); + } + + self.mixer = mixer; + + if ((logic && logic.toLowerCase() === 'and') || mixer.config.multifilter.logicWithinGroup === 'and') { + // override default group logic + + self.logic = 'and'; + + } + + self.bindEvents(); + }, + + /** + * @private + * @return {void} + */ + + cacheDom: function() { + var self = this; + + self.dom.form = h.closestParent(self.dom.el, 'form', true); + }, + + enableButtons: function() { + var self = this, + buttons = self.dom.form.querySelectorAll('button[type="submit"]:disabled'), + button = null, + i = -1; + + for (i = 0; button = buttons[i]; i++) { + if (button.disabled) { + button.disabled = false; + } + } + }, + + /** + * @private + * @return {void} + */ + + bindEvents: function() { + var self = this; + + self.handler = function(e) { + switch (e.type) { + case 'reset': + case 'submit': + self.handleFormEvent(e); + + break; + default: + self['handle' + h.pascalCase(e.type)](e); + } + }; + + h.on(self.dom.el, 'click', self.handler); + h.on(self.dom.el, 'change', self.handler); + h.on(self.dom.el, 'keyup', self.handler); + + if (self.dom.form) { + h.on(self.dom.form, 'reset', self.handler); + h.on(self.dom.form, 'submit', self.handler); + } + }, + + /** + * @private + * @return {void} + */ + + unbindEvents: function() { + var self = this; + + h.off(self.dom.el, 'click', self.handler); + h.off(self.dom.el, 'change', self.handler); + h.off(self.dom.el, 'keyup', self.handler); + + if (self.dom.form) { + h.off(self.dom.form, 'reset', self.handler); + h.off(self.dom.form, 'submit', self.handler); + } + + self.handler = null; + }, + + /** + * @private + * @param {MouseEvent} e + * @return {void} + */ + + handleClick: function(e) { + var self = this, + mixer = self.mixer, + returnValue = null, + controlEl = h.closestParent(e.target, '[data-filter], [data-toggle]', true), + controlSelector = '', + index = -1, + selector = ''; + + if (!controlEl) return; + + if ((controlSelector = self.mixer.config.selectors.control) && !controlEl.matches(controlSelector)) { + return; + } + + e.stopPropagation(); + + if (!mixer.lastClicked) { + mixer.lastClicked = controlEl; + } + + if (typeof mixer.config.callbacks.onMixClick === 'function') { + returnValue = mixer.config.callbacks.onMixClick.call(mixer.lastClicked, mixer.state, e, self); + + if (returnValue === false) { + // User has returned `false` from the callback, so do not handle click + + return; + } + } + + if (controlEl.matches('[data-filter]')) { + selector = controlEl.getAttribute('data-filter'); + + self.activeToggles = []; + self.activeSelectors = self.activeFilters = [selector]; + } else if (controlEl.matches('[data-toggle]')) { + selector = controlEl.getAttribute('data-toggle'); + + self.activeFilters = []; + + if ((index = self.activeToggles.indexOf(selector)) > -1) { + self.activeToggles.splice(index, 1); + } else { + self.activeToggles.push(selector); + } + + if (self.logic === 'and') { + // Compress into single node + + self.activeSelectors = [self.activeToggles]; + } else { + self.activeSelectors = self.activeToggles; + } + } + + self.updateControls(); + + if (self.mixer.config.multifilter.parseOn === 'change') { + self.mixer.parseFilterGroups(); + } + }, + + /** + * @private + * @param {Event} e + * @return {void} + */ + + handleChange: function(e) { + var self = this, + input = e.target; + + e.stopPropagation(); + + switch(input.type) { + case 'text': + case 'search': + case 'email': + case 'select-one': + case 'radio': + self.getSingleValue(input); + + break; + case 'checkbox': + case 'select-multiple': + self.getMultipleValues(input); + + break; + } + + if (self.mixer.config.multifilter.parseOn === 'change') { + self.mixer.parseFilterGroups(); + } + }, + + handleKeyup: function(e) { + var self = this, + input = e.target; + + // NB: Selects can fire keyup events (e.g. multiselect, textual search) + + if (['text', 'search', 'email'].indexOf(input.type) < 0) return; + + if (self.mixer.config.multifilter.parseOn !== 'change') { + self.mixer.getSingleValue(input); + + return; + } + + clearTimeout(self.keyupTimeout); + + self.keyupTimeout = setTimeout(function() { + self.getSingleValue(input); + self.mixer.parseFilterGroups(); + }, self.mixer.config.multifilter.keyupThrottleDuration); + }, + + /** + * @private + * @param {Event} e + * @return {void} + */ + + handleFormEvent: function(e) { + var self = this, + tracker = null, + group = null, + i = -1; + + if (e.type === 'submit') { + e.preventDefault(); + } + + if (e.type === 'reset') { + self.activeFilters = + self.activeToggles = + self.activeSelectors = []; + + self.updateControls(); + } + + if (!self.mixer.multifilterFormEventTracker) { + tracker = self.mixer.multifilterFormEventTracker = new mixitup.MultifilterFormEventTracker(); + + tracker.form = e.target; + + for (i = 0; group = self.mixer.filterGroups[i]; i++) { + if (group.dom.form !== e.target) continue; + + tracker.totalBound++; + } + } else { + tracker = self.mixer.multifilterFormEventTracker; + } + + if (e.target === tracker.form) { + tracker.totalHandled++; + + if (tracker.totalHandled === tracker.totalBound) { + self.mixer.multifilterFormEventTracker = null; + + if (e.type === 'submit' || self.mixer.config.multifilter.parseOn === 'change') { + self.mixer.parseFilterGroups(); + } + } + } + }, + + /** + * @private + * @param {HTMLELement} input + * @return {void} + */ + + getSingleValue: function(input) { + var self = this, + diacriticMap = null, + attributeName = '', + selector = '', + value = '', + i = -1; + + if (input.type.match(/text|search|email/g)) { + attributeName = input.getAttribute('data-search-attribute'); + + if (!attributeName) { + throw new Error('[MixItUp MultiFilter] A valid `data-search-attribute` must be present on text inputs'); + } + + if (input.value.length < self.mixer.config.multifilter.minSearchLength) { + self.activeSelectors = self.activeFilters = self.activeToggles = ['']; + + return; + } + + // Lowercase and trim + + value = input.value.toLowerCase().trim(); + + // Replace diacritics + + for (i = 0; (diacriticMap = diacriticsMap[i]); i++) { + value = value.replace(diacriticMap[1], diacriticMap[0]); + } + + // Strip non-word characters + + value = value.replace(/\W+/g, ' '); + + selector = '[' + attributeName + '*="' + value + '"]'; + } else { + selector = input.value; + } + + if (typeof input.value === 'string') { + self.activeSelectors = + self.activeToggles = + self.activeFilters = + selector ? [selector] : []; + } + }, + + /** + * @private + * @param {HTMLELement} input + * @return {void} + */ + + getMultipleValues: function(input) { + var self = this, + activeToggles = [], + query = '', + item = null, + items = null, + i = -1; + + switch (input.type) { + case 'checkbox': + query = 'input[type="checkbox"]'; + + break; + case 'select-multiple': + query = 'option'; + } + + items = self.dom.el.querySelectorAll(query); + + for (i = 0; item = items[i]; i++) { + if ((item.checked || item.selected) && item.value) { + activeToggles.push(item.value); + } + } + + self.activeFilters = []; + self.activeToggles = activeToggles; + + if (self.logic === 'and') { + // Compress into single node + + self.activeSelectors = [activeToggles]; + } else { + self.activeSelectors = activeToggles; + } + }, + + /** + * @private + * @param {Array.} [controlEls] + * @return {void} + */ + + updateControls: function(controlEls) { + var self = this, + controlEl = null, + controlSelector = '', + controlsSelector = '', + type = '', + i = -1; + + controlSelector = self.mixer.config.selectors.control.trim(); + + controlsSelector = [ + '[data-filter]' + controlSelector, + '[data-toggle]' + controlSelector + ].join(', '); + + controlEls = controlEls || self.dom.el.querySelectorAll(controlsSelector); + + for (i = 0; controlEl = controlEls[i]; i++) { + type = Boolean(controlEl.getAttribute('data-toggle')) ? 'toggle' : 'filter'; + + self.updateControl(controlEl, type); + } + }, + + /** + * @private + * @param {HTMLELement} controlEl + * @param {string} type + * @return {void} + */ + + updateControl: function(controlEl, type) { + var self = this, + selector = controlEl.getAttribute('data-' + type), + activeControls = self.activeToggles.concat(self.activeFilters), + activeClassName = ''; + + activeClassName = h.getClassname(self.mixer.config.classNames, type, self.mixer.config.classNames.modifierActive); + + if (activeControls.indexOf(selector) > -1) { + h.addClass(controlEl, activeClassName); + } else { + h.removeClass(controlEl, activeClassName); + } + }, + + /** + * @private + */ + + updateUi: function() { + var self = this, + controlEls = self.dom.el.querySelectorAll('[data-filter], [data-toggle]'), + inputEls = self.dom.el.querySelectorAll('input[type="radio"], input[type="checkbox"], option'), + activeControls = self.activeToggles.concat(self.activeFilters), + isActive = false, + inputEl = null, + i = -1; + + if (controlEls.length) { + self.updateControls(controlEls, true); + } + + for (i = 0; inputEl = inputEls[i]; i++) { + isActive = activeControls.indexOf(inputEl.value) > -1; + + switch (inputEl.tagName.toLowerCase()) { + case 'option': + inputEl.selected = isActive; + + break; + case 'input': + inputEl.checked = isActive; + + break; + } + } + } + }); + + mixitup.MixerDom.registerAction('afterConstruct', 'multifilter', function() { + this.filterGroups = []; + }); + + /** + * The `mixitup.Mixer` class is extended with API methods relating to + * the MultiFilter extension. + * + * For the full list of API methods, please refer to the MixItUp + * core documentation. + * + * @constructor + * @namespace + * @name Mixer + * @memberof mixitup + * @public + * @since 3.0.0 + */ + + mixitup.Mixer.registerAction('afterConstruct', 'multifilter', function() { + this.filterGroups = []; + this.filterGroupsHash = {}; + this.multifilterFormEventTracker = null; + }); + + mixitup.Mixer.registerAction('afterCacheDom', 'multifilter', function() { + var self = this, + parent = null; + + if (!self.config.multifilter.enable) return; + + switch (self.config.controls.scope) { + case 'local': + parent = self.dom.container; + + break; + case 'global': + parent = self.dom.document; + + break; + default: + throw new Error(mixitup.messages.ERROR_CONFIG_INVALID_CONTROLS_SCOPE); + } + + self.dom.filterGroups = parent.querySelectorAll('[data-filter-group]'); + }); + + mixitup.Mixer.registerAction('beforeInitControls', 'multifilter', function() { + var self = this; + + if (!self.config.multifilter.enable) return; + + self.config.controls.live = true; // force live controls if multifilter is enabled + }); + + mixitup.Mixer.registerAction('afterSanitizeConfig', 'multifilter', function() { + var self = this; + + self.config.multifilter.logicBetweenGroups = self.config.multifilter.logicBetweenGroups.toLowerCase().trim(); + self.config.multifilter.logicWithinGroup = self.config.multifilter.logicWithinGroup.toLowerCase().trim(); + }); + + mixitup.Mixer.registerAction('afterAttach', 'multifilter', function() { + var self = this; + + if (self.dom.filterGroups.length) { + self.indexFilterGroups(); + } + }); + + mixitup.Mixer.registerAction('afterUpdateControls', 'multifilter', function() { + var self = this, + group = null, + i = -1; + + for (i = 0; group = self.filterGroups[i]; i++) { + group.updateControls(); + } + }); + + mixitup.Mixer.registerAction('beforeDestroy', 'multifilter', function() { + var self = this, + group = null, + i = -1; + + for (i = 0; group = self.filterGroups[i]; i++) { + group.unbindEvents(); + } + }); + + mixitup.Mixer.extend( + /** @lends mixitup.Mixer */ + { + /** + * @private + * @return {void} + */ + + indexFilterGroups: function() { + var self = this, + filterGroup = null, + el = null, + i = -1; + + for (i = 0; el = self.dom.filterGroups[i]; i++) { + filterGroup = new mixitup.FilterGroup(); + + filterGroup.init(el, self); + + self.filterGroups.push(filterGroup); + + if (filterGroup.name) { + // If present, also index by name + + if (typeof self.filterGroupsHash[filterGroup.name] !== 'undefined') { + throw new Error('[MixItUp MultiFilter] A filter group with name "' + filterGroup.name + '" already exists'); + } + + self.filterGroupsHash[filterGroup.name] = filterGroup; + } + } + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Array<*>} args + * @return {mixitup.UserInstruction} + */ + + parseParseFilterGroupsArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + arg = null, + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandFilter(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (typeof arg === 'boolean') { + instruction.animate = arg; + } else if (typeof arg === 'function') { + instruction.callback = arg; + } + } + + h.freeze(instruction); + + return instruction; + }, + + /** + * Recursively builds up paths between all possible permutations + * of filter group nodes according to the defined logic. + * + * @private + * @return {Array.>} + */ + + getFilterGroupPaths: function() { + var self = this, + buildPath = null, + crawl = null, + nodes = null, + matrix = [], + paths = [], + trackers = [], + i = -1; + + for (i = 0; i < self.filterGroups.length; i++) { + // Filter out groups without any active filters + + if ((nodes = self.filterGroups[i].activeSelectors).length) { + matrix.push(nodes); + + // Initialise tracker for each group + + trackers.push(0); + } + } + + buildPath = function() { + var node = null, + path = [], + i = -1; + + for (i = 0; i < matrix.length; i++) { + node = matrix[i][trackers[i]]; + + if (Array.isArray(node)) { + // AND logic within group + + node = node.join(''); + } + + path.push(node); + } + + path = h.clean(path); + + paths.push(path); + }; + + crawl = function(index) { + index = index || 0; + + var nodes = matrix[index]; + + while (trackers[index] < nodes.length) { + if (index < matrix.length - 1) { + // If not last, recurse + + crawl(index + 1); + } else { + // Last, build selector + + buildPath(); + } + + trackers[index]++; + } + + trackers[index] = 0; + }; + + if (!matrix.length) return ''; + + crawl(); + + return paths; + }, + + /** + * Builds up a selector string from a provided paths array. + * + * @private + * @param {Array.>} paths + * @return {string} + */ + + buildSelectorFromPaths: function(paths) { + var self = this, + path = null, + output = [], + pathSelector = '', + nodeDelineator = '', + i = -1; + + if (!paths.length) { + return ''; + } + + if (self.config.multifilter.logicBetweenGroups === 'or') { + nodeDelineator = ', '; + } + + if (paths.length > 1) { + for (i = 0; i < paths.length; i++) { + path = paths[i]; + + pathSelector = path.join(nodeDelineator); + + if (output.indexOf(pathSelector) < 0) { + output.push(pathSelector); + } + } + + return output.join(', '); + } else { + return paths[0].join(nodeDelineator); + } + }, + + /** + * Traverses the currently active filters in all groups, building up a + * compound selector string as per the defined logic. A filter operation + * is then called on the mixer using the resulting selector. + * + * This method can be used to programmatically trigger the parsing of + * filter groups after manipulations to a group's active selector(s) by + * the `.setFilterGroupSelectors()` API method. + * + * @example + * + * .parseFilterGroups([animate] [, callback]) + * + * @example Example: Triggering parsing after programmatically changing the values of a filter group + * + * mixer.setFilterGroupSelectors('color', ['.green', '.blue']); + * + * mixer.parseFilterGroups(); + * + * @public + * @since 3.0.0 + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + parseFilterGroups: function() { + var self = this, + instruction = self.parseFilterArgs(arguments), + paths = self.getFilterGroupPaths(), + selector = self.buildSelectorFromPaths(paths), + callback = null, + command = {}; + + if (selector === '') { + selector = self.config.controls.toggleDefault; + } + + instruction.command.selector = selector; + + command.filter = instruction.command; + + if (typeof (callback = self.config.callbacks.onParseFilterGroups) === 'function') { + command = callback(command); + } + + return self.multimix(command, instruction.animate, instruction.callback); + }, + + /** + * Programmatically sets one or more active selectors for a specific filter + * group and updates the group's UI. + * + * Because MixItUp has no way of knowing how to break down a provided + * compound selector into its component groups, we can not use the + * standard `.filter()` or `toggleOn()/toggleOff()` API methods when using + * the MultiFilter extension. Instead, this method allows us to perform + * multi-dimensional filtering via the API by setting the active selectors of + * individual groups and then triggering the `.parseFilterGroups()` method. + * + * If setting multiple active selectors, do not pass a compound selector. + * Instead, pass an array with each item containing a single selector + * string as in example 2. + * + * @example + * + * .setFilterGroupSelectors(groupName, selectors) + * + * @example Example 1: Setting a single active selector for a "color" group + * + * mixer.setFilterGroupSelectors('color', '.green'); + * + * mixer.parseFilterGroups(); + * + * @example Example 2: Setting multiple active selectors for a "size" group + * + * mixer.setFilterGroupSelectors('size', ['.small', '.large']); + * + * mixer.parseFilterGroups(); + * + * @public + * @since 3.2.0 + * @param {string} groupName The name of the filter group as defined in the markup via the `data-filter-group` attribute. + * @param {(string|Array.)} selectors A single selector string, or multiple selector strings as an array. + * @return {void} + */ + + setFilterGroupSelectors: function(groupName, selectors) { + var self = this, + filterGroup = null; + + selectors = Array.isArray(selectors) ? selectors : [selectors]; + + if (typeof (filterGroup = self.filterGroupsHash[groupName]) === 'undefined') { + throw new Error('[MixItUp MultiFilter] No filter group could be found with the name "' + groupName + '"'); + } + + filterGroup.activeToggles = selectors.slice(); + + if (filterGroup.logic === 'and') { + // Compress into single node + + filterGroup.activeSelectors = [filterGroup.activeToggles]; + } else { + filterGroup.activeSelectors = filterGroup.activeToggles; + } + + filterGroup.updateUi(filterGroup.activeToggles); + }, + + /** + * Returns an array of active selectors for a specific filter group. + * + * @example + * + * .getFilterGroupSelectors(groupName) + * + * @example Example: Retrieving the active selectors for a "size" group + * + * mixer.getFilterGroupSelectors('size'); // ['.small', '.large'] + * + * @public + * @since 3.2.0 + * @param {string} groupName The name of the filter group as defined in the markup via the `data-filter-group` attribute. + * @return {void} + */ + + getFilterGroupSelectors: function(groupName) { + var self = this, + filterGroup = null; + + if (typeof (filterGroup = self.filterGroupsHash[groupName]) === 'undefined') { + throw new Error('[MixItUp MultiFilter] No filter group could be found with the name "' + groupName + '"'); + } + + return filterGroup.activeToggles.slice(); + } + }); + + mixitup.Facade.registerAction('afterConstruct', 'multifilter', function(mixer) { + this.parseFilterGroups = mixer.parseFilterGroups.bind(mixer); + this.setFilterGroupSelectors = mixer.setFilterGroupSelectors.bind(mixer); + this.getFilterGroupSelectors = mixer.getFilterGroupSelectors.bind(mixer); + }); }; + + mixitupMultifilter.TYPE = 'mixitup-extension'; + mixitupMultifilter.NAME = 'mixitup-multifilter'; + mixitupMultifilter.EXTENSION_VERSION = '3.3.4'; + mixitupMultifilter.REQUIRE_CORE_VERSION = '^3.1.2'; + + if (typeof exports === 'object' && typeof module === 'object') { + module.exports = mixitupMultifilter; + } else if (typeof define === 'function' && define.amd) { + define(function() { + return mixitupMultifilter; + }); + } else if (window.mixitup && typeof window.mixitup === 'function') { + mixitupMultifilter(window.mixitup); + } else { + throw new Error('[MixItUp MultiFilter] MixItUp core not found'); + }})(window); \ No newline at end of file diff --git a/themes/jamstackthemes/assets/js/libs/mixitup-pagination.js b/themes/jamstackthemes/assets/js/libs/mixitup-pagination.js new file mode 100755 index 0000000..55ba128 --- /dev/null +++ b/themes/jamstackthemes/assets/js/libs/mixitup-pagination.js @@ -0,0 +1,1974 @@ +/**! + * MixItUp Pagination v3.3.0 + * Client-side pagination for filtered and sorted content + * Build 875b7d31-63d1-4040-ac6f-b1c814027891 + * + * Requires mixitup.js >= v^3.1.8 + * + * @copyright Copyright 2014-2017 KunkaLabs Limited. + * @author KunkaLabs Limited. + * @link https://www.kunkalabs.com/mixitup-pagination/ + * + * @license Commercial use requires a commercial license. + * https://www.kunkalabs.com/mixitup-pagination/licenses/ + * + * Non-commercial use permitted under same terms as license. + * http://creativecommons.org/licenses/by-nc/3.0/ + */ + +(function(window) { + 'use strict'; + + var mixitupPagination = function(mixitup) { + var h = mixitup.h; + + if ( + !mixitup.CORE_VERSION || + !h.compareVersions(mixitupPagination.REQUIRE_CORE_VERSION, mixitup.CORE_VERSION) + ) { + throw new Error( + '[MixItUp Pagination] MixItUp Pagination ' + + mixitupPagination.EXTENSION_VERSION + + ' requires at least MixItUp ' + + mixitupPagination.REQUIRE_CORE_VERSION + ); + } + + /** + * A group of optional callback functions to be invoked at various + * points within the lifecycle of a mixer operation. + * + * @constructor + * @memberof mixitup.Config + * @name callbacks + * @namespace + * @public + * @since 2.0.0 + */ + + mixitup.ConfigCallbacks.registerAction('afterConstruct', 'pagination', function() { + /** + * A callback function invoked whenever a pagination operation starts. + * + * This function is equivalent to `onMixStart`, and is invoked immediately + * after it with the same arguments. Unlike `onMixStart` however, it will + * not be invoked for filter or sort operations. + * + * + * @name onPaginateStart + * @memberof mixitup.Config.callbacks + * @instance + * @type {function} + * @default null + */ + + this.onPaginateStart = null; + + /** + * A callback function invoked whenever a pagination operation ends. + * + * This function is equivalent to `onMixEnd`, and is invoked immediately + * after it with the same arguments. Unlike `onMixEnd` however, it will + * not be invoked for filter or sort operations. + * + * @name onPaginateStart + * @memberof mixitup.Config.callbacks + * @instance + * @type {function} + * @default null + */ + + this.onPaginateEnd = null; + }); + + /** + * A group of properties defining the output and structure of class names programmatically + * added to controls and containers to reflect the state of the mixer. + * + * @constructor + * @memberof mixitup.Config + * @name classNames + * @namespace + * @public + * @since 3.0.0 + */ + + mixitup.ConfigClassNames.registerAction('afterConstruct', 'pagination', function() { + + /** + * The "element" portion of the class name added to pager controls. + * + * @example Example: changing the `config.classNames.elementPager` value + * + * // Change from the default value of 'control' to 'pager' + * + * var mixer = mixitup(containerEl, { + * classNames: { + * elementPager: 'pager' + * } + * }); + * + * // Base pager output: "mixitup-pager" + * + * @name elementPager + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'control' + */ + + this.elementPager = 'control'; + + /** + * The "element" portion of the class name added to the page list element, when it is + * in its disabled state. + * + * The page list element is the containing element in which pagers are rendered. + * + * @example Example: changing the `config.classNames.elementPageList` value + * + * // Change from the default value of 'page-list' to 'pagination-links' + * + * var mixer = mixitup(containerEl, { + * classNames: { + * elementPageList: 'pagination-links' + * } + * }); + * + * // Disabled page-list output: "mixitup-pagination-links-disabled" + * + * @name elementPageList + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'page-list' + */ + + this.elementPageList = 'page-list'; + + /** + * The "element" portion of the class name added to the page stats element, when it is + * in its disabled state. + * + * The page stats element is the containing element in which information about the + * current page and total number of pages is rendered. + * + * @example Example: changing the `config.classNames.elementPageStats` value + * + * // Change from the default value of 'page-stats' to 'pagination-info' + * + * var mixer = mixitup(containerEl, { + * classNames: { + * elementPageList: 'pagination-info' + * } + * }); + * + * // Disabled page-list output: "mixitup-pagination-info-disabled" + * + * @name elementPageStats + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'page-stats' + */ + + this.elementPageStats = 'page-stats'; + + /** + * The "modifier" portion of the class name added to the first pager in the list of pager controls. + * + * @name modifierFirst + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'first' + */ + + this.modifierFirst = 'first'; + + /** + * The "modifier" portion of the class name added to the last pager in the list of pager controls. + * + * @name modifierLast + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'last' + */ + + this.modifierLast = 'last'; + + /** + * The "modifier" portion of the class name added to the previous pager in the list of pager controls. + * + * @name modifierLast + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'prev' + */ + + this.modifierPrev = 'prev'; + + /** + * The "modifier" portion of the class name added to the next pager in the list of pager controls. + * + * @name modifierNext + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'next' + */ + + this.modifierNext = 'next'; + + /** + * The "modifier" portion of the class name added to truncation markers in the list of pager controls. + * + * @name modifierTruncationMarker + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'truncation-marker' + */ + + this.modifierTruncationMarker = 'truncation-marker'; + }); + + /** + * A group of properties defining the initial state of the mixer on load (instantiation). + * + * @constructor + * @memberof mixitup.Config + * @name load + * @namespace + * @public + * @since 2.0.0 + */ + + mixitup.ConfigLoad.registerAction('afterConstruct', 'pagination', function() { + /** + * An integer defining the starting page on load, if a page limit is active. + * + * @example Example: Defining a start page other than 1 to be applied on load + * + * // The mixer will show page 3 on load, with 8 items per page. + * + * var mixer = mixitup(containerEl, { + * pagination: { + * limit: 8 + * }, + * load: { + * page: 3 + * } + * }); + * + * @name page + * @memberof mixitup.Config.load + * @instance + * @type {number} + * @default 1 + */ + + this.page = 1; + }); + + /** + * A group of properties defining the mixer's pagination behavior. + * + * @constructor + * @memberof mixitup.Config + * @name pagination + * @namespace + * @public + * @since 2.0.0 + */ + + mixitup.ConfigPagination = function() { + + /** + * A boolean dictating whether or not MixItUp should render a list of pager controls. + * + * If you wish to control pagination functionality via the API, or your own UI, this can be set to `false`. + * + * In order for this functionality to work, you must provide MixItUp with a `pageList` + * element matching the selector defined in `selectors.pageList`. Pager controls will be + * rendered inside this element as per the templates defined for the `templates.pager` + * and related configuration options, or if set, a custom render + * function supplied to the `render.pager` configuration option. + * + * @example Example: Disabling the rendering of the built-in "page list" UI + * + * var mixer = mixitup(containerEl, { + * pagination: { + * limit: 8, + * generatePageList: false + * } + * }); + * + * @name generatePageList + * @memberof mixitup.Config.pagination + * @instance + * @type {boolean} + * @default true + */ + + this.generatePageList = true; + + /** + * A boolean dictating whether or not MixItUp should render a stats about the + * current page (e.g. "1 to 4 of 16"). + * + * In order for this functionality to work, you must provide MixItUp with a `pageStats` + * element matching the selector defined in `selectors.pageStats`. Page stats content will + * be rendered inside this element as per the templates defined for the `templates.pageStats` + * and `templates.pageStatsSingle` configuration options, or if set, a custom render + * function supplied to the `render.pageStats` configuration option. + * + * @example Example: Disabling the rendering of the built-in "page stats" UI + * + * var mixer = mixitup(containerEl, { + * pagination: { + * limit: 8, + * generatePageStats: false + * } + * }); + * + * @name generatePageStats + * @memberof mixitup.Config.pagination + * @instance + * @type {boolean} + * @default true + */ + + this.generatePageStats = true; + + /** + * A boolean dictating whether or not to maintain the active page when switching + * from filter to filter. + * + * By default, MixItUp will attempt to maintain the active page or its highest + * equivalent in the new collection of matching targets (e.g. page 3 would become + * page 2 if there are not enough targets in the new collection), but by setting + * this option to `false`, changing the active filter will always cause the mixer + * to revert to page one of the new collection. + * + * @example Example: Ensuring that the mixer reverts to page one when filtered + * + * var mixer = mixitup(containerEl, { + * pagination: { + * limit: 8, + * maintainActivePage: false + * } + * }); + * + * @name maintainActivePage + * @memberof mixitup.Config.pagination + * @instance + * @type {boolean} + * @default true + */ + + this.maintainActivePage = true; + + /** + * A boolean dictating whether or not to allow "looping" of the built-in previous + * and next pagination controls. + * + * By default, when on the first page, the "previous" button will be disabled, + * and when on the last page, the "next" button will be disabled. By setting + * this option to `true`, the user may loop from the first to last page and + * vice-versa. + * + * @example Example: Allowing prev/next controls to "loop" through pages + * + * var mixer = mixitup(containerEl, { + * pagination: { + * limit: 8, + * loop: true + * } + * }); + * + * @name loop + * @memberof mixitup.Config.pagination + * @instance + * @type {boolean} + * @default false + */ + + this.loop = false; + + /** + * A boolean dictating whether or not to prevent rendering of the built-in + * "page list" UI if the matching collection of targets has only enough content + * for one page. + * + * @example Example: Hiding the page list UI if only one page + * + * var mixer = mixitup(containerEl, { + * pagination: { + * limit: 8, + * hidePageListIfSinglePage: true + * } + * }); + * + * @name hidePageListIfSinglePage + * @memberof mixitup.Config.pagination + * @instance + * @type {boolean} + * @default false + */ + + this.hidePageListIfSinglePage = false; + + /** + * A boolean dictating whether or not to prevent rendering of the built-in + * "page stats" UI if the matching collection of targets has only enough content + * for one page. + * + * @example Example: Hiding the page stats UI if only one page + * + * var mixer = mixitup(containerEl, { + * pagination: { + * limit: 8, + * hidePageStatsIfSinglePage: true + * } + * }); + * + * @name hidePageStatsIfSinglePage + * @memberof mixitup.Config.pagination + * @instance + * @type {boolean} + * @default false + */ + + this.hidePageStatsIfSinglePage = false; + + /** + * A number defining the maximum number of items per page. + * + * By default, this is set to `-1` and pagination is effectively + * disabled. By setting this to any number greater than 0, pagination + * will be applied to the mixers targets, effectively activating the + * extension. + * + * @example Example: Activating the pagination extension by defining a valid limit + * + * var mixer = mixitup(containerEl, { + * pagination: { + * limit: 8 + * } + * }); + * + * @name limit + * @memberof mixitup.Config.pagination + * @instance + * @type {number} + * @default -1 + */ + + this.limit = -1; + + /** + * A number dictating the maximum number of individual pager controls to render before + * truncating the list (e.g. adding an ellipses between non-consecutive pagers). + * + * The minimum value permitted for this option is 5, which ensures + * there will always be at least a first, last, and two padding pagers, in addition + * to the pager representing the currently active page. + * + * @name maxPagers + * @memberof mixitup.Config.pagination + * @instance + * @type {number} + * @default 5 + */ + + this.maxPagers = 5; + + h.seal(this); + }; + + /** + * A group of optional render functions for creating and updating elements. + * + * @constructor + * @memberof mixitup.Config + * @name render + * @namespace + * @public + * @since 3.0.0 + */ + + mixitup.ConfigRender.registerAction('afterConstruct', 'pagination', function() { + /** + * A function returning an HTML string representing a single pager control element. + * + * By default, MixItUp will render pager controls using its own internal renderer + * and templates (see `templates.pager`), but you may override this functionality by + * providing your own render function here instead. All pager elements must have a + * data-page element indicating the action of the control. + * + * The function receives an object containing all neccessary information + * about the pager as its first parameter. + * + * @name pager + * @memberof mixitup.Config.render + * @instance + * @type {function} + * @default 'null' + */ + + this.pager = null; + + /** + * A function returning an HTML string forming the contents of the "page stats" element. + * + * By default, MixItUp will render page stats using its own internal renderer + * and templates (see `templates.pageStats`), but you may override this functionality by + * providing your own render function here instead. + * + * The function receives an object containing all neccessary information + * about the current page and total pages as its first parameter. + * + * @name pageStats + * @memberof mixitup.Config.render + * @instance + * @type {function} + * @default 'null' + */ + + this.pageStats = null; + }); + + /** + * A group of properties defining the selectors used to query elements within a mixitup container. + * + * @constructor + * @memberof mixitup.Config + * @name selectors + * @namespace + * @public + * @since 2.0.0 + */ + + mixitup.ConfigSelectors.registerAction('afterConstruct', 'pagination', function() { + /** + * A selector string used to query the page list element. + * + * Depending on the value of `controls.scope`, MixItUp will either query the + * entire document for the page list element, or just the container. + * + * @name pageList + * @memberof mixitup.Config.selectors + * @instance + * @type {string} + * @default '.mixitup-page-list' + */ + + this.pageList = '.mixitup-page-list'; + + /** + * A selector string used to query the page stats element. + * + * Depending on the value of `controls.scope`, MixItUp will either query the + * entire document for the page stats element, or just the container. + * + * @name pageStats + * @memberof mixitup.Config.selectors + * @instance + * @type {string} + * @default '.mixitup-page-stats' + */ + + this.pageStats = '.mixitup-page-stats'; + }); + + /** + * A group of template strings used to render pager controls and page stats elements. + * + * @constructor + * @memberof mixitup.Config + * @name templates + * @namespace + * @public + * @since 3.0.0 + */ + + mixitup.ConfigTemplates.registerAction('afterConstruct', 'pagination', function() { + /** + * @name pager + * @memberof mixitup.Config.templates + * @instance + * @type {string} + * @default '' + */ + + this.pager = ''; + + /** + * @name pagerPrev + * @memberof mixitup.Config.templates + * @instance + * @type {string} + * @default '' + */ + + this.pagerPrev = ''; + + /** + * @name pagerNext + * @memberof mixitup.Config.templates + * @instance + * @type {string} + * @default '' + */ + + this.pagerNext = ''; + + /** + * @name pagerTruncationMarker + * @memberof mixitup.Config.templates + * @instance + * @type {string} + * @default '' + */ + + this.pagerTruncationMarker = ''; + + /** + * @name pageStats + * @memberof mixitup.Config.templates + * @instance + * @type {string} + * @default '${startPageAt} to ${endPageAt} of ${totalTargets}' + */ + + this.pageStats = '${startPageAt} to ${endPageAt} of ${totalTargets}'; + + /** + * @name pageStatsSingle + * @memberof mixitup.Config.templates + * @instance + * @type {string} + * @default '${startPageAt} of ${totalTargets}' + */ + + this.pageStatsSingle = '${startPageAt} of ${totalTargets}'; + + /** + * @name pageStatsFail + * @memberof mixitup.Config.templates + * @instance + * @type {string} + * @default 'None found' + */ + + this.pageStatsFail = 'None found'; + }); + + /** + * The MixItUp configuration object is extended with the following properties + * relating to the Pagination extension. + * + * For the full list of configuration options, please refer to the MixItUp + * core documentation. + * + * @constructor + * @memberof mixitup + * @name Config + * @namespace + * @public + * @since 2.0.0 + */ + + mixitup.Config.registerAction('beforeConstruct', 'pagination', function() { + this.pagination = new mixitup.ConfigPagination(); + }); + + mixitup.ModelPager = function() { + this.pageNumber = -1; + this.classNames = ''; + this.classList = []; + this.isDisabled = false; + this.isPrev = false; + this.isNext = false; + this.isPageLink = false; + this.isTruncationMarker = false; + + h.seal(this); + }; + + mixitup.ModelPageStats = function() { + this.startPageAt = -1; + this.endPageAt = -1; + this.totalTargets = -1; + + h.seal(this); + }; + + mixitup.UiClassNames.registerAction('afterConstruct', 'pagination', function() { + this.first = ''; + this.last = ''; + this.prev = ''; + this.next = ''; + this.first = ''; + this.last = ''; + this.truncated = ''; + this.truncationMarker = ''; + }); + + mixitup.controlDefinitions.push(new mixitup.ControlDefinition('pager', '[data-page]', true, 'pageListEls')); + + /** + * @param {mixitup.MultimixCommand[]} commands + * @param {ClickEvent} e + * @return {object|null} + */ + + mixitup.Control.registerFilter('commandsHandleClick', 'pagination', function(commands, e) { + var self = this, + command = {}, + page = '', + pageNumber = -1, + mixer = null, + button = null, + i = -1; + + if (!self.selector || self.selector !== '[data-page]') { + // Static control or non-pager live control + + return commands; + } + + button = h.closestParent(e.target, self.selector, true, self.bound[0].dom.document); + + for (i = 0; mixer = self.bound[i]; i++) { + command = commands[i]; + + if (!mixer.config.pagination || mixer.config.pagination.limit < 0 || mixer.config.pagination.limit === Infinity) { + // Pagination is disabled for this instance. Do not handle. + + commands[i] = null; + + continue; + } + + if (!button || h.hasClass(button, mixer.classNamesPager.active) || h.hasClass(button, mixer.classNamesPager.disabled)) { + // No button was clicked or button is already active. Do not handle. + + commands[i] = null; + + continue; + } + + page = button.getAttribute('data-page'); + + if (page === 'prev') { + command.paginate = 'prev'; + } else if (page === 'next') { + command.paginate = 'next'; + } else if (pageNumber) { + command.paginate = parseInt(page); + } + + if (mixer.lastClicked) { + mixer.lastClicked = button; + } + } + + return commands; + }); + + mixitup.CommandMultimix.registerAction('afterConstruct', 'pagination', function() { + this.paginate = null; + }); + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.CommandPaginate = function() { + this.page = -1; + this.limit = -1; + this.action = ''; // enum: ['prev', 'next'] + this.anchor = null; + + h.seal(this); + }; + + mixitup.Events.registerAction('afterConstruct', 'pagination', function() { + /** + * A custom event triggered whenever a pagination operation starts. + * + * @name paginateStart + * @memberof mixitup.Events + * @static + * @type {CustomEvent} + */ + + this.paginateStart = null; + + /** + * A custom event triggered whenever a pagination operation ends. + * + * @name paginateEnd + * @memberof mixitup.Events + * @static + * @type {CustomEvent} + */ + + this.paginateEnd = null; + }); + + mixitup.events = new mixitup.Events(); + + mixitup.Operation.registerAction('afterConstruct', 'pagination', function() { + this.startPagination = null; + this.newPagination = null; + this.startTotalPages = -1; + this.newTotalPages = -1; + }); + + /** + * `mixitup.State` objects expose various pieces of data detailing the state of + * a MixItUp instance. They are provided at the start and end of any operation via + * callbacks and events, with the most recent state stored between operations + * for retrieval at any time via the API. + * + * @constructor + * @namespace + * @name State + * @memberof mixitup + * @public + * @since 3.0.0 + */ + + mixitup.State.registerAction('afterConstruct', 'pagination', function() { + + /** + * The currently active pagination command as set by a control click or API call. + * + * @name activePagination + * @memberof mixitup.State + * @instance + * @type {mixitup.CommandPagination} + * @default null + */ + + this.activePagination = null; + + /** + * The total number of pages produced as a combination of the current page + * limit and active filter. + * + * @name totalPages + * @memberof mixitup.State + * @instance + * @type {number} + * @default -1 + */ + + this.totalPages = -1; + }); + + mixitup.MixerDom.registerAction('afterConstruct', 'pagination', function() { + this.pageListEls = []; + this.pageStatsEls = []; + }); + + /** + * The mixitup.Mixer class is extended with the following methods relating to + * the Pagination extension. + * + * For the full list of methods, please refer to the MixItUp core documentation. + * + * @constructor + * @namespace + * @name Mixer + * @memberof mixitup + * @public + * @since 3.0.0 + */ + + mixitup.Mixer.registerAction('afterConstruct', 'pagination', function() { + this.classNamesPager = new mixitup.UiClassNames(); + this.classNamesPageList = new mixitup.UiClassNames(); + this.classNamesPageStats = new mixitup.UiClassNames(); + }); + + /** + * @private + * @return {void} + */ + + mixitup.Mixer.registerAction('afterAttach', 'pagination', function() { + var self = this, + el = null, + i = -1; + + if (self.config.pagination.limit < 0) return; + + // Map pagination ui classNames + + // jscs:disable + self.classNamesPager.base = h.getClassname(self.config.classNames, 'pager'); + self.classNamesPager.active = h.getClassname(self.config.classNames, 'pager', self.config.classNames.modifierActive); + self.classNamesPager.disabled = h.getClassname(self.config.classNames, 'pager', self.config.classNames.modifierDisabled); + self.classNamesPager.first = h.getClassname(self.config.classNames, 'pager', self.config.classNames.modifierFirst); + self.classNamesPager.last = h.getClassname(self.config.classNames, 'pager', self.config.classNames.modifierLast); + self.classNamesPager.prev = h.getClassname(self.config.classNames, 'pager', self.config.classNames.modifierPrev); + self.classNamesPager.next = h.getClassname(self.config.classNames, 'pager', self.config.classNames.modifierNext); + self.classNamesPager.truncationMarker = h.getClassname(self.config.classNames, 'pager', self.config.classNames.modifierTruncationMarker); + + self.classNamesPageList.base = h.getClassname(self.config.classNames, 'page-list'); + self.classNamesPageList.disabled = h.getClassname(self.config.classNames, 'page-list', self.config.classNames.modifierDisabled); + + self.classNamesPageStats.base = h.getClassname(self.config.classNames, 'page-stats'); + self.classNamesPageStats.disabled = h.getClassname(self.config.classNames, 'page-stats', self.config.classNames.modifierDisabled); + // jscs:enable + + if (self.config.pagination.generatePageList && self.dom.pageListEls.length > 0) { + for (i = 0; (el = self.dom.pageListEls[i]); i++) { + self.renderPageListEl(el, self.lastOperation); + } + } + + if (self.config.pagination.generatePageStats && self.dom.pageStatsEls.length > 0) { + for (i = 0; (el = self.dom.pageStatsEls[i]); i++) { + self.renderPageStatsEl(el, self.lastOperation); + } + } + }); + + mixitup.Mixer.registerAction('afterSanitizeConfig', 'pagination', function() { + var self = this, + onMixStart = self.config.callbacks.onMixStart, + onMixEnd = self.config.callbacks.onMixEnd, + onPaginateStart = self.config.callbacks.onPaginateStart, + onPaginateEnd = self.config.callbacks.onPaginateEnd, + didPaginate = false; + + if (self.config.pagination.limit < 0) return; + + self.classNamesPager = new mixitup.UiClassNames(); + self.classNamesPageList = new mixitup.UiClassNames(); + self.classNamesPageStats = new mixitup.UiClassNames(); + + self.config.callbacks.onMixStart = function(prevState, nextState) { + if ( + prevState.activePagination.limit !== nextState.activePagination.limit || + prevState.activePagination.page !== nextState.activePagination.page + ) { + didPaginate = true; + } + + if (typeof onMixStart === 'function') onMixStart.apply(self.dom.container, arguments); + + if (!didPaginate) return; + + mixitup.events.fire('paginateStart', self.dom.container, { + state: prevState, + futureState: nextState, + instance: self + }, self.dom.document); + + if (typeof onPaginateStart === 'function') onPaginateStart.apply(self.dom.container, arguments); + }; + + self.config.callbacks.onMixEnd = function(state) { + if (typeof onMixEnd === 'function') onMixEnd.apply(self.dom.container, arguments); + + if (!didPaginate) return; + + didPaginate = false; + + mixitup.events.fire('paginateEnd', self.dom.container, { + state: state, + instance: self + }, self.dom.document); + + if (typeof onPaginateEnd === 'function') onPaginateEnd.apply(self.dom.container, arguments); + }; + }); + + /** + * @private + * @param {mixitup.Operation} operation + * @param {mixitup.State} state + * @return {mixitup.Operation} + */ + + mixitup.Mixer.registerFilter('operationGetInitialState', 'pagination', function(operation, state) { + var self = this; + + if (self.config.pagination.limit < 0) return operation; + + operation.newPagination = state.activePagination; + + return operation; + }); + + /** + * @private + * @param {mixitup.State} state + * @return {mixitup.State} + */ + + mixitup.Mixer.registerFilter('stateGetInitialState', 'pagination', function(state) { + var self = this; + + if (self.config.pagination.limit < 0) return state; + + state.activePagination = new mixitup.CommandPaginate(); + + state.activePagination.page = self.config.load.page; + state.activePagination.limit = self.config.pagination.limit; + + return state; + }); + + /** + * @private + * @return {void} + */ + + mixitup.Mixer.registerAction('afterGetFinalMixData', 'pagination', function() { + var self = this; + + if (self.config.pagination.limit < 0) return; + + if (typeof self.config.pagination.maxPagers === 'number') { + // Restrict max pagers to a minimum of 5. There must always + // be a first, last, and one on either side of the active pager. + // e.g. « 1 ... 4 5 6 ... 10 » + + self.config.pagination.maxPagers = Math.max(5, self.config.pagination.maxPagers); + } + }); + + /** + * @private + * @return {void} + */ + + mixitup.Mixer.registerAction('afterCacheDom', 'pagination', function() { + var self = this, + parent = null; + + if (self.config.pagination.limit < 0) return; + + if (!self.config.pagination.generatePageList) return; + + switch (self.config.controls.scope) { + case 'local': + parent = self.dom.container; + + break; + case 'global': + parent = self.dom.document; + + break; + default: + throw new Error(mixitup.messages.ERROR_CONFIG_INVALID_CONTROLS_SCOPE); + } + + self.dom.pageListEls = parent.querySelectorAll(self.config.selectors.pageList); + self.dom.pageStatsEls = parent.querySelectorAll(self.config.selectors.pageStats); + }); + + /** + * @private + * @param {mixitup.State} state + * @param {mixitup.Operation} operation + * @return {mixitup.State} + */ + + mixitup.Mixer.registerFilter('stateBuildState', 'pagination', function(state, operation) { + var self = this; + + if (self.config.pagination.limit < 0) return state; + + // Map pagination-specific properties into state + + state.activePagination = operation.newPagination; + state.totalPages = operation.newTotalPages; + + return state; + }); + + /** + * @private + * @param {mixitup.UserInstruction} instruction + * @return {mixitup.UserInstruction} + */ + + mixitup.Mixer.registerFilter('instructionParseMultimixArgs', 'pagination', function(instruction) { + var self = this; + + if (self.config.pagination.limit < 0) return instruction; + + if (instruction.command.paginate && !(instruction.command.paginate instanceof mixitup.CommandPaginate)) { + instruction.command.paginate = self.parsePaginateArgs([instruction.command.paginate]).command; + } + + return instruction; + }); + + /** + * @private + * @param {mixitup.Operation} operation + * @return {void} + */ + + mixitup.Mixer.registerAction('afterFilterOperation', 'pagination', function(operation) { + var self = this, + startPageAt = -1, + endPageAt = -1, + inPage = [], + notInPage = [], + target = null, + index = -1, + i = -1; + + if (self.config.pagination.limit < 0) return; + + // Calculate the new total pages as a matter of course (i.e. a change in filter) + + // New matching array has already been set at this point + + operation.newTotalPages = operation.newPagination.limit ? + Math.max(Math.ceil(operation.matching.length / operation.newPagination.limit), 1) : + 1; + + if (self.config.pagination.maintainActivePage) { + operation.newPagination.page = (operation.newPagination.page > operation.newTotalPages) ? + operation.newTotalPages : + operation.newPagination.page; + } + + // Keep config in sync with latest limit + + self.config.pagination.limit = operation.newPagination.limit; + + if (operation.newPagination.anchor) { + // Start page at an anchor element + + for (i = 0; target = operation.matching[i]; i++) { + if (target.dom.el === operation.newPagination.anchor) break; + } + + startPageAt = i; + endPageAt = i + operation.newPagination.limit - 1; + } else { + // Start page based on limit and page index + + startPageAt = operation.newPagination.limit * (operation.newPagination.page - 1); + endPageAt = (operation.newPagination.limit * operation.newPagination.page) - 1; + + if (isNaN(startPageAt)) { + startPageAt = 0; + } + } + + if (operation.newPagination.limit < 0) return; + + for (i = 0; target = operation.show[i]; i++) { + // For each target in `show`, include in page, only if within the range + + if (i >= startPageAt && i <= endPageAt) { + inPage.push(target); + } else { + // Else move to `notInPage` + + notInPage.push(target); + } + } + + // override the operation's `show` array with the newly constructed `inPage` array + + operation.show = inPage; + + // For anything not in the page, make sure it is correctly assigned: + + for (i = 0; target = operation.toHide[i]; i++) { + // For example, if a target would normally be included in `toHide`, but is + // now already hidden as not in the page, make sure it is removed from `toHide` + // so it is not included in the operation. + + if (!target.isShown) { + operation.toHide.splice(i, 1); + + target.isShown = false; + + i--; + } + } + + for (i = 0; target = notInPage[i]; i++) { + // For each target not in page, move into `hide` + + operation.hide.push(target); + + if ((index = operation.toShow.indexOf(target)) > -1) { + // Any targets due to be shown will no longer be shown + + operation.toShow.splice(index, 1); + } + + if (target.isShown) { + // If currently shown, move to `toHide` + + operation.toHide.push(target); + } + } + }); + + /** + * @private + * @param {mixitup.Operation} operation + * @param {mixitup.CommandMultimix} command + * @return {mixitup.Operation} + */ + + mixitup.Mixer.registerFilter('operationUnmappedGetOperation', 'pagination', function(operation, command) { + var self = this; + + if (self.config.pagination.limit < 0) return operation; + + operation.startState = self.state; + operation.startPagination = self.state.activePagination; + operation.startTotalPages = self.state.totalPages; + + operation.newPagination = new mixitup.CommandPaginate(); + + operation.newPagination.limit = operation.startPagination.limit; + operation.newPagination.page = operation.startPagination.page; + + if (command.paginate) { + self.parsePaginateCommand(command.paginate, operation); + } else if (command.filter || command.sort) { + h.extend(operation.newPagination, operation.startPagination); + + // Reset to 1, or maintain active: + + if (!self.config.pagination.maintainActivePage) { + operation.newPagination.page = 1; + } else { + operation.newPagination.page = self.state.activePagination.page; + } + } + + return operation; + }); + + /** + * @private + * @param {mixitup.Operation} operation + * @param {object} command + * @param {boolean} [isPreFetch=false] + * @return {mixitup.Operation} + */ + + mixitup.Mixer.registerFilter('operationMappedGetOperation', 'pagination', function(operation, command, isPreFetch) { + var self = this, + el = null, + i = -1; + + if (self.config.pagination.limit < 0) return operation; + + if (isPreFetch) { + // The operation is being pre-fetched, so don't update the pagers or stats yet. + + return operation; + } + + if (self.config.pagination.generatePageList && self.dom.pageListEls.length > 0) { + for (i = 0; (el = self.dom.pageListEls[i]); i++) { + self.renderPageListEl(el, operation); + } + } + + if (self.config.pagination.generatePageStats && self.dom.pageStatsEls.length > 0) { + for (i = 0; (el = self.dom.pageStatsEls[i]); i++) { + self.renderPageStatsEl(el, operation); + } + } + + return operation; + }); + + mixitup.Mixer.extend( + /** @lends mixitup.Mixer */ + { + /** + * @private + * @param {mixitup.CommandPaginate} command + * @param {mixitup.Operation} operation + * @return {void} + */ + + parsePaginateCommand: function(command, operation) { + var self = this; + + // e.g. mixer.paginate({page: 3, limit: 2}); + // e.g. mixer.paginate({action: 'next'}); + // e.g. mixer.paginate({anchor: anchorTarget, limit: 5}); + + if (command.page > -1) { + if (command.page === 0) throw new Error(mixitup.messages.ERROR_PAGINATE_INDEX_RANGE); + + // TODO: replace Infinity with the highest possible page index + + operation.newPagination.page = Math.max(1, Math.min(Infinity, command.page)); + } else if (command.action === 'next') { + operation.newPagination.page = self.getNextPage(); + } else if (command.action === 'prev') { + operation.newPagination.page = self.getPrevPage(); + } else if (command.anchor) { + operation.newPagination.anchor = command.anchor; + } + + if (command.limit > -1) { + operation.newPagination.limit = command.limit; + } + + if (operation.newPagination.limit !== operation.startPagination.limit) { + // A new limit has been sent via the API, calculate total pages + + operation.newTotalPages = operation.newPagination.limit ? + Math.max(Math.ceil(operation.startState.matching.length / operation.newPagination.limit), 1) : + 1; + } + + if (operation.newPagination.limit <= 0 || operation.newPagination.limit === Infinity) { + operation.newPagination.page = 1; + } + }, + + /** + * @private + * @return {number} page + */ + + getNextPage: function() { + var self = this, + page = -1; + + page = self.state.activePagination.page + 1; + + if (page > self.state.totalPages) { + page = self.config.pagination.loop ? 1 : self.state.activePagination.page; + } + + return page; + }, + + /** + * @private + * @return {Number} page + */ + + getPrevPage: function() { + var self = this, + page = -1; + + page = self.state.activePagination.page - 1; + + if (page < 1) { + page = self.config.pagination.loop ? self.state.totalPages : self.state.activePagination.page; + } + + return page; + }, + + /** + * @private + * @param {HTMLElement} pageListEl + * @param {mixitup.Operation} operation + * @return {void} + */ + + renderPageListEl: function(pageListEl, operation) { + var self = this, + activeIndex = -1, + pagerHtml = '', + buttonList = [], + model = null, + renderer = null, + allowedIndices = [], + truncatedBefore = false, + truncatedAfter = false, + disabled = null, + el = null, + html = '', + i = -1; + + if ( + operation.newPagination.limit < 0 || + operation.newPagination.limit === Infinity || + (operation.newTotalPages < 2 && self.config.pagination.hidePageListIfSinglePage) + ) { + // Empty the pager list, and add disabled class + + pageListEl.innerHTML = ''; + + h.addClass(pageListEl, self.classNamesPageList.disabled); + + return; + } + + activeIndex = operation.newPagination.page - 1; + + renderer = typeof (renderer = self.config.render.pager) === 'function' ? renderer : null; + + if (self.config.pagination.maxPagers < Infinity && operation.newTotalPages > self.config.pagination.maxPagers) { + allowedIndices = self.getAllowedIndices(operation); + } + + // Render prev button + + model = new mixitup.ModelPager(); + + model.isPrev = true; + model.classList.push(self.classNamesPager.base, self.classNamesPager.prev); + + // If first and not looping, disable the prev button + + if (operation.newPagination.page === 1 && !self.config.pagination.loop) { + model.classList.push(self.classNamesPager.disabled); + + model.isDisabled = true; + } + + model.classNames = model.classList.join(' '); + + if (renderer) { + pagerHtml = renderer(model); + } else { + pagerHtml = h.template(self.config.templates.pagerPrev)(model); + } + + buttonList.push(pagerHtml); + + // Render per-page pagers + + for (i = 0; i < operation.newTotalPages; i++) { + pagerHtml = self.renderPager(i, operation, allowedIndices); + + if (pagerHtml || (i < activeIndex && truncatedBefore) || i > activeIndex && truncatedAfter) { + if (pagerHtml) { + buttonList.push(pagerHtml); + } + + continue; + } + + // Replace gaps between pagers with a truncation maker, but only once + + model = new mixitup.ModelPager(); + + model.isTruncationMarker = true; + + model.classList.push(self.classNamesPager.base, self.classNamesPager.truncationMarker); + model.classNames = model.classList.join(' '); + + if (renderer) { + pagerHtml = renderer(model); + } else { + pagerHtml = h.template(self.config.templates.pagerTruncationMarker)(model); + } + + buttonList.push(pagerHtml); + + // Prevent multiple truncation markers + + if (i < activeIndex) { + truncatedBefore = true; + } + + if (i > activeIndex) { + truncatedAfter = true; + } + } + + // Render next button + + model = new mixitup.ModelPager(); + + model.isNext = true; + model.classList.push(self.classNamesPager.base, self.classNamesPager.next); + + // If last page and not looping, disable the next button + + if (operation.newPagination.page === operation.newTotalPages && !self.config.pagination.loop) { + model.classList.push(self.classNamesPager.disabled); + } + + model.classNames = model.classList.join(' '); + + if (renderer) { + pagerHtml = renderer(model); + } else { + pagerHtml = h.template(self.config.templates.pagerNext)(model); + } + + buttonList.push(pagerHtml); + + // Replace markup + + html = buttonList.join(' '); + + pageListEl.innerHTML = html; + + // Add disabled attribute to disabled buttons + + disabled = pageListEl.querySelectorAll('.' + self.classNamesPager.disabled); + + for (i = 0; el = disabled[i]; i++) { + if (typeof el.disabled === 'boolean') { + el.disabled = true; + } + } + + if (truncatedBefore || truncatedAfter) { + h.addClass(pageListEl, self.classNamesPageList.truncated); + } else { + h.removeClass(pageListEl, self.classNamesPageList.truncated); + } + + if (operation.newTotalPages > 1) { + h.removeClass(pageListEl, self.classNamesPageList.disabled); + } else { + h.addClass(pageListEl, self.classNamesPageList.disabled); + } + }, + + /** + * An algorithm defining which pagers should be rendered based on their index + * and the current active page, when a `pagination.maxPagers` value is applied. + * + * @private + * @param {mixitup.Operation} operation + * @return {number[]} + */ + + getAllowedIndices: function(operation) { + var self = this, + activeIndex = operation.newPagination.page - 1, + lastIndex = operation.newTotalPages - 1, + indices = [], + paddingRange = -1, + paddingBack = -1, + paddingFront = -1, + paddingRangeStart = -1, + paddingRangeEnd = -1, + paddingRangeOffset = -1, + i = -1; + + // Examples: + + // « 1 2 *3* 4 5 » maxPagers = 5 + // « 1 ... 4 *5* 6 ... 10 » maxPagers = 5 + + // « 1 ... 6 7 *8* 9 10 » maxPagers = 6 + // « 1 ... 3 4 *5* 6 ... 10 » maxPagers = 6 + + // « 1 ... 3 4 *5* 6 7 ... 10 » maxPagers = 7 + // « *1* 2 3 4 5 6 ... 10 » maxPagers = 7 + + // This algorithm ensures that at any time, the active pager + // should be surrounded by as many "padding" pagers as possible to equal the + // value of `pagination.maxPagers`, accounting for the fact the first and last + // pager should also always be rendered. + + // Push in index 0 to represent the first pager + + indices.push(0); + + // Calculate the "padding range" by subtracting 2 from `pagination.maxPagers` + + paddingRange = self.config.pagination.maxPagers - 2; + + // Distribute the padding equally behind and in front of the active pager. + // If the padding range is an even number, we allow an extra pager behind the active pager. + + paddingBack = Math.ceil((paddingRange - 1) / 2); + paddingFront = Math.floor((paddingRange - 1) / 2); + + // Calculate where the range should start and finish based on the active index + + paddingRangeStart = activeIndex - paddingBack; + paddingRangeEnd = activeIndex + paddingFront; + + // Set the offset to 0 + + paddingRangeOffset = 0; + + // If the start of the range has collided with the first pager, positively offset as needed + + if (paddingRangeStart < 1) { + paddingRangeOffset = 1 - paddingRangeStart; + } + + // If the end of the range has collided with the last pager, negatively offset as needed + + if (paddingRangeEnd > lastIndex - 1) { + paddingRangeOffset = (lastIndex - 1) - paddingRangeEnd; + } + + // Calcuate the first index of the range taking into account any offset + + i = paddingRangeStart + paddingRangeOffset; + + // Iteratate through the range, adding the respective indices: + + while (paddingRange) { + indices.push(i); + + i++; + paddingRange--; + } + + indices.push(lastIndex); + + return indices; + }, + + /** + * Renderes individual, per-page pagers. + * + * @private + * @param {number} i + * @param {mixitup.Operation} operation + * @param {number[]} [allowedIndices] + * @return {string} + */ + + renderPager: function(i, operation, allowedIndices) { + var self = this, + renderer = null, + activePage = operation.newPagination.page - 1, + model = new mixitup.ModelPager(), + output = ''; + + if ( + self.config.pagination.maxPagers < Infinity && + allowedIndices.length && + allowedIndices.indexOf(i) < 0 + ) { + // maxPagers is set, and this pager is not in the allowed range + + return ''; + } + + renderer = typeof (renderer = self.config.render.pager) === 'function' ? renderer : null; + + model.isPageLink = true; + + model.classList.push(self.classNamesPager.base); + + if (i === 0) { + model.classList.push(self.classNamesPager.first); + } + + if (i === operation.newTotalPages - 1) { + model.classList.push(self.classNamesPager.last); + } + + if (i === activePage) { + model.classList.push(self.classNamesPager.active); + } + + model.classNames = model.classList.join(' '); + model.pageNumber = i + 1; + + if (renderer) { + output = renderer(model); + } else { + output = h.template(self.config.templates.pager)(model); + } + + return output; + }, + + /** + * @private + * @param {HTMLElement} pageStatsEl + * @param {mixitup.Operation} operation + * @return {void} + */ + + renderPageStatsEl: function(pageStatsEl, operation) { + var self = this, + model = new mixitup.ModelPageStats(), + renderer = null, + output = '', + template = ''; + + if ( + operation.newPagination.limit < 0 || + operation.newPagination.limit === Infinity || + (operation.newTotalPages < 2 && self.config.pagination.hidePageStatsIfSinglePage) + ) { + // Empty the pager list, and add disabled class + + pageStatsEl.innerHTML = ''; + + h.addClass(pageStatsEl, self.classNamesPageStats.disabled); + + return; + } + + renderer = typeof (renderer = self.config.render.pageStats) === 'function' ? renderer : null; + + model.totalTargets = operation.matching.length; + + if (model.totalTargets) { + template = operation.newPagination.limit === 1 ? + self.config.templates.pageStatsSingle : + self.config.templates.pageStats; + } else { + template = self.config.templates.pageStatsFail; + } + + if (model.totalTargets && operation.newPagination.limit > 0) { + model.startPageAt = ((operation.newPagination.page - 1) * operation.newPagination.limit) + 1; + model.endPageAt = Math.min(model.startPageAt + operation.newPagination.limit - 1, model.totalTargets); + } else { + model.startPageAt = model.endPageAt = 0; + } + + if (renderer) { + output = renderer(model); + } else { + output = h.template(template)(model); + } + + pageStatsEl.innerHTML = output; + + if (model.totalTargets) { + h.removeClass(pageStatsEl, self.classNamesPageStats.disabled); + } else { + h.addClass(pageStatsEl, self.classNamesPageStats.disabled); + } + }, + + /** + * @private + * @param {Array<*>} args + * @return {mixitup.UserInstruction} instruction + */ + + parsePaginateArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + arg = null, + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandPaginate(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (arg === null) continue; + + if (typeof arg === 'object' && h.isElement(arg, self.dom.document)) { + instruction.command.anchor = arg; + } else if (arg instanceof mixitup.CommandPaginate || typeof arg === 'object') { + h.extend(instruction.command, arg); + } else if (typeof arg === 'number') { + instruction.command.page = arg; + } else if (typeof arg === 'string' && !isNaN(parseInt(arg))) { + // e.g. "4" + + instruction.command.page = parseInt(arg); + } else if (typeof arg === 'string') { + instruction.command.action = arg; + } else if (typeof arg === 'boolean') { + instruction.animate = arg; + } else if (typeof arg === 'function') { + instruction.callback = arg; + } + } + + h.freeze(instruction); + + // NB: Don't freeze command as may need to be sanitized later by other methods + + return instruction; + }, + + /** + * Changes the current page and/or the current page limit. + * + * @example + * + * .paginate(page [, animate] [, callback]) + * + * @example Example 1: Changing the active page + * + * console.log(mixer.getState().activePagination.page); // 1 + * + * mixer.paginate(2) + * .then(function(state) { + * console.log(mixer.getState().activePagination.page === 2); // true + * }); + * + * @example Example 2: Progressing to the next page + * + * console.log(mixer.getState().activePagination.page); // 1 + * + * mixer.paginate('next') + * .then(function(state) { + * console.log(mixer.getState().activePagination.page === 2); // true + * }); + * + * @example Example 3: Starting a page from an abitrary "anchor" element + * + * var anchorEl = mixer.getState().show[3]; + * + * mixer.paginate(anchorEl) + * .then(function(state) { + * console.log(mixer.getState().activePagination.anchor === anchorEl); // true + * console.log(mixer.getState().show[0] === anchorEl); // true + * }); + * + * @example Example 4: Changing the page limit + * + * var anchorEl = mixer.getState().show[3]; + * + * console.log(mixer.getState().activePagination.limit); // 8 + * + * mixer.paginate({ + * limit: 4 + * }) + * .then(function(state) { + * console.log(mixer.getState().activePagination.limit === 4); // true + * }); + * + * @example Example 5: Changing the active page and page limit + * + * mixer.paginate({ + * limit: 4, + * page: 2 + * }) + * .then(function(state) { + * console.log(mixer.getState().activePagination.page === 2); // true + * console.log(mixer.getState().activePagination.limit === 4); // true + * }); + * + * @public + * @instance + * @param {(number|string|object|HTMLElement)} page + * A page number, string (`'next'`, `'prev'`), HTML element reference, or command object. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + paginate: function() { + var self = this, + instruction = self.parsePaginateArgs(arguments); + + return self.multimix({ + paginate: instruction.command + }, instruction.animate, instruction.callback); + }, + + /** + * A shorthand for `.paginate('next')`. Moves to the next page. + * + * @example + * + * .nextPage() + * + * @example Example: Moving to the next page + * + * console.log(mixer.getState().activePagination.page); // 1 + * + * mixer.nextPage() + * .then(function(state) { + * console.log(mixer.getState().activePagination.page === 2); // true + * }); + * + * @public + * @instance + * @return {Promise.} + * A promise resolving with the current state object. + */ + + nextPage: function() { + var self = this, + instruction = self.parsePaginateArgs(arguments); + + return self.multimix({ + paginate: { + action: 'next' + } + }, instruction.animate, instruction.callback); + }, + + /** + * A shorthand for `.paginate('prev')`. Moves to the previous page. + * + * @example + * + * .prevPage() + * + * @example Example: Moving to the previous page + * + * console.log(mixer.getState().activePagination.page); // 5 + * + * mixer.prevPage() + * .then(function(state) { + * console.log(mixer.getState().activePagination.page === 4); // true + * }); + * + * @public + * @instance + * @return {Promise.} + * A promise resolving with the current state object. + */ + + prevPage: function() { + var self = this, + instruction = self.parsePaginateArgs(arguments); + + return self.multimix({ + paginate: { + action: 'prev' + } + }, instruction.animate, instruction.callback); + } + }); + + mixitup.Facade.registerAction('afterConstruct', 'pagination', function(mixer) { + this.paginate = mixer.paginate.bind(mixer); + this.nextPage = mixer.nextPage.bind(mixer); + this.prevPage = mixer.prevPage.bind(mixer); + }); }; + + mixitupPagination.TYPE = 'mixitup-extension'; + mixitupPagination.NAME = 'mixitup-pagination'; + mixitupPagination.EXTENSION_VERSION = '3.3.0'; + mixitupPagination.REQUIRE_CORE_VERSION = '^3.1.8'; + + if (typeof exports === 'object' && typeof module === 'object') { + module.exports = mixitupPagination; + } else if (typeof define === 'function' && define.amd) { + define(function() { + return mixitupPagination; + }); + } else if (window.mixitup && typeof window.mixitup === 'function') { + mixitupPagination(window.mixitup); + } else { + throw new Error('[MixItUp Pagination] MixItUp core not found'); + }})(window); \ No newline at end of file diff --git a/themes/jamstackthemes/assets/js/libs/mixitup.js b/themes/jamstackthemes/assets/js/libs/mixitup.js new file mode 100755 index 0000000..a9af1f7 --- /dev/null +++ b/themes/jamstackthemes/assets/js/libs/mixitup.js @@ -0,0 +1,10682 @@ +/**! + * MixItUp v3.3.1 + * A high-performance, dependency-free library for animated filtering, sorting and more + * Build 94e0fbf6-cd0b-4987-b3c0-14b59b67b8a0 + * + * @copyright Copyright 2014-2018 KunkaLabs Limited. + * @author KunkaLabs Limited. + * @link https://www.kunkalabs.com/mixitup/ + * + * @license Commercial use requires a commercial license. + * https://www.kunkalabs.com/mixitup/licenses/ + * + * Non-commercial use permitted under same terms as CC BY-NC 3.0 license. + * http://creativecommons.org/licenses/by-nc/3.0/ + */ + +(function(window) { + 'use strict'; + + var mixitup = null, + h = null; + + (function() { + var VENDORS = ['webkit', 'moz', 'o', 'ms'], + canary = window.document.createElement('div'), + i = -1; + + // window.requestAnimationFrame + + for (i = 0; i < VENDORS.length && !window.requestAnimationFrame; i++) { + window.requestAnimationFrame = window[VENDORS[i] + 'RequestAnimationFrame']; + } + + // Element.nextElementSibling + + if (typeof canary.nextElementSibling === 'undefined') { + Object.defineProperty(window.Element.prototype, 'nextElementSibling', { + get: function() { + var el = this.nextSibling; + + while (el) { + if (el.nodeType === 1) { + return el; + } + + el = el.nextSibling; + } + + return null; + } + }); + } + + // Element.matches + + (function(ElementPrototype) { + ElementPrototype.matches = + ElementPrototype.matches || + ElementPrototype.machesSelector || + ElementPrototype.mozMatchesSelector || + ElementPrototype.msMatchesSelector || + ElementPrototype.oMatchesSelector || + ElementPrototype.webkitMatchesSelector || + function (selector) { + return Array.prototype.indexOf.call(this.parentElement.querySelectorAll(selector), this) > -1; + }; + })(window.Element.prototype); + + // Object.keys + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys + + if (!Object.keys) { + Object.keys = (function() { + var hasOwnProperty = Object.prototype.hasOwnProperty, + hasDontEnumBug = false, + dontEnums = [], + dontEnumsLength = -1; + + hasDontEnumBug = !({ + toString: null + }) + .propertyIsEnumerable('toString'); + + dontEnums = [ + 'toString', + 'toLocaleString', + 'valueOf', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor' + ]; + + dontEnumsLength = dontEnums.length; + + return function(obj) { + var result = [], + prop = '', + i = -1; + + if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { + throw new TypeError('Object.keys called on non-object'); + } + + for (prop in obj) { + if (hasOwnProperty.call(obj, prop)) { + result.push(prop); + } + } + + if (hasDontEnumBug) { + for (i = 0; i < dontEnumsLength; i++) { + if (hasOwnProperty.call(obj, dontEnums[i])) { + result.push(dontEnums[i]); + } + } + } + + return result; + }; + }()); + } + + // Array.isArray + // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray + + if (!Array.isArray) { + Array.isArray = function(arg) { + return Object.prototype.toString.call(arg) === '[object Array]'; + }; + } + + // Object.create + // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/create + + if (typeof Object.create !== 'function') { + Object.create = (function(undefined) { + var Temp = function() {}; + + return function (prototype, propertiesObject) { + if (prototype !== Object(prototype) && prototype !== null) { + throw TypeError('Argument must be an object, or null'); + } + + Temp.prototype = prototype || {}; + + var result = new Temp(); + + Temp.prototype = null; + + if (propertiesObject !== undefined) { + Object.defineProperties(result, propertiesObject); + } + + if (prototype === null) { + /* jshint ignore:start */ + result.__proto__ = null; + /* jshint ignore:end */ + } + + return result; + }; + })(); + } + + // String.prototyoe.trim + + if (!String.prototype.trim) { + String.prototype.trim = function() { + return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); + }; + } + + // Array.prototype.indexOf + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf + + if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function(searchElement) { + var n, k, t, len; + + if (this === null) { + throw new TypeError(); + } + + t = Object(this); + + len = t.length >>> 0; + + if (len === 0) { + return -1; + } + + n = 0; + + if (arguments.length > 1) { + n = Number(arguments[1]); + + if (n !== n) { + n = 0; + } else if (n !== 0 && n !== Infinity && n !== -Infinity) { + n = (n > 0 || -1) * Math.floor(Math.abs(n)); + } + } + + if (n >= len) { + return -1; + } + + for (k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); k < len; k++) { + if (k in t && t[k] === searchElement) { + return k; + } + } + + return -1; + }; + } + + // Function.prototype.bind + // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind + + if (!Function.prototype.bind) { + Function.prototype.bind = function(oThis) { + var aArgs, self, FNOP, fBound; + + if (typeof this !== 'function') { + throw new TypeError(); + } + + aArgs = Array.prototype.slice.call(arguments, 1); + + self = this; + + FNOP = function() {}; + + fBound = function() { + return self.apply(this instanceof FNOP ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + if (this.prototype) { + FNOP.prototype = this.prototype; + } + + fBound.prototype = new FNOP(); + + return fBound; + }; + } + + // Element.prototype.dispatchEvent + + if (!window.Element.prototype.dispatchEvent) { + window.Element.prototype.dispatchEvent = function(event) { + try { + return this.fireEvent('on' + event.type, event); + } catch (err) {} + }; + } + })(); + + /** + * The `mixitup()` "factory" function creates and returns individual instances + * of MixItUp, known as "mixers", on which API methods can be called. + * + * When loading MixItUp via a script tag, the factory function is accessed + * via the global variable `mixitup`. When using a module loading + * system (e.g. ES2015, CommonJS, RequireJS), the factory function is + * exported into your module when you require the MixItUp library. + * + * @example + * mixitup(container [,config] [,foreignDoc]) + * + * @example Example 1: Creating a mixer instance with an element reference + * var containerEl = document.querySelector('.container'); + * + * var mixer = mixitup(containerEl); + * + * @example Example 2: Creating a mixer instance with a selector string + * var mixer = mixitup('.container'); + * + * @example Example 3: Passing a configuration object + * var mixer = mixitup(containerEl, { + * animation: { + * effects: 'fade scale(0.5)' + * } + * }); + * + * @example Example 4: Passing an iframe reference + * var mixer = mixitup(containerEl, config, foreignDocument); + * + * @global + * @namespace + * @public + * @kind function + * @since 3.0.0 + * @param {(Element|string)} container + * A DOM element or selector string representing the container(s) on which to instantiate MixItUp. + * @param {object} [config] + * An optional "configuration object" used to customize the behavior of the MixItUp instance. + * @param {object} [foreignDoc] + * An optional reference to a `document`, which can be used to control a MixItUp instance in an iframe. + * @return {mixitup.Mixer} + * A "mixer" object holding the MixItUp instance. + */ + + mixitup = function(container, config, foreignDoc) { + var el = null, + returnCollection = false, + instance = null, + facade = null, + doc = null, + output = null, + instances = [], + id = '', + elements = [], + i = -1; + + doc = foreignDoc || window.document; + + if (returnCollection = arguments[3]) { + // A non-documented 4th paramater enabling control of multiple instances + + returnCollection = typeof returnCollection === 'boolean'; + } + + if (typeof container === 'string') { + elements = doc.querySelectorAll(container); + } else if (container && typeof container === 'object' && h.isElement(container, doc)) { + elements = [container]; + } else if (container && typeof container === 'object' && container.length) { + // Although not documented, the container may also be an array-like list of + // elements such as a NodeList or jQuery collection, is returnCollection is true + + elements = container; + } else { + throw new Error(mixitup.messages.errorFactoryInvalidContainer()); + } + + if (elements.length < 1) { + throw new Error(mixitup.messages.errorFactoryContainerNotFound()); + } + + for (i = 0; el = elements[i]; i++) { + if (i > 0 && !returnCollection) break; + + if (!el.id) { + id = 'MixItUp' + h.randomHex(); + + el.id = id; + } else { + id = el.id; + } + + if (mixitup.instances[id] instanceof mixitup.Mixer) { + instance = mixitup.instances[id]; + + if (!config || (config && config.debug && config.debug.showWarnings !== false)) { + console.warn(mixitup.messages.warningFactoryPreexistingInstance()); + } + } else { + instance = new mixitup.Mixer(); + + instance.attach(el, doc, id, config); + + mixitup.instances[id] = instance; + } + + facade = new mixitup.Facade(instance); + + if (config && config.debug && config.debug.enable) { + instances.push(instance); + } else { + instances.push(facade); + } + } + + if (returnCollection) { + output = new mixitup.Collection(instances); + } else { + // Return the first instance regardless + + output = instances[0]; + } + + return output; + }; + + /** + * The `.use()` static method is used to extend the functionality of mixitup with compatible + * extensions and libraries in an environment with modular scoping e.g. ES2015, CommonJS, or RequireJS. + * + * You need only call the `.use()` function once per project, per extension, as module loaders + * will cache a single reference to MixItUp inclusive of all changes made. + * + * @example + * mixitup.use(extension) + * + * @example Example 1: Extending MixItUp with the Pagination Extension + * + * import mixitup from 'mixitup'; + * import mixitupPagination from 'mixitup-pagination'; + * + * mixitup.use(mixitupPagination); + * + * // All mixers created by the factory function in all modules will now + * // have pagination functionality + * + * var mixer = mixitup('.container'); + * + * @public + * @name use + * @memberof mixitup + * @kind function + * @static + * @since 3.0.0 + * @param {*} extension A reference to the extension or library to be used. + * @return {void} + */ + + mixitup.use = function(extension) { + mixitup.Base.prototype.callActions.call(mixitup, 'beforeUse', arguments); + + // Call the extension's factory function, passing + // the mixitup factory as a paramater + + if (typeof extension === 'function' && extension.TYPE === 'mixitup-extension') { + // Mixitup extension + + if (typeof mixitup.extensions[extension.NAME] === 'undefined') { + extension(mixitup); + + mixitup.extensions[extension.NAME] = extension; + } + } else if (extension.fn && extension.fn.jquery) { + // jQuery + + mixitup.libraries.$ = extension; + } + + mixitup.Base.prototype.callActions.call(mixitup, 'afterUse', arguments); + }; + + mixitup.instances = {}; + mixitup.extensions = {}; + mixitup.libraries = {}; + + /** + * @private + */ + + h = { + + /** + * @private + * @param {HTMLElement} el + * @param {string} cls + * @return {boolean} + */ + + hasClass: function(el, cls) { + return !!el.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')); + }, + + /** + * @private + * @param {HTMLElement} el + * @param {string} cls + * @return {void} + */ + + addClass: function(el, cls) { + if (!this.hasClass(el, cls)) el.className += el.className ? ' ' + cls : cls; + }, + + /** + * @private + * @param {HTMLElement} el + * @param {string} cls + * @return {void} + */ + + removeClass: function(el, cls) { + if (this.hasClass(el, cls)) { + var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)'); + + el.className = el.className.replace(reg, ' ').trim(); + } + }, + + /** + * Merges the properties of the source object onto the + * target object. Alters the target object. + * + * @private + * @param {object} destination + * @param {object} source + * @param {boolean} [deep=false] + * @param {boolean} [handleErrors=false] + * @return {void} + */ + + extend: function(destination, source, deep, handleErrors) { + var sourceKeys = [], + key = '', + i = -1; + + deep = deep || false; + handleErrors = handleErrors || false; + + try { + if (Array.isArray(source)) { + for (i = 0; i < source.length; i++) { + sourceKeys.push(i); + } + } else if (source) { + sourceKeys = Object.keys(source); + } + + for (i = 0; i < sourceKeys.length; i++) { + key = sourceKeys[i]; + + if (!deep || typeof source[key] !== 'object' || this.isElement(source[key])) { + // All non-object properties, or all properties if shallow extend + + destination[key] = source[key]; + } else if (Array.isArray(source[key])) { + // Arrays + + if (!destination[key]) { + destination[key] = []; + } + + this.extend(destination[key], source[key], deep, handleErrors); + } else { + // Objects + + if (!destination[key]) { + destination[key] = {}; + } + + this.extend(destination[key], source[key], deep, handleErrors); + } + } + } catch(err) { + if (handleErrors) { + this.handleExtendError(err, destination); + } else { + throw err; + } + } + + return destination; + }, + + /** + * @private + * @param {Error} err + * @param {object} destination + * @return {void} + */ + + handleExtendError: function(err, destination) { + var re = /property "?(\w*)"?[,:] object/i, + matches = null, + erroneous = '', + message = '', + suggestion = '', + probableMatch = '', + key = '', + mostMatchingChars = -1, + i = -1; + + if (err instanceof TypeError && (matches = re.exec(err.message))) { + erroneous = matches[1]; + + for (key in destination) { + i = 0; + + while (i < erroneous.length && erroneous.charAt(i) === key.charAt(i)) { + i++; + } + + if (i > mostMatchingChars) { + mostMatchingChars = i; + probableMatch = key; + } + } + + if (mostMatchingChars > 1) { + suggestion = mixitup.messages.errorConfigInvalidPropertySuggestion({ + probableMatch: probableMatch + }); + } + + message = mixitup.messages.errorConfigInvalidProperty({ + erroneous: erroneous, + suggestion: suggestion + }); + + throw new TypeError(message); + } + + throw err; + }, + + /** + * @private + * @param {string} str + * @return {function} + */ + + template: function(str) { + var re = /\${([\w]*)}/g, + dynamics = {}, + matches = null; + + while ((matches = re.exec(str))) { + dynamics[matches[1]] = new RegExp('\\${' + matches[1] + '}', 'g'); + } + + return function(data) { + var key = '', + output = str; + + data = data || {}; + + for (key in dynamics) { + output = output.replace(dynamics[key], typeof data[key] !== 'undefined' ? data[key] : ''); + } + + return output; + }; + }, + + /** + * @private + * @param {HTMLElement} el + * @param {string} type + * @param {function} fn + * @param {boolean} useCapture + * @return {void} + */ + + on: function(el, type, fn, useCapture) { + if (!el) return; + + if (el.addEventListener) { + el.addEventListener(type, fn, useCapture); + } else if (el.attachEvent) { + el['e' + type + fn] = fn; + + el[type + fn] = function() { + el['e' + type + fn](window.event); + }; + + el.attachEvent('on' + type, el[type + fn]); + } + }, + + /** + * @private + * @param {HTMLElement} el + * @param {string} type + * @param {function} fn + * @return {void} + */ + + off: function(el, type, fn) { + if (!el) return; + + if (el.removeEventListener) { + el.removeEventListener(type, fn, false); + } else if (el.detachEvent) { + el.detachEvent('on' + type, el[type + fn]); + el[type + fn] = null; + } + }, + + /** + * @private + * @param {string} eventType + * @param {object} detail + * @param {Document} [doc] + * @return {CustomEvent} + */ + + getCustomEvent: function(eventType, detail, doc) { + var event = null; + + doc = doc || window.document; + + if (typeof window.CustomEvent === 'function') { + event = new window.CustomEvent(eventType, { + detail: detail, + bubbles: true, + cancelable: true + }); + } else if (typeof doc.createEvent === 'function') { + event = doc.createEvent('CustomEvent'); + event.initCustomEvent(eventType, true, true, detail); + } else { + event = doc.createEventObject(), + event.type = eventType; + + event.returnValue = false; + event.cancelBubble = false; + event.detail = detail; + } + + return event; + }, + + /** + * @private + * @param {Event} e + * @return {Event} + */ + + getOriginalEvent: function(e) { + if (e.touches && e.touches.length) { + return e.touches[0]; + } else if (e.changedTouches && e.changedTouches.length) { + return e.changedTouches[0]; + } else { + return e; + } + }, + + /** + * @private + * @param {HTMLElement} el + * @param {string} selector + * @return {Number} + */ + + index: function(el, selector) { + var i = 0; + + while ((el = el.previousElementSibling) !== null) { + if (!selector || el.matches(selector)) { + ++i; + } + } + + return i; + }, + + /** + * Converts a dash or snake-case string to camel case. + * + * @private + * @param {string} str + * @param {boolean} [isPascal] + * @return {string} + */ + + camelCase: function(str) { + return str.toLowerCase().replace(/([_-][a-z])/g, function($1) { + return $1.toUpperCase().replace(/[_-]/, ''); + }); + }, + + /** + * Converts a dash or snake-case string to pascal case. + * + * @private + * @param {string} str + * @param {boolean} [isPascal] + * @return {string} + */ + + pascalCase: function(str) { + return (str = this.camelCase(str)).charAt(0).toUpperCase() + str.slice(1); + }, + + /** + * Converts a camel or pascal-case string to dash case. + * + * @private + * @param {string} str + * @return {string} + */ + + dashCase: function(str) { + return str.replace(/([A-Z])/g, '-$1').replace(/^-/, '').toLowerCase(); + }, + + /** + * @private + * @param {HTMLElement} el + * @param {HTMLHtmlElement} [doc] + * @return {boolean} + */ + + isElement: function(el, doc) { + doc = doc || window.document; + + if ( + window.HTMLElement && + el instanceof window.HTMLElement + ) { + return true; + } else if ( + doc.defaultView && + doc.defaultView.HTMLElement && + el instanceof doc.defaultView.HTMLElement + ) { + return true; + } else { + return ( + el !== null && + el.nodeType === 1 && + typeof el.nodeName === 'string' + ); + } + }, + + /** + * @private + * @param {string} htmlString + * @param {HTMLHtmlElement} [doc] + * @return {DocumentFragment} + */ + + createElement: function(htmlString, doc) { + var frag = null, + temp = null; + + doc = doc || window.document; + + frag = doc.createDocumentFragment(); + temp = doc.createElement('div'); + + temp.innerHTML = htmlString.trim(); + + while (temp.firstChild) { + frag.appendChild(temp.firstChild); + } + + return frag; + }, + + /** + * @private + * @param {Node} node + * @return {void} + */ + + removeWhitespace: function(node) { + var deleting; + + while (node && node.nodeName === '#text') { + deleting = node; + + node = node.previousSibling; + + deleting.parentElement && deleting.parentElement.removeChild(deleting); + } + }, + + /** + * @private + * @param {Array<*>} a + * @param {Array<*>} b + * @return {boolean} + */ + + isEqualArray: function(a, b) { + var i = a.length; + + if (i !== b.length) return false; + + while (i--) { + if (a[i] !== b[i]) return false; + } + + return true; + }, + + /** + * @private + * @param {object} a + * @param {object} b + * @return {boolean} + */ + + deepEquals: function(a, b) { + var key; + + if (typeof a === 'object' && a && typeof b === 'object' && b) { + if (Object.keys(a).length !== Object.keys(b).length) return false; + + for (key in a) { + if (!b.hasOwnProperty(key) || !this.deepEquals(a[key], b[key])) return false; + } + } else if (a !== b) { + return false; + } + + return true; + }, + + /** + * @private + * @param {Array<*>} oldArray + * @return {Array<*>} + */ + + arrayShuffle: function(oldArray) { + var newArray = oldArray.slice(), + len = newArray.length, + i = len, + p = -1, + t = []; + + while (i--) { + p = ~~(Math.random() * len); + t = newArray[i]; + + newArray[i] = newArray[p]; + newArray[p] = t; + } + + return newArray; + }, + + /** + * @private + * @param {object} list + */ + + arrayFromList: function(list) { + var output, i; + + try { + return Array.prototype.slice.call(list); + } catch(err) { + output = []; + + for (i = 0; i < list.length; i++) { + output.push(list[i]); + } + + return output; + } + }, + + /** + * @private + * @param {function} func + * @param {Number} wait + * @param {boolean} immediate + * @return {function} + */ + + debounce: function(func, wait, immediate) { + var timeout; + + return function() { + var self = this, + args = arguments, + callNow = immediate && !timeout, + later = null; + + later = function() { + timeout = null; + + if (!immediate) { + func.apply(self, args); + } + }; + + clearTimeout(timeout); + + timeout = setTimeout(later, wait); + + if (callNow) func.apply(self, args); + }; + }, + + /** + * @private + * @param {HTMLElement} element + * @return {object} + */ + + position: function(element) { + var xPosition = 0, + yPosition = 0, + offsetParent = element; + + while (element) { + xPosition -= element.scrollLeft; + yPosition -= element.scrollTop; + + if (element === offsetParent) { + xPosition += element.offsetLeft; + yPosition += element.offsetTop; + + offsetParent = element.offsetParent; + } + + element = element.parentElement; + } + + return { + x: xPosition, + y: yPosition + }; + }, + + /** + * @private + * @param {object} node1 + * @param {object} node2 + * @return {Number} + */ + + getHypotenuse: function(node1, node2) { + var distanceX = node1.x - node2.x, + distanceY = node1.y - node2.y; + + distanceX = distanceX < 0 ? distanceX * -1 : distanceX, + distanceY = distanceY < 0 ? distanceY * -1 : distanceY; + + return Math.sqrt(Math.pow(distanceX, 2) + Math.pow(distanceY, 2)); + }, + + /** + * Calcuates the area of intersection between two rectangles and expresses it as + * a ratio in comparison to the area of the first rectangle. + * + * @private + * @param {Rect} box1 + * @param {Rect} box2 + * @return {number} + */ + + getIntersectionRatio: function(box1, box2) { + var controlArea = box1.width * box1.height, + intersectionX = -1, + intersectionY = -1, + intersectionArea = -1, + ratio = -1; + + intersectionX = + Math.max(0, Math.min(box1.left + box1.width, box2.left + box2.width) - Math.max(box1.left, box2.left)); + + intersectionY = + Math.max(0, Math.min(box1.top + box1.height, box2.top + box2.height) - Math.max(box1.top, box2.top)); + + intersectionArea = intersectionY * intersectionX; + + ratio = intersectionArea / controlArea; + + return ratio; + }, + + /** + * @private + * @param {object} el + * @param {string} selector + * @param {boolean} [includeSelf] + * @param {HTMLHtmlElement} [doc] + * @return {Element|null} + */ + + closestParent: function(el, selector, includeSelf, doc) { + var parent = el.parentNode; + + doc = doc || window.document; + + if (includeSelf && el.matches(selector)) { + return el; + } + + while (parent && parent != doc.body) { + if (parent.matches && parent.matches(selector)) { + return parent; + } else if (parent.parentNode) { + parent = parent.parentNode; + } else { + return null; + } + } + + return null; + }, + + /** + * @private + * @param {HTMLElement} el + * @param {string} selector + * @param {HTMLHtmlElement} [doc] + * @return {NodeList} + */ + + children: function(el, selector, doc) { + var children = [], + tempId = ''; + + doc = doc || window.doc; + + if (el) { + if (!el.id) { + tempId = 'Temp' + this.randomHexKey(); + + el.id = tempId; + } + + children = doc.querySelectorAll('#' + el.id + ' > ' + selector); + + if (tempId) { + el.removeAttribute('id'); + } + } + + return children; + }, + + /** + * Creates a clone of a provided array, with any empty strings removed. + * + * @private + * @param {Array<*>} originalArray + * @return {Array<*>} + */ + + clean: function(originalArray) { + var cleanArray = [], + i = -1; + + for (i = 0; i < originalArray.length; i++) { + if (originalArray[i] !== '') { + cleanArray.push(originalArray[i]); + } + } + + return cleanArray; + }, + + /** + * Abstracts an ES6 promise into a q-like deferred interface for storage and deferred resolution. + * + * @private + * @param {object} libraries + * @return {h.Deferred} + */ + + defer: function(libraries) { + var deferred = null, + promiseWrapper = null, + $ = null; + + promiseWrapper = new this.Deferred(); + + if (mixitup.features.has.promises) { + // ES6 native promise or polyfill + + promiseWrapper.promise = new Promise(function(resolve, reject) { + promiseWrapper.resolve = resolve; + promiseWrapper.reject = reject; + }); + } else if (($ = (window.jQuery || libraries.$)) && typeof $.Deferred === 'function') { + // jQuery + + deferred = $.Deferred(); + + promiseWrapper.promise = deferred.promise(); + promiseWrapper.resolve = deferred.resolve; + promiseWrapper.reject = deferred.reject; + } else if (window.console) { + // No implementation + + console.warn(mixitup.messages.warningNoPromiseImplementation()); + } + + return promiseWrapper; + }, + + /** + * @private + * @param {Array} tasks + * @param {object} libraries + * @return {Promise} + */ + + all: function(tasks, libraries) { + var $ = null; + + if (mixitup.features.has.promises) { + return Promise.all(tasks); + } else if (($ = (window.jQuery || libraries.$)) && typeof $.when === 'function') { + return $.when.apply($, tasks) + .done(function() { + // jQuery when returns spread arguments rather than an array or resolutions + + return arguments; + }); + } + + // No implementation + + if (window.console) { + console.warn(mixitup.messages.warningNoPromiseImplementation()); + } + + return []; + }, + + /** + * @private + * @param {HTMLElement} el + * @param {string} property + * @param {Array} vendors + * @return {string} + */ + + getPrefix: function(el, property, vendors) { + var i = -1, + prefix = ''; + + if (h.dashCase(property) in el.style) return ''; + + for (i = 0; prefix = vendors[i]; i++) { + if (prefix + property in el.style) { + return prefix.toLowerCase(); + } + } + + return 'unsupported'; + }, + + /** + * @private + * @return {string} + */ + + randomHex: function() { + return ('00000' + (Math.random() * 16777216 << 0).toString(16)).substr(-6).toUpperCase(); + }, + + /** + * @private + * @param {HTMLDocument} [doc] + * @return {object} + */ + + getDocumentState: function(doc) { + doc = typeof doc.body === 'object' ? doc : window.document; + + return { + scrollTop: window.pageYOffset, + scrollLeft: window.pageXOffset, + docHeight: doc.documentElement.scrollHeight, + docWidth: doc.documentElement.scrollWidth, + viewportHeight: doc.documentElement.clientHeight, + viewportWidth: doc.documentElement.clientWidth + }; + }, + + /** + * @private + * @param {object} obj + * @param {function} fn + * @return {function} + */ + + bind: function(obj, fn) { + return function() { + return fn.apply(obj, arguments); + }; + }, + + /** + * @private + * @param {HTMLElement} el + * @return {boolean} + */ + + isVisible: function(el) { + var styles = null; + + if (el.offsetParent) return true; + + styles = window.getComputedStyle(el); + + if ( + styles.position === 'fixed' && + styles.visibility !== 'hidden' && + styles.opacity !== '0' + ) { + // Fixed elements report no offsetParent, + // but may still be invisible + + return true; + } + + return false; + }, + + /** + * @private + * @param {object} obj + */ + + seal: function(obj) { + if (typeof Object.seal === 'function') { + Object.seal(obj); + } + }, + + /** + * @private + * @param {object} obj + */ + + freeze: function(obj) { + if (typeof Object.freeze === 'function') { + Object.freeze(obj); + } + }, + + /** + * @private + * @param {string} control + * @param {string} specimen + * @return {boolean} + */ + + compareVersions: function(control, specimen) { + var controlParts = control.split('.'), + specimenParts = specimen.split('.'), + controlPart = -1, + specimenPart = -1, + i = -1; + + for (i = 0; i < controlParts.length; i++) { + controlPart = parseInt(controlParts[i].replace(/[^\d.]/g, '')); + specimenPart = parseInt(specimenParts[i].replace(/[^\d.]/g, '') || 0); + + if (specimenPart < controlPart) { + return false; + } else if (specimenPart > controlPart) { + return true; + } + } + + return true; + }, + + /** + * @private + * @constructor + */ + + Deferred: function() { + this.promise = null; + this.resolve = null; + this.reject = null; + this.id = h.randomHex(); + }, + + /** + * @private + * @param {object} obj + * @return {boolean} + */ + + isEmptyObject: function(obj) { + var key = ''; + + if (typeof Object.keys === 'function') { + return Object.keys(obj).length === 0; + } + + for (key in obj) { + if (obj.hasOwnProperty(key)) { + return false; + } + } + + return true; + }, + + /** + * @param {mixitup.Config.ClassNames} classNames + * @param {string} elementName + * @param {string} [modifier] + * @return {string} + */ + + getClassname: function(classNames, elementName, modifier) { + var classname = ''; + + classname += classNames.block; + + if (classname.length) { + classname += classNames.delineatorElement; + } + + classname += classNames['element' + this.pascalCase(elementName)]; + + if (!modifier) return classname; + + if (classname.length) { + classname += classNames.delineatorModifier; + } + + classname += modifier; + + return classname; + }, + + /** + * Returns the value of a property on a given object via its string key. + * + * @param {object} obj + * @param {string} stringKey + * @return {*} value + */ + + getProperty: function(obj, stringKey) { + var parts = stringKey.split('.'), + returnCurrent = null, + current = '', + i = 0; + + if (!stringKey) { + return obj; + } + + returnCurrent = function(obj) { + if (!obj) { + return null; + } else { + return obj[current]; + } + }; + + while (i < parts.length) { + current = parts[i]; + + obj = returnCurrent(obj); + + i++; + } + + if (typeof obj !== 'undefined') { + return obj; + } else { + return null; + } + } + }; + + mixitup.h = h; + + /** + * The Base class adds instance methods to all other extensible MixItUp classes, + * enabling the calling of any registered hooks. + * + * @constructor + * @namespace + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.Base = function() {}; + + mixitup.Base.prototype = { + constructor: mixitup.Base, + + /** + * Calls any registered hooks for the provided action. + * + * @memberof mixitup.Base + * @private + * @instance + * @since 2.0.0 + * @param {string} actionName + * @param {Array<*>} args + * @return {void} + */ + + callActions: function(actionName, args) { + var self = this, + hooks = self.constructor.actions[actionName], + extensionName = ''; + + if (!hooks || h.isEmptyObject(hooks)) return; + + for (extensionName in hooks) { + hooks[extensionName].apply(self, args); + } + }, + + /** + * Calls any registered hooks for the provided filter. + * + * @memberof mixitup.Base + * @private + * @instance + * @since 2.0.0 + * @param {string} filterName + * @param {*} input + * @param {Array<*>} args + * @return {*} + */ + + callFilters: function(filterName, input, args) { + var self = this, + hooks = self.constructor.filters[filterName], + output = input, + extensionName = ''; + + if (!hooks || h.isEmptyObject(hooks)) return output; + + args = args || []; + + for (extensionName in hooks) { + args = h.arrayFromList(args); + + args.unshift(output); + + output = hooks[extensionName].apply(self, args); + } + + return output; + } + }; + + /** + * The BaseStatic class holds a set of static methods which are then added to all other + * extensible MixItUp classes as a means of integrating extensions via the addition of new + * methods and/or actions and hooks. + * + * @constructor + * @namespace + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.BaseStatic = function() { + this.actions = {}; + this.filters = {}; + + /** + * Performs a shallow extend on the class's prototype, adding one or more new members to + * the class in a single operation. + * + * @memberof mixitup.BaseStatic + * @public + * @static + * @since 2.1.0 + * @param {object} extension + * @return {void} + */ + + this.extend = function(extension) { + h.extend(this.prototype, extension); + }; + + /** + * Registers a function to be called on the action hook of the provided name. + * + * @memberof mixitup.BaseStatic + * @public + * @static + * @since 2.1.0 + * @param {string} hookName + * @param {string} extensionName + * @param {function} func + * @return {void} + */ + + this.registerAction = function(hookName, extensionName, func) { + (this.actions[hookName] = this.actions[hookName] || {})[extensionName] = func; + }; + + /** + * Registers a function to be called on the filter of the provided name. + * + * @memberof mixitup.BaseStatic + * @public + * @static + * @since 2.1.0 + * @param {string} hookName + * @param {string} extensionName + * @param {function} func + * @return {void} + */ + + this.registerFilter = function(hookName, extensionName, func) { + (this.filters[hookName] = this.filters[hookName] || {})[extensionName] = func; + }; + }; + + /** + * The `mixitup.Features` class performs all feature and CSS prefix detection + * neccessary for MixItUp to function correctly, as well as storing various + * string and array constants. All feature decection is on evaluation of the + * library and stored in a singleton instance for use by other internal classes. + * + * @constructor + * @namespace + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.Features = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.boxSizingPrefix = ''; + this.transformPrefix = ''; + this.transitionPrefix = ''; + + this.boxSizingPrefix = ''; + this.transformProp = ''; + this.transformRule = ''; + this.transitionProp = ''; + this.perspectiveProp = ''; + this.perspectiveOriginProp = ''; + + this.has = new mixitup.Has(); + + this.canary = null; + + this.BOX_SIZING_PROP = 'boxSizing'; + this.TRANSITION_PROP = 'transition'; + this.TRANSFORM_PROP = 'transform'; + this.PERSPECTIVE_PROP = 'perspective'; + this.PERSPECTIVE_ORIGIN_PROP = 'perspectiveOrigin'; + this.VENDORS = ['Webkit', 'moz', 'O', 'ms']; + + this.TWEENABLE = [ + 'opacity', + 'width', 'height', + 'marginRight', 'marginBottom', + 'x', 'y', + 'scale', + 'translateX', 'translateY', 'translateZ', + 'rotateX', 'rotateY', 'rotateZ' + ]; + + this.callActions('afterConstruct'); + }; + + mixitup.BaseStatic.call(mixitup.Features); + + mixitup.Features.prototype = Object.create(mixitup.Base.prototype); + + h.extend(mixitup.Features.prototype, + /** @lends mixitup.Features */ + { + constructor: mixitup.Features, + + /** + * @private + * @return {void} + */ + + init: function() { + var self = this; + + self.callActions('beforeInit', arguments); + + self.canary = document.createElement('div'); + + self.setPrefixes(); + self.runTests(); + + self.callActions('beforeInit', arguments); + }, + + /** + * @private + * @return {void} + */ + + runTests: function() { + var self = this; + + self.callActions('beforeRunTests', arguments); + + self.has.promises = typeof window.Promise === 'function'; + self.has.transitions = self.transitionPrefix !== 'unsupported'; + + self.callActions('afterRunTests', arguments); + + h.freeze(self.has); + }, + + /** + * @private + * @return {void} + */ + + setPrefixes: function() { + var self = this; + + self.callActions('beforeSetPrefixes', arguments); + + self.transitionPrefix = h.getPrefix(self.canary, 'Transition', self.VENDORS); + self.transformPrefix = h.getPrefix(self.canary, 'Transform', self.VENDORS); + self.boxSizingPrefix = h.getPrefix(self.canary, 'BoxSizing', self.VENDORS); + + self.boxSizingProp = self.boxSizingPrefix ? + self.boxSizingPrefix + h.pascalCase(self.BOX_SIZING_PROP) : self.BOX_SIZING_PROP; + + self.transitionProp = self.transitionPrefix ? + self.transitionPrefix + h.pascalCase(self.TRANSITION_PROP) : self.TRANSITION_PROP; + + self.transformProp = self.transformPrefix ? + self.transformPrefix + h.pascalCase(self.TRANSFORM_PROP) : self.TRANSFORM_PROP; + + self.transformRule = self.transformPrefix ? + '-' + self.transformPrefix + '-' + self.TRANSFORM_PROP : self.TRANSFORM_PROP; + + self.perspectiveProp = self.transformPrefix ? + self.transformPrefix + h.pascalCase(self.PERSPECTIVE_PROP) : self.PERSPECTIVE_PROP; + + self.perspectiveOriginProp = self.transformPrefix ? + self.transformPrefix + h.pascalCase(self.PERSPECTIVE_ORIGIN_PROP) : + self.PERSPECTIVE_ORIGIN_PROP; + + self.callActions('afterSetPrefixes', arguments); + } + }); + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.Has = function() { + this.transitions = false; + this.promises = false; + + h.seal(this); + }; + + // Assign a singleton instance to `mixitup.features` and initialise: + + mixitup.features = new mixitup.Features(); + + mixitup.features.init(); + + /** + * A group of properties defining the mixer's animation and effects settings. + * + * @constructor + * @memberof mixitup.Config + * @name animation + * @namespace + * @public + * @since 2.0.0 + */ + + mixitup.ConfigAnimation = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A boolean dictating whether or not animation should be enabled for the MixItUp instance. + * If `false`, all operations will occur instantly and syncronously, although callback + * functions and any returned promises will still be fulfilled. + * + * @example Example: Create a mixer with all animations disabled + * var mixer = mixitup(containerEl, { + * animation: { + * enable: false + * } + * }); + * + * @name enable + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default true + */ + + this.enable = true; + + /** + * A string of one or more space-seperated properties to which transitions will be + * applied for all filtering animations. + * + * Properties can be listed any order or combination, although they will be applied in a specific + * predefined order to produce consistent results. + * + * To learn more about available effects, experiment with our + * sandbox demo and try out the "Export config" button in the Animation options drop down. + * + * @example Example: Apply "fade" and "translateZ" effects to all animations + * // As targets are filtered in and out, they will fade between + * // opacity 1 and 0 and transform between translateZ(-100px) and + * // translateZ(0). + * + * var mixer = mixitup(containerEl, { + * animation: { + * effects: 'fade translateZ(-100px)' + * } + * }); + * + * @name effects + * @memberof mixitup.Config.animation + * @instance + * @type {string} + * @default 'fade scale' + */ + + this.effects = 'fade scale'; + + /** + * A string of one or more space-seperated effects to be applied only to filter-in + * animations, overriding `config.animation.effects` if set. + * + * @example Example: Apply downwards vertical translate to targets being filtered in + * + * var mixer = mixitup(containerEl, { + * animation: { + * effectsIn: 'fade translateY(-100%)' + * } + * }); + * + * @name effectsIn + * @memberof mixitup.Config.animation + * @instance + * @type {string} + * @default '' + */ + + this.effectsIn = ''; + + /** + * A string of one or more space-seperated effects to be applied only to filter-out + * animations, overriding `config.animation.effects` if set. + * + * @example Example: Apply upwards vertical translate to targets being filtered out + * + * var mixer = mixitup(containerEl, { + * animation: { + * effectsOut: 'fade translateY(-100%)' + * } + * }); + * + * @name effectsOut + * @memberof mixitup.Config.animation + * @instance + * @type {string} + * @default '' + */ + + this.effectsOut = ''; + + /** + * An integer dictating the duration of all MixItUp animations in milliseconds, not + * including any additional delay apllied via the `'stagger'` effect. + * + * @example Example: Apply an animation duration of 200ms to all mixitup animations + * + * var mixer = mixitup(containerEl, { + * animation: { + * duration: 200 + * } + * }); + * + * @name duration + * @memberof mixitup.Config.animation + * @instance + * @type {number} + * @default 600 + */ + + this.duration = 600; + + /** + * A valid CSS3 transition-timing function or shorthand. For a full list of accepted + * values, visit easings.net. + * + * @example Example 1: Apply "ease-in-out" easing to all animations + * + * var mixer = mixitup(containerEl, { + * animation: { + * easing: 'ease-in-out' + * } + * }); + * + * @example Example 2: Apply a custom "cubic-bezier" easing function to all animations + * var mixer = mixitup(containerEl, { + * animation: { + * easing: 'cubic-bezier(0.645, 0.045, 0.355, 1)' + * } + * }); + * + * @name easing + * @memberof mixitup.Config.animation + * @instance + * @type {string} + * @default 'ease' + */ + + this.easing = 'ease'; + + /** + * A boolean dictating whether or not to apply perspective to the MixItUp container + * during animations. By default, perspective is always applied and creates the + * illusion of three-dimensional space for effects such as `translateZ`, `rotateX`, + * and `rotateY`. + * + * You may wish to disable this and define your own perspective settings via CSS. + * + * @example Example: Prevent perspective from being applied to any 3D transforms + * var mixer = mixitup(containerEl, { + * animation: { + * applyPerspective: false + * } + * }); + * + * @name applyPerspective + * @memberof mixitup.Config.animation + * @instance + * @type {bolean} + * @default true + */ + + this.applyPerspective = true; + + /** + * The perspective distance value to be applied to the container during animations, + * affecting any 3D-transform-based effects. + * + * @example Example: Set a perspective distance of 2000px + * var mixer = mixitup(containerEl, { + * animation: { + * effects: 'rotateY(-25deg)', + * perspectiveDistance: '2000px' + * } + * }); + * + * @name perspectiveDistance + * @memberof mixitup.Config.animation + * @instance + * @type {string} + * @default '3000px' + */ + + this.perspectiveDistance = '3000px'; + + /** + * The perspective-origin value to be applied to the container during animations, + * affecting any 3D-transform-based effects. + * + * @example Example: Set a perspective origin in the top-right of the container + * var mixer = mixitup(containerEl, { + * animation: { + * effects: 'transateZ(-200px)', + * perspectiveOrigin: '100% 0' + * } + * }); + * + * @name perspectiveOrigin + * @memberof mixitup.Config.animation + * @instance + * @type {string} + * @default '50% 50%' + */ + + this.perspectiveOrigin = '50% 50%'; + + /** + * A boolean dictating whether or not to enable the queuing of operations. + * + * If `true` (default), and a control is clicked or an API call is made while another + * operation is progress, the operation will go into the queue and will be automatically exectuted + * when the previous operaitons is finished. + * + * If `false`, any requested operations will be ignored, and the `onMixBusy` callback and `mixBusy` + * event will be fired. If `debug.showWarnings` is enabled, a console warning will also occur. + * + * @example Example: Disable queuing + * var mixer = mixitup(containerEl, { + * animation: { + * queue: false + * } + * }); + * + * @name queue + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default true + */ + + this.queue = true; + + /** + * An integer dictacting the maximum number of operations allowed in the queue at + * any time, when queuing is enabled. + * + * @example Example: Allow a maximum of 5 operations in the queue at any time + * var mixer = mixitup(containerEl, { + * animation: { + * queueLimit: 5 + * } + * }); + * + * @name queueLimit + * @memberof mixitup.Config.animation + * @instance + * @type {number} + * @default 3 + */ + + this.queueLimit = 3; + + /** + * A boolean dictating whether or not to transition the height and width of the + * container as elements are filtered in and out. If disabled, the container height + * will change abruptly. + * + * It may be desirable to disable this on mobile devices as the CSS `height` and + * `width` properties do not receive GPU-acceleration and can therefore cause stuttering. + * + * @example Example 1: Disable the transitioning of the container height and/or width + * var mixer = mixitup(containerEl, { + * animation: { + * animateResizeContainer: false + * } + * }); + * + * @example Example 2: Disable the transitioning of the container height and/or width for mobile devices only + * var mixer = mixitup(containerEl, { + * animation: { + * animateResizeContainer: myFeatureTests.isMobile ? false : true + * } + * }); + * + * @name animateResizeContainer + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default true + */ + + this.animateResizeContainer = true; + + /** + * A boolean dictating whether or not to transition the height and width of target + * elements as they change throughout the course of an animation. + * + * This is often a must for flex-box grid layouts where the size of target elements may change + * depending on final their position in relation to their siblings, or for `.changeLayout()` + * operations where the size of targets change between layouts. + * + * NB: This feature requires additional calculations and manipulation to non-hardware-accelerated + * properties which may adversely affect performance on slower devices, and is therefore + * disabled by default. + * + * @example Example: Enable the transitioning of target widths and heights + * var mixer = mixitup(containerEl, { + * animation: { + * animateResizeTargets: true + * } + * }); + * + * @name animateResizeTargets + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default false + */ + + this.animateResizeTargets = false; + + /** + * A custom function used to manipulate the order in which the stagger delay is + * incremented when using the ‘stagger’ effect. + * + * When using the 'stagger' effect, the delay applied to each target element is incremented + * based on its index. You may create a custom function to manipulate the order in which the + * delay is incremented and create engaging non-linear stagger effects. + * + * The function receives the index of the target element as a parameter, and must + * return an integer which serves as the multiplier for the stagger delay. + * + * @example Example 1: Stagger target elements by column in a 3-column grid + * var mixer = mixitup(containerEl, { + * animation: { + * effects: 'fade stagger(100ms)', + * staggerSequence: function(i) { + * return i % 3; + * } + * } + * }); + * + * @example Example 2: Using an algorithm to produce a more complex sequence + * var mixer = mixitup(containerEl, { + * animation: { + * effects: 'fade stagger(100ms)', + * staggerSequence: function(i) { + * return (2*i) - (5*((i/3) - ((1/3) * (i%3)))); + * } + * } + * }); + * + * @name staggerSequence + * @memberof mixitup.Config.animation + * @instance + * @type {function} + * @default null + */ + + this.staggerSequence = null; + + /** + * A boolean dictating whether or not to reverse the direction of `translate` + * and `rotate` transforms for elements being filtered out. + * + * It can be used to create carousel-like animations where elements enter and exit + * from opposite directions. If enabled, the effect `translateX(-100%)` for elements + * being filtered in would become `translateX(100%)` for targets being filtered out. + * + * This functionality can also be achieved by providing seperate effects + * strings for `config.animation.effectsIn` and `config.animation.effectsOut`. + * + * @example Example: Reverse the desired direction on any translate/rotate effect for targets being filtered out + * // Elements being filtered in will be translated from '100%' to '0' while + * // elements being filtered out will be translated from 0 to '-100%' + * + * var mixer = mixitup(containerEl, { + * animation: { + * effects: 'fade translateX(100%)', + * reverseOut: true, + * nudge: false // Disable nudging to create a carousel-like effect + * } + * }); + * + * @name reverseOut + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default false + */ + + this.reverseOut = false; + + /** + * A boolean dictating whether or not to "nudge" the animation path of targets + * when they are being filtered in and out simulatenously. + * + * This has been the default behavior of MixItUp since version 1, but it + * may be desirable to disable this effect when filtering directly from + * one exclusive set of targets to a different exclusive set of targets, + * to create a carousel-like effect, or a generally more subtle animation. + * + * @example Example: Disable the "nudging" of targets being filtered in and out simulatenously + * + * var mixer = mixitup(containerEl, { + * animation: { + * nudge: false + * } + * }); + * + * @name nudge + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default true + */ + + this.nudge = true; + + /** + * A boolean dictating whether or not to clamp the height of the container while MixItUp's + * geometry tests are carried out before an operation. + * + * To prevent scroll-bar flicker, clamping is turned on by default. But in the case where the + * height of the container might affect its vertical positioning in the viewport + * (e.g. a vertically-centered container), this should be turned off to ensure accurate + * test results and a smooth animation. + * + * @example Example: Disable container height-clamping + * + * var mixer = mixitup(containerEl, { + * animation: { + * clampHeight: false + * } + * }); + * + * @name clampHeight + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default true + */ + + this.clampHeight = true; + + /** + * A boolean dictating whether or not to clamp the width of the container while MixItUp's + * geometry tests are carried out before an operation. + * + * To prevent scroll-bar flicker, clamping is turned on by default. But in the case where the + * width of the container might affect its horitzontal positioning in the viewport + * (e.g. a horizontall-centered container), this should be turned off to ensure accurate + * test results and a smooth animation. + * + * @example Example: Disable container width-clamping + * + * var mixer = mixitup(containerEl, { + * animation: { + * clampWidth: false + * } + * }); + * + * @name clampWidth + * @memberof mixitup.Config.animation + * @instance + * @type {boolean} + * @default true + */ + + this.clampWidth = true; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigAnimation); + + mixitup.ConfigAnimation.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigAnimation.prototype.constructor = mixitup.ConfigAnimation; + + /** + * A group of properties relating to the behavior of the Mixer. + * + * @constructor + * @memberof mixitup.Config + * @name behavior + * @namespace + * @public + * @since 3.1.12 + */ + + mixitup.ConfigBehavior = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A boolean dictating whether to allow "live" sorting of the mixer. + * + * Because of the expensive nature of sorting, MixItUp makes use of several + * internal optimizations to skip redundant sorting operations, such as when + * the newly requested sort command is the same as the active one. The caveat + * to this optimization is that "live" edits to the value of a target's sorting + * attribute will be ignored when requesting a re-sort by the same attribute. + * + * By setting to `behavior.liveSort` to `true`, the mixer will always re-sort + * regardless of whether or not the sorting attribute and order have changed. + * + * @example Example: Enabling `liveSort` to allow for re-sorting + * + * var mixer = mixitup(containerEl, { + * behavior: { + * liveSort: true + * }, + * load: { + * sort: 'edited:desc' + * } + * }); + * + * var target = containerEl.children[3]; + * + * console.log(target.getAttribute('data-edited')); // '2015-04-24' + * + * target.setAttribute('data-edited', '2017-08-10'); // Update the target's edited date + * + * mixer.sort('edited:desc') + * .then(function(state) { + * // The target is now at the top of the list + * + * console.log(state.targets[0] === target); // true + * }); + * + * @name liveSort + * @memberof mixitup.Config.behavior + * @instance + * @type {boolean} + * @default false + */ + + this.liveSort = false; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigBehavior); + + mixitup.ConfigBehavior.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigBehavior.prototype.constructor = mixitup.ConfigBehavior; + + /** + * A group of optional callback functions to be invoked at various + * points within the lifecycle of a mixer operation. + * + * Each function is analogous to an event of the same name triggered from the + * container element, and is invoked immediately after it. + * + * All callback functions receive the current `state` object as their first + * argument, as well as other more specific arguments described below. + * + * @constructor + * @memberof mixitup.Config + * @name callbacks + * @namespace + * @public + * @since 2.0.0 + */ + + mixitup.ConfigCallbacks = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A callback function invoked immediately after any MixItUp operation is requested + * and before animations have begun. + * + * A second `futureState` argument is passed to the function which represents the final + * state of the mixer once the requested operation has completed. + * + * @example Example: Adding an `onMixStart` callback function + * var mixer = mixitup(containerEl, { + * callbacks: { + * onMixStart: function(state, futureState) { + * console.log('Starting operation...'); + * } + * } + * }); + * + * @name onMixStart + * @memberof mixitup.Config.callbacks + * @instance + * @type {function} + * @default null + */ + + this.onMixStart = null; + + /** + * A callback function invoked when a MixItUp operation is requested while another + * operation is in progress, and the animation queue is full, or queueing + * is disabled. + * + * @example Example: Adding an `onMixBusy` callback function + * var mixer = mixitup(containerEl, { + * callbacks: { + * onMixBusy: function(state) { + * console.log('Mixer busy'); + * } + * } + * }); + * + * @name onMixBusy + * @memberof mixitup.Config.callbacks + * @instance + * @type {function} + * @default null + */ + + this.onMixBusy = null; + + /** + * A callback function invoked after any MixItUp operation has completed, and the + * state has been updated. + * + * @example Example: Adding an `onMixEnd` callback function + * var mixer = mixitup(containerEl, { + * callbacks: { + * onMixEnd: function(state) { + * console.log('Operation complete'); + * } + * } + * }); + * + * @name onMixEnd + * @memberof mixitup.Config.callbacks + * @instance + * @type {function} + * @default null + */ + + this.onMixEnd = null; + + /** + * A callback function invoked whenever an operation "fails", i.e. no targets + * could be found matching the requested filter. + * + * @example Example: Adding an `onMixFail` callback function + * var mixer = mixitup(containerEl, { + * callbacks: { + * onMixFail: function(state) { + * console.log('No items could be found matching the requested filter'); + * } + * } + * }); + * + * @name onMixFail + * @memberof mixitup.Config.callbacks + * @instance + * @type {function} + * @default null + */ + + this.onMixFail = null; + + /** + * A callback function invoked whenever a MixItUp control is clicked, and before its + * respective operation is requested. + * + * The clicked element is assigned to the `this` keyword within the function. The original + * click event is passed to the function as the second argument, which can be useful if + * using `` tags as controls where the default behavior needs to be prevented. + * + * Returning `false` from the callback will prevent the control click from triggering + * an operation. + * + * @example Example 1: Adding an `onMixClick` callback function + * var mixer = mixitup(containerEl, { + * callbacks: { + * onMixClick: function(state, originalEvent) { + * console.log('The control "' + this.innerText + '" was clicked'); + * } + * } + * }); + * + * @example Example 2: Using `onMixClick` to manipulate the original click event + * var mixer = mixitup(containerEl, { + * callbacks: { + * onMixClick: function(state, originalEvent) { + * // Prevent original click event from bubbling up: + * originalEvent.stopPropagation(); + * + * // Prevent default behavior of clicked element: + * originalEvent.preventDefault(); + * } + * } + * }); + * + * @example Example 3: Using `onMixClick` to conditionally cancel operations + * var mixer = mixitup(containerEl, { + * callbacks: { + * onMixClick: function(state, originalEvent) { + * // Perform some conditional check: + * + * if (myApp.isLoading) { + * // By returning false, we can prevent the control click from triggering an operation. + * + * return false; + * } + * } + * } + * }); + * + * @name onMixClick + * @memberof mixitup.Config.callbacks + * @instance + * @type {function} + * @default null + */ + + this.onMixClick = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigCallbacks); + + mixitup.ConfigCallbacks.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigCallbacks.prototype.constructor = mixitup.ConfigCallbacks; + + /** + * A group of properties relating to clickable control elements. + * + * @constructor + * @memberof mixitup.Config + * @name controls + * @namespace + * @public + * @since 2.0.0 + */ + + mixitup.ConfigControls = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A boolean dictating whether or not controls should be enabled for the mixer instance. + * + * If `true` (default behavior), MixItUp will search the DOM for any clickable elements with + * `data-filter`, `data-sort` or `data-toggle` attributes, and bind them for click events. + * + * If `false`, no click handlers will be bound, and all functionality must therefore be performed + * via the mixer's API methods. + * + * If you do not intend to use the default controls, setting this property to `false` will + * marginally improve the startup time of your mixer instance, and will also prevent any other active + * mixer instances in the DOM which are bound to controls from controlling the instance. + * + * @example Example: Disabling controls + * var mixer = mixitup(containerEl, { + * controls: { + * enable: false + * } + * }); + * + * // With the default controls disabled, we can only control + * // the mixer via its API methods, e.g.: + * + * mixer.filter('.cat-1'); + * + * @name enable + * @memberof mixitup.Config.controls + * @instance + * @type {boolean} + * @default true + */ + + this.enable = true; + + /** + * A boolean dictating whether or not to use event delegation when binding click events + * to the default controls. + * + * If `false` (default behavior), each control button in the DOM will be found and + * individually bound when a mixer is instantiated, with their corresponding actions + * cached for performance. + * + * If `true`, a single click handler will be applied to the `window` (or container element - see + * `config.controls.scope`), and any click events triggered by elements with `data-filter`, + * `data-sort` or `data-toggle` attributes present will be handled as they propagate upwards. + * + * If you require a user interface where control buttons may be added, removed, or changed during the + * lifetime of a mixer, `controls.live` should be set to `true`. There is a marginal but unavoidable + * performance deficit when using live controls, as the value of each control button must be read + * from the DOM in real time once the click event has propagated. + * + * @example Example: Setting live controls + * var mixer = mixitup(containerEl, { + * controls: { + * live: true + * } + * }); + * + * // Control buttons can now be added, remove and changed without breaking + * // the mixer's UI + * + * @name live + * @memberof mixitup.Config.controls + * @instance + * @type {boolean} + * @default true + */ + + this.live = false; + + /** + * A string dictating the "scope" to use when binding or querying the default controls. The available + * values are `'global'` or `'local'`. + * + * When set to `'global'` (default behavior), MixItUp will query the entire document for control buttons + * to bind, or delegate click events from (see `config.controls.live`). + * + * When set to `'local'`, MixItUp will only query (or bind click events to) its own container element. + * This may be desireable if you require multiple active mixer instances within the same document, with + * controls that would otherwise intefere with each other if scoped globally. + * + * Conversely, if you wish to control multiple instances with a single UI, you would create one + * set of controls and keep the controls scope of each mixer set to `global`. + * + * @example Example: Setting 'local' scoped controls + * var mixerOne = mixitup(containerOne, { + * controls: { + * scope: 'local' + * } + * }); + * + * var mixerTwo = mixitup(containerTwo, { + * controls: { + * scope: 'local' + * } + * }); + * + * // Both mixers can now exist within the same document with + * // isolated controls placed within their container elements. + * + * @name scope + * @memberof mixitup.Config.controls + * @instance + * @type {string} + * @default 'global' + */ + + this.scope = 'global'; // enum: ['local' ,'global'] + + /** + * A string dictating the type of logic to apply when concatenating the filter selectors of + * active toggle buttons (i.e. any clickable element with a `data-toggle` attribute). + * + * If set to `'or'` (default behavior), selectors will be concatenated together as + * a comma-seperated list. For example: + * + * `'.cat-1, .cat-2'` (shows any elements matching `'.cat-1'` OR `'.cat-2'`) + * + * If set to `'and'`, selectors will be directly concatenated together. For example: + * + * `'.cat-1.cat-2'` (shows any elements which match both `'.cat-1'` AND `'.cat-2'`) + * + * @example Example: Setting "and" toggle logic + * var mixer = mixitup(containerEl, { + * controls: { + * toggleLogic: 'and' + * } + * }); + * + * @name toggleLogic + * @memberof mixitup.Config.controls + * @instance + * @type {string} + * @default 'or' + */ + + this.toggleLogic = 'or'; // enum: ['or', 'and'] + + /** + * A string dictating the filter behavior when all toggles are inactive. + * + * When set to `'all'` (default behavior), *all* targets will be shown by default + * when no toggles are active, or at the moment all active toggles are toggled off. + * + * When set to `'none'`, no targets will be shown by default when no toggles are + * active, or at the moment all active toggles are toggled off. + * + * @example Example 1: Setting the default toggle behavior to `'all'` + * var mixer = mixitup(containerEl, { + * controls: { + * toggleDefault: 'all' + * } + * }); + * + * mixer.toggleOn('.cat-2') + * .then(function() { + * // Deactivate all active toggles + * + * return mixer.toggleOff('.cat-2') + * }) + * .then(function(state) { + * console.log(state.activeFilter.selector); // 'all' + * console.log(state.totalShow); // 12 + * }); + * + * @example Example 2: Setting the default toggle behavior to `'none'` + * var mixer = mixitup(containerEl, { + * controls: { + * toggleDefault: 'none' + * } + * }); + * + * mixer.toggleOn('.cat-2') + * .then(function() { + * // Deactivate all active toggles + * + * return mixer.toggleOff('.cat-2') + * }) + * .then(function(state) { + * console.log(state.activeFilter.selector); // 'none' + * console.log(state.totalShow); // 0 + * }); + * + * @name toggleDefault + * @memberof mixitup.Config.controls + * @instance + * @type {string} + * @default 'all' + */ + + this.toggleDefault = 'all'; // enum: ['all', 'none'] + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigControls); + + mixitup.ConfigControls.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigControls.prototype.constructor = mixitup.ConfigControls; + + /** + * A group of properties defining the output and structure of class names programmatically + * added to controls and containers to reflect the state of the mixer. + * + * Most commonly, class names are added to controls by MixItUp to indicate that + * the control is active so that it can be styled accordingly - `'mixitup-control-active'` by default. + * + * Using a "BEM" like structure, each classname is broken into the three parts: + * a block namespace (`'mixitup'`), an element name (e.g. `'control'`), and an optional modifier + * name (e.g. `'active'`) reflecting the state of the element. + * + * By default, each part of the classname is concatenated together using single hyphens as + * delineators, but this can be easily customised to match the naming convention and style of + * your project. + * + * @constructor + * @memberof mixitup.Config + * @name classNames + * @namespace + * @public + * @since 3.0.0 + */ + + mixitup.ConfigClassNames = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * The "block" portion, or top-level namespace added to the start of any class names created by MixItUp. + * + * @example Example 1: changing the `config.classNames.block` value + * var mixer = mixitup(containerEl, { + * classNames: { + * block: 'portfolio' + * } + * }); + * + * // Active control output: "portfolio-control-active" + * + * @example Example 2: Removing `config.classNames.block` + * var mixer = mixitup(containerEl, { + * classNames: { + * block: '' + * } + * }); + * + * // Active control output: "control-active" + * + * @name block + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'mixitup' + */ + + this.block = 'mixitup'; + + /** + * The "element" portion of the class name added to container. + * + * @name elementContainer + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'container' + */ + + this.elementContainer = 'container'; + + /** + * The "element" portion of the class name added to filter controls. + * + * By default, all filter, sort, multimix and toggle controls take the same element value of `'control'`, but + * each type's element value can be individually overwritten to match the unique classNames of your controls as needed. + * + * @example Example 1: changing the `config.classNames.elementFilter` value + * var mixer = mixitup(containerEl, { + * classNames: { + * elementFilter: 'filter' + * } + * }); + * + * // Active filter output: "mixitup-filter-active" + * + * @example Example 2: changing the `config.classNames.block` and `config.classNames.elementFilter` values + * var mixer = mixitup(containerEl, { + * classNames: { + * block: 'portfolio', + * elementFilter: 'filter' + * } + * }); + * + * // Active filter output: "portfolio-filter-active" + * + * @name elementFilter + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'control' + */ + + this.elementFilter = 'control'; + + /** + * The "element" portion of the class name added to sort controls. + * + * By default, all filter, sort, multimix and toggle controls take the same element value of `'control'`, but + * each type's element value can be individually overwritten to match the unique classNames of your controls as needed. + * + * @example Example 1: changing the `config.classNames.elementSort` value + * var mixer = mixitup(containerEl, { + * classNames: { + * elementSort: 'sort' + * } + * }); + * + * // Active sort output: "mixitup-sort-active" + * + * @example Example 2: changing the `config.classNames.block` and `config.classNames.elementSort` values + * var mixer = mixitup(containerEl, { + * classNames: { + * block: 'portfolio', + * elementSort: 'sort' + * } + * }); + * + * // Active sort output: "portfolio-sort-active" + * + * @name elementSort + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'control' + */ + + this.elementSort = 'control'; + + /** + * The "element" portion of the class name added to multimix controls. + * + * By default, all filter, sort, multimix and toggle controls take the same element value of `'control'`, but + * each type's element value can be individually overwritten to match the unique classNames of your controls as needed. + * + * @example Example 1: changing the `config.classNames.elementMultimix` value + * var mixer = mixitup(containerEl, { + * classNames: { + * elementMultimix: 'multimix' + * } + * }); + * + * // Active multimix output: "mixitup-multimix-active" + * + * @example Example 2: changing the `config.classNames.block` and `config.classNames.elementMultimix` values + * var mixer = mixitup(containerEl, { + * classNames: { + * block: 'portfolio', + * elementSort: 'multimix' + * } + * }); + * + * // Active multimix output: "portfolio-multimix-active" + * + * @name elementMultimix + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'control' + */ + + this.elementMultimix = 'control'; + + /** + * The "element" portion of the class name added to toggle controls. + * + * By default, all filter, sort, multimix and toggle controls take the same element value of `'control'`, but + * each type's element value can be individually overwritten to match the unique classNames of your controls as needed. + * + * @example Example 1: changing the `config.classNames.elementToggle` value + * var mixer = mixitup(containerEl, { + * classNames: { + * elementToggle: 'toggle' + * } + * }); + * + * // Active toggle output: "mixitup-toggle-active" + * + * @example Example 2: changing the `config.classNames.block` and `config.classNames.elementToggle` values + * var mixer = mixitup(containerEl, { + * classNames: { + * block: 'portfolio', + * elementToggle: 'toggle' + * } + * }); + * + * // Active toggle output: "portfolio-toggle-active" + * + * @name elementToggle + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'control' + */ + + this.elementToggle = 'control'; + + /** + * The "modifier" portion of the class name added to active controls. + * @name modifierActive + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'active' + */ + + this.modifierActive = 'active'; + + /** + * The "modifier" portion of the class name added to disabled controls. + * + * @name modifierDisabled + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'disabled' + */ + + this.modifierDisabled = 'disabled'; + + /** + * The "modifier" portion of the class name added to the container when in a "failed" state. + * + * @name modifierFailed + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default 'failed' + */ + + this.modifierFailed = 'failed'; + + /** + * The delineator used between the "block" and "element" portions of any class name added by MixItUp. + * + * If the block portion is ommited by setting it to an empty string, no delineator will be added. + * + * @example Example: changing the delineator to match BEM convention + * var mixer = mixitup(containerEl, { + * classNames: { + * delineatorElement: '__' + * } + * }); + * + * // example active control output: "mixitup__control-active" + * + * @name delineatorElement + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default '-' + */ + + this.delineatorElement = '-'; + + /** + * The delineator used between the "element" and "modifier" portions of any class name added by MixItUp. + * + * If the element portion is ommited by setting it to an empty string, no delineator will be added. + * + * @example Example: changing both delineators to match BEM convention + * var mixer = mixitup(containerEl, { + * classNames: { + * delineatorElement: '__' + * delineatorModifier: '--' + * } + * }); + * + * // Active control output: "mixitup__control--active" + * + * @name delineatorModifier + * @memberof mixitup.Config.classNames + * @instance + * @type {string} + * @default '-' + */ + + this.delineatorModifier = '-'; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigClassNames); + + mixitup.ConfigClassNames.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigClassNames.prototype.constructor = mixitup.ConfigClassNames; + + /** + * A group of properties relating to MixItUp's dataset API. + * + * @constructor + * @memberof mixitup.Config + * @name data + * @namespace + * @public + * @since 3.0.0 + */ + + mixitup.ConfigData = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A string specifying the name of the key containing your data model's unique + * identifier (UID). To use the dataset API, a UID key must be specified and + * be present and unique on all objects in the dataset you provide to MixItUp. + * + * For example, if your dataset is made up of MongoDB documents, the UID + * key would be `'id'` or `'_id'`. + * + * @example Example: Setting the UID to `'id'` + * var mixer = mixitup(containerEl, { + * data: { + * uidKey: 'id' + * } + * }); + * + * @name uidKey + * @memberof mixitup.Config.data + * @instance + * @type {string} + * @default '' + */ + + this.uidKey = ''; + + /** + * A boolean dictating whether or not MixItUp should "dirty check" each object in + * your dataset for changes whenever `.dataset()` is called, and re-render any targets + * for which a change is found. + * + * Depending on the complexity of your data model, dirty checking can be expensive + * and is therefore disabled by default. + * + * NB: For changes to be detected, a new immutable instance of the edited model must be + * provided to mixitup, rather than manipulating properties on the existing instance. + * If your changes are a result of a DB write and read, you will most likely be calling + * `.dataset()` with a clean set of objects each time, so this will not be an issue. + * + * @example Example: Enabling dirty checking + * + * var myDataset = [ + * { + * id: 0, + * title: "Blog Post Title 0" + * ... + * }, + * { + * id: 1, + * title: "Blog Post Title 1" + * ... + * } + * ]; + * + * // Instantiate a mixer with a pre-loaded dataset, and a target renderer + * // function defined + * + * var mixer = mixitup(containerEl, { + * data: { + * uidKey: 'id', + * dirtyCheck: true + * }, + * load: { + * dataset: myDataset + * }, + * render: { + * target: function() { ... } + * } + * }); + * + * // For illustration, we will clone and edit the second object in the dataset. + * // NB: this would typically be done server-side in response to a DB update, + * and then re-queried via an API. + * + * myDataset[1] = Object.assign({}, myDataset[1]); + * + * myDataset[1].title = 'Blog Post Title 11'; + * + * mixer.dataset(myDataset) + * .then(function() { + * // the target with ID "1", will be re-rendered reflecting its new title + * }); + * + * @name dirtyCheck + * @memberof mixitup.Config.data + * @instance + * @type {boolean} + * @default false + */ + + this.dirtyCheck = false; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigData); + + mixitup.ConfigData.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigData.prototype.constructor = mixitup.ConfigData; + + /** + * A group of properties allowing the toggling of various debug features. + * + * @constructor + * @memberof mixitup.Config + * @name debug + * @namespace + * @public + * @since 3.0.0 + */ + + mixitup.ConfigDebug = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A boolean dictating whether or not the mixer instance returned by the + * `mixitup()` factory function should expose private properties and methods. + * + * By default, mixer instances only expose their public API, but enabling + * debug mode will give you access to various mixer internals which may aid + * in debugging, or the authoring of extensions. + * + * @example Example: Enabling debug mode + * + * var mixer = mixitup(containerEl, { + * debug: { + * enable: true + * } + * }); + * + * // Private properties and methods will now be visible on the mixer instance: + * + * console.log(mixer); + * + * @name enable + * @memberof mixitup.Config.debug + * @instance + * @type {boolean} + * @default false + */ + + this.enable = false; + + /** + * A boolean dictating whether or not warnings should be shown when various + * common gotchas occur. + * + * Warnings are intended to provide insights during development when something + * occurs that is not a fatal, but may indicate an issue with your integration, + * and are therefore turned on by default. However, you may wish to disable + * them in production. + * + * @example Example 1: Disabling warnings + * + * var mixer = mixitup(containerEl, { + * debug: { + * showWarnings: false + * } + * }); + * + * @example Example 2: Disabling warnings based on environment + * + * var showWarnings = myAppConfig.environment === 'development' ? true : false; + * + * var mixer = mixitup(containerEl, { + * debug: { + * showWarnings: showWarnings + * } + * }); + * + * @name showWarnings + * @memberof mixitup.Config.debug + * @instance + * @type {boolean} + * @default true + */ + + this.showWarnings = true; + + /** + * Used for server-side testing only. + * + * @private + * @name fauxAsync + * @memberof mixitup.Config.debug + * @instance + * @type {boolean} + * @default false + */ + + this.fauxAsync = false; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigDebug); + + mixitup.ConfigDebug.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigDebug.prototype.constructor = mixitup.ConfigDebug; + + /** + * A group of properties relating to the layout of the container. + * + * @constructor + * @memberof mixitup.Config + * @name layout + * @namespace + * @public + * @since 3.0.0 + */ + + mixitup.ConfigLayout = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A boolean dictating whether or not mixitup should query all descendants + * of the container for targets, or only immediate children. + * + * By default, mixitup will query all descendants matching the + * `selectors.target` selector when indexing targets upon instantiation. + * This allows for targets to be nested inside a sub-container which is + * useful when ring-fencing targets from locally scoped controls in your + * markup (see `controls.scope`). + * + * However, if you are building a more complex UI requiring the nesting + * of mixers within mixers, you will most likely want to limit targets to + * immediate children of the container by setting this property to `false`. + * + * @example Example: Restricting targets to immediate children + * + * var mixer = mixitup(containerEl, { + * layout: { + * allowNestedTargets: false + * } + * }); + * + * @name allowNestedTargets + * @memberof mixitup.Config.layout + * @instance + * @type {boolean} + * @default true + */ + + this.allowNestedTargets = true; + + /** + * A string specifying an optional class name to apply to the container when in + * its default state. + * + * By changing this class name or adding a class name to the container via the + * `.changeLayout()` API method, the CSS layout of the container can be changed, + * and MixItUp will attemp to gracefully animate the container and its targets + * between states. + * + * @example Example 1: Specifying a container class name + * + * var mixer = mixitup(containerEl, { + * layout: { + * containerClassName: 'grid' + * } + * }); + * + * @example Example 2: Changing the default class name with `.changeLayout()` + * + * var mixer = mixitup(containerEl, { + * layout: { + * containerClassName: 'grid' + * } + * }); + * + * mixer.changeLayout('list') + * .then(function(state) { + * console.log(state.activeContainerClass); // "list" + * }); + * + * @name containerClassName + * @memberof mixitup.Config.layout + * @instance + * @type {string} + * @default '' + */ + + this.containerClassName = ''; + + /** + * A reference to a non-target sibling element after which to insert targets + * when there are no targets in the container. + * + * @example Example: Setting a `siblingBefore` reference element + * + * var addButton = containerEl.querySelector('button'); + * + * var mixer = mixitup(containerEl, { + * layout: { + * siblingBefore: addButton + * } + * }); + * + * @name siblingBefore + * @memberof mixitup.Config.layout + * @instance + * @type {HTMLElement} + * @default null + */ + + this.siblingBefore = null; + + /** + * A reference to a non-target sibling element before which to insert targets + * when there are no targets in the container. + * + * @example Example: Setting an `siblingAfter` reference element + * + * var gap = containerEl.querySelector('.gap'); + * + * var mixer = mixitup(containerEl, { + * layout: { + * siblingAfter: gap + * } + * }); + * + * @name siblingAfter + * @memberof mixitup.Config.layout + * @instance + * @type {HTMLElement} + * @default null + */ + + this.siblingAfter = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigLayout); + + mixitup.ConfigLayout.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigLayout.prototype.constructor = mixitup.ConfigLayout; + + /** + * A group of properties defining the initial state of the mixer on load (instantiation). + * + * @constructor + * @memberof mixitup.Config + * @name load + * @namespace + * @public + * @since 2.0.0 + */ + + mixitup.ConfigLoad = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A string defining any filtering to be statically applied to the mixer on load. + * As per the `.filter()` API, this can be any valid selector string, or the + * values `'all'` or `'none'`. + * + * @example Example 1: Defining an initial filter selector to be applied on load + * + * // The mixer will show only those targets matching '.category-a' on load. + * + * var mixer = mixitup(containerEl, { + * load: { + * filter: '.category-a' + * } + * }); + * + * @example Example 2: Hiding all targets on load + * + * // The mixer will show hide all targets on load. + * + * var mixer = mixitup(containerEl, { + * load: { + * filter: 'none' + * } + * }); + * + * @name filter + * @memberof mixitup.Config.load + * @instance + * @type {string} + * @default 'all' + */ + + this.filter = 'all'; + + /** + * A string defining any sorting to be statically applied to the mixer on load. + * As per the `.sort()` API, this should be a valid "sort string" made up of + * an attribute to sort by (or `'default'`) followed by an optional sorting + * order, or the value `'random'`; + * + * @example Example: Defining sorting to be applied on load + * + * // The mixer will sort the container by the value of the `data-published-date` + * // attribute, in descending order. + * + * var mixer = mixitup(containerEl, { + * load: { + * sort: 'published-date:desc' + * } + * }); + * + * @name sort + * @memberof mixitup.Config.load + * @instance + * @type {string} + * @default 'default:asc' + */ + + this.sort = 'default:asc'; + + /** + * An array of objects representing the underlying data of any pre-rendered targets, + * when using the `.dataset()` API. + * + * NB: If targets are pre-rendered when the mixer is instantiated, this must be set. + * + * @example Example: Defining the initial underyling dataset + * + * var myDataset = [ + * { + * id: 0, + * title: "Blog Post Title 0", + * ... + * }, + * { + * id: 1, + * title: "Blog Post Title 1", + * ... + * } + * ]; + * + * var mixer = mixitup(containerEl, { + * data: { + * uidKey: 'id' + * }, + * load: { + * dataset: myDataset + * } + * }); + * + * @name dataset + * @memberof mixitup.Config.load + * @instance + * @type {Array.} + * @default null + */ + + this.dataset = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigLoad); + + mixitup.ConfigLoad.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigLoad.prototype.constructor = mixitup.ConfigLoad; + + /** + * A group of properties defining the selectors used to query elements within a mixitup container. + * + * @constructor + * @memberof mixitup.Config + * @name selectors + * @namespace + * @public + * @since 3.0.0 + */ + + mixitup.ConfigSelectors = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A selector string used to query and index target elements within the container. + * + * By default, the class selector `'.mix'` is used, but this can be changed to an + * attribute or element selector to match the style of your project. + * + * @example Example 1: Changing the target selector + * + * var mixer = mixitup(containerEl, { + * selectors: { + * target: '.portfolio-item' + * } + * }); + * + * @example Example 2: Using an attribute selector as a target selector + * + * // The mixer will search for any children with the attribute `data-ref="mix"` + * + * var mixer = mixitup(containerEl, { + * selectors: { + * target: '[data-ref="mix"]' + * } + * }); + * + * @name target + * @memberof mixitup.Config.selectors + * @instance + * @type {string} + * @default '.mix' + */ + + this.target = '.mix'; + + /** + * A optional selector string used to add further specificity to the querying of control elements, + * in addition to their mandatory data attribute (e.g. `data-filter`, `data-toggle`, `data-sort`). + * + * This can be used if other elements in your document must contain the above attributes + * (e.g. for use in third-party scripts), and would otherwise interfere with MixItUp. Adding + * an additional `control` selector of your choice allows MixItUp to restrict event handling + * to only those elements matching the defined selector. + * + * @name control + * @memberof mixitup.Config.selectors + * @instance + * @type {string} + * @default '' + * + * @example Example 1: Adding a `selectors.control` selector + * + * var mixer = mixitup(containerEl, { + * selectors: { + * control: '.mixitup-control' + * } + * }); + * + * // Will not be handled: + * // + * + * // Will be handled: + * // + */ + + this.control = ''; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigSelectors); + + mixitup.ConfigSelectors.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigSelectors.prototype.constructor = mixitup.ConfigSelectors; + + /** + * A group of optional render functions for creating and updating elements. + * + * All render functions receive a data object, and should return a valid HTML string. + * + * @constructor + * @memberof mixitup.Config + * @name render + * @namespace + * @public + * @since 3.0.0 + */ + + mixitup.ConfigRender = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A function returning an HTML string representing a target element, or a reference to a + * single DOM element. + * + * The function is invoked as part of the `.dataset()` API, whenever a new item is added + * to the dataset, or an item in the dataset changes (if `dataset.dirtyCheck` is enabled). + * + * The function receives the relevant dataset item as its first parameter. + * + * @example Example 1: Using string concatenation + * + * var mixer = mixitup(containerEl, { + * render: { + * target: function(item) { + * return ( + * '<div class="mix">' + + * '<h2>' + item.title + '</h2>' + + * '</div>' + * ); + * } + * } + * }); + * + * @example Example 2: Using an ES2015 template literal + * + * var mixer = mixitup(containerEl, { + * render: { + * target: function(item) { + * return ( + * `<div class="mix"> + * <h2>${item.title}</h2> + * </div>` + * ); + * } + * } + * }); + * + * @example Example 3: Using a Handlebars template + * + * var targetTemplate = Handlebars.compile('<div class="mix"><h2>{{title}}</h2></div>'); + * + * var mixer = mixitup(containerEl, { + * render: { + * target: targetTemplate + * } + * }); + * + * @example Example 4: Returning a DOM element + * + * var mixer = mixitup(containerEl, { + * render: { + * target: function(item) { + * // Create a single element using your framework's built-in renderer + * + * var el = ... + * + * return el; + * } + * } + * }); + * + * @name target + * @memberof mixitup.Config.render + * @instance + * @type {function} + * @default 'null' + */ + + this.target = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigRender); + + mixitup.ConfigRender.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigRender.prototype.constructor = mixitup.ConfigRender; + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.ConfigTemplates = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ConfigTemplates); + + mixitup.ConfigTemplates.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ConfigTemplates.prototype.constructor = mixitup.ConfigTemplates; + + /** + * `mixitup.Config` is an interface used for customising the functionality of a + * mixer instance. It is organised into several semantically distinct sub-objects, + * each one pertaining to a particular aspect of MixItUp functionality. + * + * An object literal containing any or all of the available properies, + * known as the "configuration object", can be passed as the second parameter to + * the `mixitup` factory function when creating a mixer instance to customise its + * functionality as needed. + * + * If no configuration object is passed, the mixer instance will take on the default + * configuration values detailed below. + * + * @example Example 1: Creating and passing the configuration object + * // Create a configuration object with desired values + * + * var config = { + * animation: { + * enable: false + * }, + * selectors: { + * target: '.item' + * } + * }; + * + * // Pass the configuration object to the mixitup factory function + * + * var mixer = mixitup(containerEl, config); + * + * @example Example 2: Passing the configuration object inline + * // Typically, the configuration object is passed inline for brevity. + * + * var mixer = mixitup(containerEl, { + * controls: { + * live: true, + * toggleLogic: 'and' + * } + * }); + * + * + * @constructor + * @memberof mixitup + * @namespace + * @public + * @since 2.0.0 + */ + + mixitup.Config = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.animation = new mixitup.ConfigAnimation(); + this.behavior = new mixitup.ConfigBehavior(); + this.callbacks = new mixitup.ConfigCallbacks(); + this.controls = new mixitup.ConfigControls(); + this.classNames = new mixitup.ConfigClassNames(); + this.data = new mixitup.ConfigData(); + this.debug = new mixitup.ConfigDebug(); + this.layout = new mixitup.ConfigLayout(); + this.load = new mixitup.ConfigLoad(); + this.selectors = new mixitup.ConfigSelectors(); + this.render = new mixitup.ConfigRender(); + this.templates = new mixitup.ConfigTemplates(); + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.Config); + + mixitup.Config.prototype = Object.create(mixitup.Base.prototype); + + mixitup.Config.prototype.constructor = mixitup.Config; + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.MixerDom = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.document = null; + this.body = null; + this.container = null; + this.parent = null; + this.targets = []; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.MixerDom); + + mixitup.MixerDom.prototype = Object.create(mixitup.Base.prototype); + + mixitup.MixerDom.prototype.constructor = mixitup.MixerDom; + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.UiClassNames = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.base = ''; + this.active = ''; + this.disabled = ''; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.UiClassNames); + + mixitup.UiClassNames.prototype = Object.create(mixitup.Base.prototype); + + mixitup.UiClassNames.prototype.constructor = mixitup.UiClassNames; + + /** + * An object into which all arbitrary arguments sent to '.dataset()' are mapped. + * + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.CommandDataset = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.dataset = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.CommandDataset); + + mixitup.CommandDataset.prototype = Object.create(mixitup.Base.prototype); + + mixitup.CommandDataset.prototype.constructor = mixitup.CommandDataset; + + /** + * An object into which all arbitrary arguments sent to '.multimix()' are mapped. + * + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.CommandMultimix = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.filter = null; + this.sort = null; + this.insert = null; + this.remove = null; + this.changeLayout = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.CommandMultimix); + + mixitup.CommandMultimix.prototype = Object.create(mixitup.Base.prototype); + + mixitup.CommandMultimix.prototype.constructor = mixitup.CommandMultimix; + + /** + * An object into which all arbitrary arguments sent to '.filter()' are mapped. + * + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.CommandFilter = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.selector = ''; + this.collection = null; + this.action = 'show'; // enum: ['show', 'hide'] + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.CommandFilter); + + mixitup.CommandFilter.prototype = Object.create(mixitup.Base.prototype); + + mixitup.CommandFilter.prototype.constructor = mixitup.CommandFilter; + + /** + * An object into which all arbitrary arguments sent to '.sort()' are mapped. + * + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.CommandSort = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.sortString = ''; + this.attribute = ''; + this.order = 'asc'; + this.collection = null; + this.next = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.CommandSort); + + mixitup.CommandSort.prototype = Object.create(mixitup.Base.prototype); + + mixitup.CommandSort.prototype.constructor = mixitup.CommandSort; + + /** + * An object into which all arbitrary arguments sent to '.insert()' are mapped. + * + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.CommandInsert = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.index = 0; + this.collection = []; + this.position = 'before'; // enum: ['before', 'after'] + this.sibling = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.CommandInsert); + + mixitup.CommandInsert.prototype = Object.create(mixitup.Base.prototype); + + mixitup.CommandInsert.prototype.constructor = mixitup.CommandInsert; + + /** + * An object into which all arbitrary arguments sent to '.remove()' are mapped. + * + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.CommandRemove = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.targets = []; + this.collection = []; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.CommandRemove); + + mixitup.CommandRemove.prototype = Object.create(mixitup.Base.prototype); + + mixitup.CommandRemove.prototype.constructor = mixitup.CommandRemove; + + /** + * An object into which all arbitrary arguments sent to '.changeLayout()' are mapped. + * + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.CommandChangeLayout = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.containerClassName = ''; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.CommandChangeLayout); + + mixitup.CommandChangeLayout.prototype = Object.create(mixitup.Base.prototype); + + mixitup.CommandChangeLayout.prototype.constructor = mixitup.CommandChangeLayout; + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + * @param {string} type + * @param {string} selector + * @param {boolean} [live] + * @param {string} [parent] + * An optional string representing the name of the mixer.dom property containing a reference to a parent element. + */ + + mixitup.ControlDefinition = function(type, selector, live, parent) { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.type = type; + this.selector = selector; + this.live = live || false; + this.parent = parent || ''; + + this.callActions('afterConstruct'); + + h.freeze(this); + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.ControlDefinition); + + mixitup.ControlDefinition.prototype = Object.create(mixitup.Base.prototype); + + mixitup.ControlDefinition.prototype.constructor = mixitup.ControlDefinition; + + mixitup.controlDefinitions = []; + + mixitup.controlDefinitions.push(new mixitup.ControlDefinition('multimix', '[data-filter][data-sort]')); + mixitup.controlDefinitions.push(new mixitup.ControlDefinition('filter', '[data-filter]')); + mixitup.controlDefinitions.push(new mixitup.ControlDefinition('sort', '[data-sort]')); + mixitup.controlDefinitions.push(new mixitup.ControlDefinition('toggle', '[data-toggle]')); + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.Control = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.el = null; + this.selector = ''; + this.bound = []; + this.pending = -1; + this.type = ''; + this.status = 'inactive'; // enum: ['inactive', 'active', 'disabled', 'live'] + this.filter = ''; + this.sort = ''; + this.canDisable = false; + this.handler = null; + this.classNames = new mixitup.UiClassNames(); + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.Control); + + mixitup.Control.prototype = Object.create(mixitup.Base.prototype); + + h.extend(mixitup.Control.prototype, + /** @lends mixitup.Control */ + { + constructor: mixitup.Control, + + /** + * @private + * @param {HTMLElement} el + * @param {string} type + * @param {string} selector + */ + + init: function(el, type, selector) { + var self = this; + + this.callActions('beforeInit', arguments); + + self.el = el; + self.type = type; + self.selector = selector; + + if (self.selector) { + self.status = 'live'; + } else { + self.canDisable = typeof self.el.disable === 'boolean'; + + switch (self.type) { + case 'filter': + self.filter = self.el.getAttribute('data-filter'); + + break; + case 'toggle': + self.filter = self.el.getAttribute('data-toggle'); + + break; + case 'sort': + self.sort = self.el.getAttribute('data-sort'); + + break; + case 'multimix': + self.filter = self.el.getAttribute('data-filter'); + self.sort = self.el.getAttribute('data-sort'); + + break; + } + } + + self.bindClick(); + + mixitup.controls.push(self); + + this.callActions('afterInit', arguments); + }, + + /** + * @private + * @param {mixitup.Mixer} mixer + * @return {boolean} + */ + + isBound: function(mixer) { + var self = this, + isBound = false; + + this.callActions('beforeIsBound', arguments); + + isBound = self.bound.indexOf(mixer) > -1; + + return self.callFilters('afterIsBound', isBound, arguments); + }, + + /** + * @private + * @param {mixitup.Mixer} mixer + * @return {void} + */ + + addBinding: function(mixer) { + var self = this; + + this.callActions('beforeAddBinding', arguments); + + if (!self.isBound()) { + self.bound.push(mixer); + } + + this.callActions('afterAddBinding', arguments); + }, + + /** + * @private + * @param {mixitup.Mixer} mixer + * @return {void} + */ + + removeBinding: function(mixer) { + var self = this, + removeIndex = -1; + + this.callActions('beforeRemoveBinding', arguments); + + if ((removeIndex = self.bound.indexOf(mixer)) > -1) { + self.bound.splice(removeIndex, 1); + } + + if (self.bound.length < 1) { + // No bindings exist, unbind event click handlers + + self.unbindClick(); + + // Remove from `mixitup.controls` list + + removeIndex = mixitup.controls.indexOf(self); + + mixitup.controls.splice(removeIndex, 1); + + if (self.status === 'active') { + self.renderStatus(self.el, 'inactive'); + } + } + + this.callActions('afterRemoveBinding', arguments); + }, + + /** + * @private + * @return {void} + */ + + bindClick: function() { + var self = this; + + this.callActions('beforeBindClick', arguments); + + self.handler = function(e) { + self.handleClick(e); + }; + + h.on(self.el, 'click', self.handler); + + this.callActions('afterBindClick', arguments); + }, + + /** + * @private + * @return {void} + */ + + unbindClick: function() { + var self = this; + + this.callActions('beforeUnbindClick', arguments); + + h.off(self.el, 'click', self.handler); + + self.handler = null; + + this.callActions('afterUnbindClick', arguments); + }, + + /** + * @private + * @param {MouseEvent} e + * @return {void} + */ + + handleClick: function(e) { + var self = this, + button = null, + mixer = null, + isActive = false, + returnValue = void(0), + command = {}, + clone = null, + commands = [], + i = -1; + + this.callActions('beforeHandleClick', arguments); + + this.pending = 0; + + mixer = self.bound[0]; + + if (!self.selector) { + button = self.el; + } else { + button = h.closestParent(e.target, mixer.config.selectors.control + self.selector, true, mixer.dom.document); + } + + if (!button) { + self.callActions('afterHandleClick', arguments); + + return; + } + + switch (self.type) { + case 'filter': + command.filter = self.filter || button.getAttribute('data-filter'); + + break; + case 'sort': + command.sort = self.sort || button.getAttribute('data-sort'); + + break; + case 'multimix': + command.filter = self.filter || button.getAttribute('data-filter'); + command.sort = self.sort || button.getAttribute('data-sort'); + + break; + case 'toggle': + command.filter = self.filter || button.getAttribute('data-toggle'); + + if (self.status === 'live') { + isActive = h.hasClass(button, self.classNames.active); + } else { + isActive = self.status === 'active'; + } + + break; + } + + for (i = 0; i < self.bound.length; i++) { + // Create a clone of the command for each bound mixer instance + + clone = new mixitup.CommandMultimix(); + + h.extend(clone, command); + + commands.push(clone); + } + + commands = self.callFilters('commandsHandleClick', commands, arguments); + + self.pending = self.bound.length; + + for (i = 0; mixer = self.bound[i]; i++) { + command = commands[i]; + + if (!command) { + // An extension may set a command null to indicate that the click should not be handled + + continue; + } + + if (!mixer.lastClicked) { + mixer.lastClicked = button; + } + + mixitup.events.fire('mixClick', mixer.dom.container, { + state: mixer.state, + instance: mixer, + originalEvent: e, + control: mixer.lastClicked + }, mixer.dom.document); + + if (typeof mixer.config.callbacks.onMixClick === 'function') { + returnValue = mixer.config.callbacks.onMixClick.call(mixer.lastClicked, mixer.state, e, mixer); + + if (returnValue === false) { + // User has returned `false` from the callback, so do not handle click + + continue; + } + } + + if (self.type === 'toggle') { + isActive ? mixer.toggleOff(command.filter) : mixer.toggleOn(command.filter); + } else { + mixer.multimix(command); + } + } + + this.callActions('afterHandleClick', arguments); + }, + + /** + * @param {object} command + * @param {Array} toggleArray + * @return {void} + */ + + update: function(command, toggleArray) { + var self = this, + actions = new mixitup.CommandMultimix(); + + self.callActions('beforeUpdate', arguments); + + self.pending--; + + self.pending = Math.max(0, self.pending); + + if (self.pending > 0) return; + + if (self.status === 'live') { + // Live control (status unknown) + + self.updateLive(command, toggleArray); + } else { + // Static control + + actions.sort = self.sort; + actions.filter = self.filter; + + self.callFilters('actionsUpdate', actions, arguments); + + self.parseStatusChange(self.el, command, actions, toggleArray); + } + + self.callActions('afterUpdate', arguments); + }, + + /** + * @param {mixitup.CommandMultimix} command + * @param {Array} toggleArray + * @return {void} + */ + + updateLive: function(command, toggleArray) { + var self = this, + controlButtons = null, + actions = null, + button = null, + i = -1; + + self.callActions('beforeUpdateLive', arguments); + + if (!self.el) return; + + controlButtons = self.el.querySelectorAll(self.selector); + + for (i = 0; button = controlButtons[i]; i++) { + actions = new mixitup.CommandMultimix(); + + switch (self.type) { + case 'filter': + actions.filter = button.getAttribute('data-filter'); + + break; + case 'sort': + actions.sort = button.getAttribute('data-sort'); + + break; + case 'multimix': + actions.filter = button.getAttribute('data-filter'); + actions.sort = button.getAttribute('data-sort'); + + break; + case 'toggle': + actions.filter = button.getAttribute('data-toggle'); + + break; + } + + actions = self.callFilters('actionsUpdateLive', actions, arguments); + + self.parseStatusChange(button, command, actions, toggleArray); + } + + self.callActions('afterUpdateLive', arguments); + }, + + /** + * @param {HTMLElement} button + * @param {mixitup.CommandMultimix} command + * @param {mixitup.CommandMultimix} actions + * @param {Array} toggleArray + * @return {void} + */ + + parseStatusChange: function(button, command, actions, toggleArray) { + var self = this, + alias = '', + toggle = '', + i = -1; + + self.callActions('beforeParseStatusChange', arguments); + + switch (self.type) { + case 'filter': + if (command.filter === actions.filter) { + self.renderStatus(button, 'active'); + } else { + self.renderStatus(button, 'inactive'); + } + + break; + case 'multimix': + if (command.sort === actions.sort && command.filter === actions.filter) { + self.renderStatus(button, 'active'); + } else { + self.renderStatus(button, 'inactive'); + } + + break; + case 'sort': + if (command.sort.match(/:asc/g)) { + alias = command.sort.replace(/:asc/g, ''); + } + + if (command.sort === actions.sort || alias === actions.sort) { + self.renderStatus(button, 'active'); + } else { + self.renderStatus(button, 'inactive'); + } + + break; + case 'toggle': + if (toggleArray.length < 1) self.renderStatus(button, 'inactive'); + + if (command.filter === actions.filter) { + self.renderStatus(button, 'active'); + } + + for (i = 0; i < toggleArray.length; i++) { + toggle = toggleArray[i]; + + if (toggle === actions.filter) { + // Button matches one active toggle + + self.renderStatus(button, 'active'); + + break; + } + + self.renderStatus(button, 'inactive'); + } + + break; + } + + self.callActions('afterParseStatusChange', arguments); + }, + + /** + * @param {HTMLElement} button + * @param {string} status + * @return {void} + */ + + renderStatus: function(button, status) { + var self = this; + + self.callActions('beforeRenderStatus', arguments); + + switch (status) { + case 'active': + h.addClass(button, self.classNames.active); + h.removeClass(button, self.classNames.disabled); + + if (self.canDisable) self.el.disabled = false; + + break; + case 'inactive': + h.removeClass(button, self.classNames.active); + h.removeClass(button, self.classNames.disabled); + + if (self.canDisable) self.el.disabled = false; + + break; + case 'disabled': + if (self.canDisable) self.el.disabled = true; + + h.addClass(button, self.classNames.disabled); + h.removeClass(button, self.classNames.active); + + break; + } + + if (self.status !== 'live') { + // Update the control's status propery if not live + + self.status = status; + } + + self.callActions('afterRenderStatus', arguments); + } + }); + + mixitup.controls = []; + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.StyleData = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.x = 0; + this.y = 0; + this.top = 0; + this.right = 0; + this.bottom = 0; + this.left = 0; + this.width = 0; + this.height = 0; + this.marginRight = 0; + this.marginBottom = 0; + this.opacity = 0; + this.scale = new mixitup.TransformData(); + this.translateX = new mixitup.TransformData(); + this.translateY = new mixitup.TransformData(); + this.translateZ = new mixitup.TransformData(); + this.rotateX = new mixitup.TransformData(); + this.rotateY = new mixitup.TransformData(); + this.rotateZ = new mixitup.TransformData(); + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.StyleData); + + mixitup.StyleData.prototype = Object.create(mixitup.Base.prototype); + + mixitup.StyleData.prototype.constructor = mixitup.StyleData; + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.TransformData = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.value = 0; + this.unit = ''; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.TransformData); + + mixitup.TransformData.prototype = Object.create(mixitup.Base.prototype); + + mixitup.TransformData.prototype.constructor = mixitup.TransformData; + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.TransformDefaults = function() { + mixitup.StyleData.apply(this); + + this.callActions('beforeConstruct'); + + this.scale.value = 0.01; + this.scale.unit = ''; + + this.translateX.value = 20; + this.translateX.unit = 'px'; + + this.translateY.value = 20; + this.translateY.unit = 'px'; + + this.translateZ.value = 20; + this.translateZ.unit = 'px'; + + this.rotateX.value = 90; + this.rotateX.unit = 'deg'; + + this.rotateY.value = 90; + this.rotateY.unit = 'deg'; + + this.rotateX.value = 90; + this.rotateX.unit = 'deg'; + + this.rotateZ.value = 180; + this.rotateZ.unit = 'deg'; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.TransformDefaults); + + mixitup.TransformDefaults.prototype = Object.create(mixitup.StyleData.prototype); + + mixitup.TransformDefaults.prototype.constructor = mixitup.TransformDefaults; + + /** + * @private + * @static + * @since 3.0.0 + * @type {mixitup.TransformDefaults} + */ + + mixitup.transformDefaults = new mixitup.TransformDefaults(); + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.EventDetail = function() { + this.state = null; + this.futureState = null; + this.instance = null; + this.originalEvent = null; + }; + + /** + * The `mixitup.Events` class contains all custom events dispatched by MixItUp at various + * points within the lifecycle of a mixer operation. + * + * Each event is analogous to the callback function of the same name defined in + * the `callbacks` configuration object, and is triggered immediately before it. + * + * Events are always triggered from the container element on which MixItUp is instantiated + * upon. + * + * As with any event, registered event handlers receive the event object as a parameter + * which includes a `detail` property containting references to the current `state`, + * the `mixer` instance, and other event-specific properties described below. + * + * @constructor + * @namespace + * @memberof mixitup + * @public + * @since 3.0.0 + */ + + mixitup.Events = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * A custom event triggered immediately after any MixItUp operation is requested + * and before animations have begun. + * + * The `mixStart` event also exposes a `futureState` property via the + * `event.detail` object, which represents the final state of the mixer once + * the requested operation has completed. + * + * @name mixStart + * @memberof mixitup.Events + * @static + * @type {CustomEvent} + */ + + this.mixStart = null; + + /** + * A custom event triggered when a MixItUp operation is requested while another + * operation is in progress, and the animation queue is full, or queueing + * is disabled. + * + * @name mixBusy + * @memberof mixitup.Events + * @static + * @type {CustomEvent} + */ + + this.mixBusy = null; + + /** + * A custom event triggered after any MixItUp operation has completed, and the + * state has been updated. + * + * @name mixEnd + * @memberof mixitup.Events + * @static + * @type {CustomEvent} + */ + + this.mixEnd = null; + + /** + * A custom event triggered whenever a filter operation "fails", i.e. no targets + * could be found matching the requested filter. + * + * @name mixFail + * @memberof mixitup.Events + * @static + * @type {CustomEvent} + */ + + this.mixFail = null; + + /** + * A custom event triggered whenever a MixItUp control is clicked, and before its + * respective operation is requested. + * + * This event also exposes an `originalEvent` property via the `event.detail` + * object, which holds a reference to the original click event. + * + * @name mixClick + * @memberof mixitup.Events + * @static + * @type {CustomEvent} + */ + + this.mixClick = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.Events); + + mixitup.Events.prototype = Object.create(mixitup.Base.prototype); + + mixitup.Events.prototype.constructor = mixitup.Events; + + /** + * @private + * @param {string} eventType + * @param {Element} el + * @param {object} detail + * @param {Document} [doc] + */ + + mixitup.Events.prototype.fire = function(eventType, el, detail, doc) { + var self = this, + event = null, + eventDetail = new mixitup.EventDetail(); + + self.callActions('beforeFire', arguments); + + if (typeof self[eventType] === 'undefined') { + throw new Error('Event type "' + eventType + '" not found.'); + } + + eventDetail.state = new mixitup.State(); + + h.extend(eventDetail.state, detail.state); + + if (detail.futureState) { + eventDetail.futureState = new mixitup.State(); + + h.extend(eventDetail.futureState, detail.futureState); + } + + eventDetail.instance = detail.instance; + + if (detail.originalEvent) { + eventDetail.originalEvent = detail.originalEvent; + } + + event = h.getCustomEvent(eventType, eventDetail, doc); + + self.callFilters('eventFire', event, arguments); + + el.dispatchEvent(event); + }; + + // Asign a singleton instance to `mixitup.events`: + + mixitup.events = new mixitup.Events(); + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.QueueItem = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.args = []; + this.instruction = null; + this.triggerElement = null; + this.deferred = null; + this.isToggling = false; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.QueueItem); + + mixitup.QueueItem.prototype = Object.create(mixitup.Base.prototype); + + mixitup.QueueItem.prototype.constructor = mixitup.QueueItem; + + /** + * The `mixitup.Mixer` class is used to hold discreet, user-configured + * instances of MixItUp on a provided container element. + * + * Mixer instances are returned whenever the `mixitup()` factory function is called, + * which expose a range of methods enabling API-based filtering, sorting, + * insertion, removal and more. + * + * @constructor + * @namespace + * @memberof mixitup + * @public + * @since 3.0.0 + */ + + mixitup.Mixer = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.config = new mixitup.Config(); + + this.id = ''; + + this.isBusy = false; + this.isToggling = false; + this.incPadding = true; + + this.controls = []; + this.targets = []; + this.origOrder = []; + this.cache = {}; + + this.toggleArray = []; + + this.targetsMoved = 0; + this.targetsImmovable = 0; + this.targetsBound = 0; + this.targetsDone = 0; + + this.staggerDuration = 0; + this.effectsIn = null; + this.effectsOut = null; + this.transformIn = []; + this.transformOut = []; + this.queue = []; + + this.state = null; + this.lastOperation = null; + this.lastClicked = null; + this.userCallback = null; + this.userDeferred = null; + + this.dom = new mixitup.MixerDom(); + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.Mixer); + + mixitup.Mixer.prototype = Object.create(mixitup.Base.prototype); + + h.extend(mixitup.Mixer.prototype, + /** @lends mixitup.Mixer */ + { + constructor: mixitup.Mixer, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {HTMLElement} container + * @param {HTMLElement} document + * @param {string} id + * @param {object} [config] + */ + + attach: function(container, document, id, config) { + var self = this, + target = null, + i = -1; + + self.callActions('beforeAttach', arguments); + + self.id = id; + + if (config) { + h.extend(self.config, config, true, true); + } + + self.sanitizeConfig(); + + self.cacheDom(container, document); + + if (self.config.layout.containerClassName) { + h.addClass(self.dom.container, self.config.layout.containerClassName); + } + + if (!mixitup.features.has.transitions) { + self.config.animation.enable = false; + } + + if (typeof window.console === 'undefined') { + self.config.debug.showWarnings = false; + } + + if (self.config.data.uidKey) { + // If the dataset API is in use, force disable controls + + self.config.controls.enable = false; + } + + self.indexTargets(); + + self.state = self.getInitialState(); + + for (i = 0; target = self.lastOperation.toHide[i]; i++) { + target.hide(); + } + + if (self.config.controls.enable) { + self.initControls(); + + self.buildToggleArray(null, self.state); + + self.updateControls({ + filter: self.state.activeFilter, + sort: self.state.activeSort + }); + } + + self.parseEffects(); + + self.callActions('afterAttach', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @return {void} + */ + + sanitizeConfig: function() { + var self = this; + + self.callActions('beforeSanitizeConfig', arguments); + + // Sanitize enum/string config options + + self.config.controls.scope = self.config.controls.scope.toLowerCase().trim(); + self.config.controls.toggleLogic = self.config.controls.toggleLogic.toLowerCase().trim(); + self.config.controls.toggleDefault = self.config.controls.toggleDefault.toLowerCase().trim(); + + self.config.animation.effects = self.config.animation.effects.trim(); + + self.callActions('afterSanitizeConfig', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @return {mixitup.State} + */ + + getInitialState: function() { + var self = this, + state = new mixitup.State(), + operation = new mixitup.Operation(); + + self.callActions('beforeGetInitialState', arguments); + + // Map initial values into a mock state object in order to construct an operation + + state.activeContainerClassName = self.config.layout.containerClassName; + + if (self.config.load.dataset) { + // Dataset API + + if (!self.config.data.uidKey || typeof self.config.data.uidKey !== 'string') { + throw new TypeError(mixitup.messages.errorConfigDataUidKeyNotSet()); + } + + operation.startDataset = operation.newDataset = state.activeDataset = self.config.load.dataset.slice(); + operation.startContainerClassName = operation.newContainerClassName = state.activeContainerClassName; + operation.show = self.targets.slice(); + + state = self.callFilters('stateGetInitialState', state, arguments); + } else { + // DOM API + + state.activeFilter = self.parseFilterArgs([self.config.load.filter]).command; + state.activeSort = self.parseSortArgs([self.config.load.sort]).command; + state.totalTargets = self.targets.length; + + state = self.callFilters('stateGetInitialState', state, arguments); + + if ( + state.activeSort.collection || state.activeSort.attribute || + state.activeSort.order === 'random' || state.activeSort.order === 'desc' + ) { + // Sorting on load + + operation.newSort = state.activeSort; + + self.sortOperation(operation); + + self.printSort(false, operation); + + self.targets = operation.newOrder; + } else { + operation.startOrder = operation.newOrder = self.targets; + } + + operation.startFilter = operation.newFilter = state.activeFilter; + operation.startSort = operation.newSort = state.activeSort; + operation.startContainerClassName = operation.newContainerClassName = state.activeContainerClassName; + + if (operation.newFilter.selector === 'all') { + operation.newFilter.selector = self.config.selectors.target; + } else if (operation.newFilter.selector === 'none') { + operation.newFilter.selector = ''; + } + } + + operation = self.callFilters('operationGetInitialState', operation, [state]); + + self.lastOperation = operation; + + if (operation.newFilter) { + self.filterOperation(operation); + } + + state = self.buildState(operation); + + return state; + }, + + /** + * Caches references of DOM elements neccessary for the mixer's functionality. + * + * @private + * @instance + * @since 3.0.0 + * @param {HTMLElement} el + * @param {HTMLHtmlElement} document + * @return {void} + */ + + cacheDom: function(el, document) { + var self = this; + + self.callActions('beforeCacheDom', arguments); + + self.dom.document = document; + self.dom.body = self.dom.document.querySelector('body'); + self.dom.container = el; + self.dom.parent = el; + + self.callActions('afterCacheDom', arguments); + }, + + /** + * Indexes all child elements of the mixer matching the `selectors.target` + * selector, instantiating a mixitup.Target for each one. + * + * @private + * @instance + * @since 3.0.0 + * @return {void} + */ + + indexTargets: function() { + var self = this, + target = null, + el = null, + dataset = null, + i = -1; + + self.callActions('beforeIndexTargets', arguments); + + self.dom.targets = self.config.layout.allowNestedTargets ? + self.dom.container.querySelectorAll(self.config.selectors.target) : + h.children(self.dom.container, self.config.selectors.target, self.dom.document); + + self.dom.targets = h.arrayFromList(self.dom.targets); + + self.targets = []; + + if ((dataset = self.config.load.dataset) && dataset.length !== self.dom.targets.length) { + throw new Error(mixitup.messages.errorDatasetPrerenderedMismatch()); + } + + if (self.dom.targets.length) { + for (i = 0; el = self.dom.targets[i]; i++) { + target = new mixitup.Target(); + + target.init(el, self, dataset ? dataset[i] : void(0)); + + target.isInDom = true; + + self.targets.push(target); + } + + self.dom.parent = self.dom.targets[0].parentElement === self.dom.container ? + self.dom.container : + self.dom.targets[0].parentElement; + } + + self.origOrder = self.targets; + + self.callActions('afterIndexTargets', arguments); + }, + + initControls: function() { + var self = this, + definition = '', + controlElements = null, + el = null, + parent = null, + delagators = null, + control = null, + i = -1, + j = -1; + + self.callActions('beforeInitControls', arguments); + + switch (self.config.controls.scope) { + case 'local': + parent = self.dom.container; + + break; + case 'global': + parent = self.dom.document; + + break; + default: + throw new Error(mixitup.messages.errorConfigInvalidControlsScope()); + } + + for (i = 0; definition = mixitup.controlDefinitions[i]; i++) { + if (self.config.controls.live || definition.live) { + if (definition.parent) { + delagators = self.dom[definition.parent]; + + if (!delagators || delagators.length < 0) continue; + + if (typeof delagators.length !== 'number') { + delagators = [delagators]; + } + } else { + delagators = [parent]; + } + + for (j = 0; (el = delagators[j]); j++) { + control = self.getControl(el, definition.type, definition.selector); + + self.controls.push(control); + } + } else { + controlElements = parent.querySelectorAll(self.config.selectors.control + definition.selector); + + for (j = 0; (el = controlElements[j]); j++) { + control = self.getControl(el, definition.type, ''); + + if (!control) continue; + + self.controls.push(control); + } + } + } + + self.callActions('afterInitControls', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {HTMLElement} el + * @param {string} type + * @param {string} selector + * @return {mixitup.Control|null} + */ + + getControl: function(el, type, selector) { + var self = this, + control = null, + i = -1; + + self.callActions('beforeGetControl', arguments); + + if (!selector) { + // Static controls only + + for (i = 0; control = mixitup.controls[i]; i++) { + if (control.el === el && control.isBound(self)) { + // Control already bound to this mixer (as another type). + + // NB: This prevents duplicate controls from being registered where a selector + // might collide, eg: "[data-filter]" and "[data-filter][data-sort]" + + return self.callFilters('controlGetControl', null, arguments); + } else if (control.el === el && control.type === type && control.selector === selector) { + // Another mixer is already using this control, add this mixer as a binding + + control.addBinding(self); + + return self.callFilters('controlGetControl', control, arguments); + } + } + } + + // Create new control + + control = new mixitup.Control(); + + control.init(el, type, selector); + + control.classNames.base = h.getClassname(self.config.classNames, type); + control.classNames.active = h.getClassname(self.config.classNames, type, self.config.classNames.modifierActive); + control.classNames.disabled = h.getClassname(self.config.classNames, type, self.config.classNames.modifierDisabled); + + // Add a reference to this mixer as a binding + + control.addBinding(self); + + return self.callFilters('controlGetControl', control, arguments); + }, + + /** + * Creates a compound selector by joining the `toggleArray` value as per the + * defined toggle logic. + * + * @private + * @instance + * @since 3.0.0 + * @return {string} + */ + + getToggleSelector: function() { + var self = this, + delineator = self.config.controls.toggleLogic === 'or' ? ', ' : '', + toggleSelector = ''; + + self.callActions('beforeGetToggleSelector', arguments); + + self.toggleArray = h.clean(self.toggleArray); + + toggleSelector = self.toggleArray.join(delineator); + + if (toggleSelector === '') { + toggleSelector = self.config.controls.toggleDefault; + } + + return self.callFilters('selectorGetToggleSelector', toggleSelector, arguments); + }, + + /** + * Breaks compound selector strings in an array of discreet selectors, + * as per the active `controls.toggleLogic` configuration option. Accepts + * either a dynamic command object, or a state object. + * + * @private + * @instance + * @since 2.0.0 + * @param {object} [command] + * @param {mixitup.State} [state] + * @return {void} + */ + + buildToggleArray: function(command, state) { + var self = this, + activeFilterSelector = ''; + + self.callActions('beforeBuildToggleArray', arguments); + + if (command && command.filter) { + activeFilterSelector = command.filter.selector.replace(/\s/g, ''); + } else if (state) { + activeFilterSelector = state.activeFilter.selector.replace(/\s/g, ''); + } else { + return; + } + + if (activeFilterSelector === self.config.selectors.target || activeFilterSelector === 'all') { + activeFilterSelector = ''; + } + + if (self.config.controls.toggleLogic === 'or') { + self.toggleArray = activeFilterSelector.split(','); + } else { + self.toggleArray = self.splitCompoundSelector(activeFilterSelector); + } + + self.toggleArray = h.clean(self.toggleArray); + + self.callActions('afterBuildToggleArray', arguments); + }, + + /** + * Takes a compound selector (e.g. `.cat-1.cat-2`, `[data-cat="1"][data-cat="2"]`) + * and breaks into its individual selectors. + * + * @private + * @instance + * @since 3.0.0 + * @param {string} compoundSelector + * @return {string[]} + */ + + splitCompoundSelector: function(compoundSelector) { + // Break at a `.` or `[`, capturing the delineator + + var partials = compoundSelector.split(/([\.\[])/g), + toggleArray = [], + selector = '', + i = -1; + + if (partials[0] === '') { + partials.shift(); + } + + for (i = 0; i < partials.length; i++) { + if (i % 2 === 0) { + selector = ''; + } + + selector += partials[i]; + + if (i % 2 !== 0) { + toggleArray.push(selector); + } + } + + return toggleArray; + }, + + /** + * Updates controls to their active/inactive state based on the command or + * current state of the mixer. + * + * @private + * @instance + * @since 2.0.0 + * @param {object} command + * @return {void} + */ + + updateControls: function(command) { + var self = this, + control = null, + output = new mixitup.CommandMultimix(), + i = -1; + + self.callActions('beforeUpdateControls', arguments); + + // Sanitise to defaults + + if (command.filter) { + output.filter = command.filter.selector; + } else { + output.filter = self.state.activeFilter.selector; + } + + if (command.sort) { + output.sort = self.buildSortString(command.sort); + } else { + output.sort = self.buildSortString(self.state.activeSort); + } + + if (output.filter === self.config.selectors.target) { + output.filter = 'all'; + } + + if (output.filter === '') { + output.filter = 'none'; + } + + h.freeze(output); + + for (i = 0; control = self.controls[i]; i++) { + control.update(output, self.toggleArray); + } + + self.callActions('afterUpdateControls', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {mixitup.CommandSort} command + * @return {string} + */ + + buildSortString: function(command) { + var self = this; + var output = ''; + + output += command.sortString; + + if (command.next) { + output += ' ' + self.buildSortString(command.next); + } + + return output; + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {object} command + * @param {Operation} operation + * @return {Promise.} + */ + + insertTargets: function(command, operation) { + var self = this, + nextSibling = null, + insertionIndex = -1, + frag = null, + target = null, + el = null, + i = -1; + + self.callActions('beforeInsertTargets', arguments); + + if (typeof command.index === 'undefined') command.index = 0; + + nextSibling = self.getNextSibling(command.index, command.sibling, command.position); + frag = self.dom.document.createDocumentFragment(); + + if (nextSibling) { + insertionIndex = h.index(nextSibling, self.config.selectors.target); + } else { + insertionIndex = self.targets.length; + } + + if (command.collection) { + for (i = 0; el = command.collection[i]; i++) { + if (self.dom.targets.indexOf(el) > -1) { + throw new Error(mixitup.messages.errorInsertPreexistingElement()); + } + + // Ensure elements are hidden when they are added to the DOM, so they can + // be animated in gracefully + + el.style.display = 'none'; + + frag.appendChild(el); + frag.appendChild(self.dom.document.createTextNode(' ')); + + if (!h.isElement(el, self.dom.document) || !el.matches(self.config.selectors.target)) continue; + + target = new mixitup.Target(); + + target.init(el, self); + + target.isInDom = true; + + self.targets.splice(insertionIndex, 0, target); + + insertionIndex++; + } + + self.dom.parent.insertBefore(frag, nextSibling); + } + + // Since targets have been added, the original order must be updated + + operation.startOrder = self.origOrder = self.targets; + + self.callActions('afterInsertTargets', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Number} [index] + * @param {Element} [sibling] + * @param {string} [position] + * @return {Element} + */ + + getNextSibling: function(index, sibling, position) { + var self = this, + element = null; + + index = Math.max(index, 0); + + if (sibling && position === 'before') { + // Explicit sibling + + element = sibling; + } else if (sibling && position === 'after') { + // Explicit sibling + + element = sibling.nextElementSibling || null; + } else if (self.targets.length > 0 && typeof index !== 'undefined') { + // Index and targets exist + + element = (index < self.targets.length || !self.targets.length) ? + self.targets[index].dom.el : + self.targets[self.targets.length - 1].dom.el.nextElementSibling; + } else if (self.targets.length === 0 && self.dom.parent.children.length > 0) { + // No targets but other siblings + + if (self.config.layout.siblingAfter) { + element = self.config.layout.siblingAfter; + } else if (self.config.layout.siblingBefore) { + element = self.config.layout.siblingBefore.nextElementSibling; + } else { + self.dom.parent.children[0]; + } + } else { + element === null; + } + + return self.callFilters('elementGetNextSibling', element, arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + filterOperation: function(operation) { + var self = this, + testResult = false, + index = -1, + action = '', + target = null, + i = -1; + + self.callActions('beforeFilterOperation', arguments); + + action = operation.newFilter.action; + + for (i = 0; target = operation.newOrder[i]; i++) { + if (operation.newFilter.collection) { + // show via collection + + testResult = operation.newFilter.collection.indexOf(target.dom.el) > -1; + } else { + // show via selector + + if (operation.newFilter.selector === '') { + testResult = false; + } else { + testResult = target.dom.el.matches(operation.newFilter.selector); + } + } + + self.evaluateHideShow(testResult, target, action, operation); + } + + if (operation.toRemove.length) { + for (i = 0; target = operation.show[i]; i++) { + if (operation.toRemove.indexOf(target) > -1) { + // If any shown targets should be removed, move them into the toHide array + + operation.show.splice(i, 1); + + if ((index = operation.toShow.indexOf(target)) > -1) { + operation.toShow.splice(index, 1); + } + + operation.toHide.push(target); + operation.hide.push(target); + + i--; + } + } + } + + operation.matching = operation.show.slice(); + + if (operation.show.length === 0 && operation.newFilter.selector !== '' && self.targets.length !== 0) { + operation.hasFailed = true; + } + + self.callActions('afterFilterOperation', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {boolean} testResult + * @param {Element} target + * @param {string} action + * @param {Operation} operation + * @return {void} + */ + + evaluateHideShow: function(testResult, target, action, operation) { + var self = this, + filteredTestResult = false, + args = Array.prototype.slice.call(arguments, 1); + + filteredTestResult = self.callFilters('testResultEvaluateHideShow', testResult, args); + + self.callActions('beforeEvaluateHideShow', arguments); + + if ( + filteredTestResult === true && action === 'show' || + filteredTestResult === false && action === 'hide' + ) { + operation.show.push(target); + + !target.isShown && operation.toShow.push(target); + } else { + operation.hide.push(target); + + target.isShown && operation.toHide.push(target); + } + + self.callActions('afterEvaluateHideShow', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + sortOperation: function(operation) { + var self = this, + newOrder = [], + target = null, + el = null, + i = -1; + + self.callActions('beforeSortOperation', arguments); + + operation.startOrder = self.targets; + + if (operation.newSort.collection) { + // Sort by collection + + newOrder = []; + + for (i = 0; (el = operation.newSort.collection[i]); i++) { + if (self.dom.targets.indexOf(el) < 0) { + throw new Error(mixitup.messages.errorSortNonExistentElement()); + } + + target = new mixitup.Target(); + + target.init(el, self); + + target.isInDom = true; + + newOrder.push(target); + } + + operation.newOrder = newOrder; + } else if (operation.newSort.order === 'random') { + // Sort random + + operation.newOrder = h.arrayShuffle(operation.startOrder); + } else if (operation.newSort.attribute === '') { + // Sort by default + + operation.newOrder = self.origOrder.slice(); + + if (operation.newSort.order === 'desc') { + operation.newOrder.reverse(); + } + } else { + // Sort by attribute + + operation.newOrder = operation.startOrder.slice(); + + operation.newOrder.sort(function(a, b) { + return self.compare(a, b, operation.newSort); + }); + } + + if (h.isEqualArray(operation.newOrder, operation.startOrder)) { + operation.willSort = false; + } + + self.callActions('afterSortOperation', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {mixitup.Target} a + * @param {mixitup.Target} b + * @param {mixitup.CommandSort} command + * @return {Number} + */ + + compare: function(a, b, command) { + var self = this, + order = command.order, + attrA = self.getAttributeValue(a, command.attribute), + attrB = self.getAttributeValue(b, command.attribute); + + if (isNaN(attrA * 1) || isNaN(attrB * 1)) { + attrA = attrA.toLowerCase(); + attrB = attrB.toLowerCase(); + } else { + attrA = attrA * 1; + attrB = attrB * 1; + } + + if (attrA < attrB) { + return order === 'asc' ? -1 : 1; + } + + if (attrA > attrB) { + return order === 'asc' ? 1 : -1; + } + + if (attrA === attrB && command.next) { + return self.compare(a, b, command.next); + } + + return 0; + }, + + /** + * Reads the values of any data attributes present the provided target element + * which match the current sort command. + * + * @private + * @instance + * @since 3.0.0 + * @param {mixitup.Target} target + * @param {string} [attribute] + * @return {(String|Number)} + */ + + getAttributeValue: function(target, attribute) { + var self = this, + value = ''; + + value = target.dom.el.getAttribute('data-' + attribute); + + if (value === null) { + if (self.config.debug.showWarnings) { + // Encourage users to assign values to all targets to avoid erroneous sorting + // when types are mixed + + console.warn(mixitup.messages.warningInconsistentSortingAttributes({ + attribute: 'data-' + attribute + })); + } + } + + // If an attribute is not present, return 0 as a safety value + + return self.callFilters('valueGetAttributeValue', value || 0, arguments); + }, + + /** + * Inserts elements into the DOM in the appropriate + * order using a document fragment for minimal + * DOM thrashing + * + * @private + * @instance + * @since 2.0.0 + * @param {boolean} isResetting + * @param {Operation} operation + * @return {void} + */ + + printSort: function(isResetting, operation) { + var self = this, + startOrder = isResetting ? operation.newOrder : operation.startOrder, + newOrder = isResetting ? operation.startOrder : operation.newOrder, + nextSibling = startOrder.length ? startOrder[startOrder.length - 1].dom.el.nextElementSibling : null, + frag = window.document.createDocumentFragment(), + whitespace = null, + target = null, + el = null, + i = -1; + + self.callActions('beforePrintSort', arguments); + + // Empty the container + + for (i = 0; target = startOrder[i]; i++) { + el = target.dom.el; + + if (el.style.position === 'absolute') continue; + + h.removeWhitespace(el.previousSibling); + + el.parentElement.removeChild(el); + } + + whitespace = nextSibling ? nextSibling.previousSibling : self.dom.parent.lastChild; + + if (whitespace && whitespace.nodeName === '#text') { + h.removeWhitespace(whitespace); + } + + for (i = 0; target = newOrder[i]; i++) { + // Add targets into a document fragment + + el = target.dom.el; + + if (h.isElement(frag.lastChild)) { + frag.appendChild(window.document.createTextNode(' ')); + } + + frag.appendChild(el); + } + + // Insert the document fragment into the container + // before any other non-target elements + + if (self.dom.parent.firstChild && self.dom.parent.firstChild !== nextSibling) { + frag.insertBefore(window.document.createTextNode(' '), frag.childNodes[0]); + } + + if (nextSibling) { + frag.appendChild(window.document.createTextNode(' ')); + + self.dom.parent.insertBefore(frag, nextSibling); + } else { + self.dom.parent.appendChild(frag); + } + + self.callActions('afterPrintSort', arguments); + }, + + /** + * Parses user-defined sort strings (i.e. `default:asc`) into sort commands objects. + * + * @private + * @instance + * @since 3.0.0 + * @param {string} sortString + * @param {mixitup.CommandSort} command + * @return {mixitup.CommandSort} + */ + + parseSortString: function(sortString, command) { + var self = this, + rules = sortString.split(' '), + current = command, + rule = [], + i = -1; + + // command.sortString = sortString; + + for (i = 0; i < rules.length; i++) { + rule = rules[i].split(':'); + + current.sortString = rules[i]; + current.attribute = h.dashCase(rule[0]); + current.order = rule[1] || 'asc'; + + switch (current.attribute) { + case 'default': + // treat "default" as sorting by no attribute + + current.attribute = ''; + + break; + case 'random': + // treat "random" as an order not an attribute + + current.attribute = ''; + current.order = 'random'; + + break; + } + + if (!current.attribute || current.order === 'random') break; + + if (i < rules.length - 1) { + // Embed reference to the next command + + current.next = new mixitup.CommandSort(); + + h.freeze(current); + + current = current.next; + } + } + + return self.callFilters('commandsParseSort', command, arguments); + }, + + /** + * Parses all effects out of the user-defined `animation.effects` string into + * their respective properties and units. + * + * @private + * @instance + * @since 2.0.0 + * @return {void} + */ + + parseEffects: function() { + var self = this, + transformName = '', + effectsIn = self.config.animation.effectsIn || self.config.animation.effects, + effectsOut = self.config.animation.effectsOut || self.config.animation.effects; + + self.callActions('beforeParseEffects', arguments); + + self.effectsIn = new mixitup.StyleData(); + self.effectsOut = new mixitup.StyleData(); + self.transformIn = []; + self.transformOut = []; + + self.effectsIn.opacity = self.effectsOut.opacity = 1; + + self.parseEffect('fade', effectsIn, self.effectsIn, self.transformIn); + self.parseEffect('fade', effectsOut, self.effectsOut, self.transformOut, true); + + for (transformName in mixitup.transformDefaults) { + if (!(mixitup.transformDefaults[transformName] instanceof mixitup.TransformData)) { + continue; + } + + self.parseEffect(transformName, effectsIn, self.effectsIn, self.transformIn); + self.parseEffect(transformName, effectsOut, self.effectsOut, self.transformOut, true); + } + + self.parseEffect('stagger', effectsIn, self.effectsIn, self.transformIn); + self.parseEffect('stagger', effectsOut, self.effectsOut, self.transformOut, true); + + self.callActions('afterParseEffects', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {string} effectName + * @param {string} effectString + * @param {StyleData} effects + * @param {String[]} transform + * @param {boolean} [isOut] + */ + + parseEffect: function(effectName, effectString, effects, transform, isOut) { + var self = this, + re = /\(([^)]+)\)/, + propIndex = -1, + str = '', + match = [], + val = '', + units = ['%', 'px', 'em', 'rem', 'vh', 'vw', 'deg'], + unit = '', + i = -1; + + self.callActions('beforeParseEffect', arguments); + + if (typeof effectString !== 'string') { + throw new TypeError(mixitup.messages.errorConfigInvalidAnimationEffects()); + } + + if (effectString.indexOf(effectName) < 0) { + // The effect is not present in the effects string + + if (effectName === 'stagger') { + // Reset stagger to 0 + + self.staggerDuration = 0; + } + + return; + } + + // The effect is present + + propIndex = effectString.indexOf(effectName + '('); + + if (propIndex > -1) { + // The effect has a user defined value in parentheses + + // Extract from the first parenthesis to the end of string + + str = effectString.substring(propIndex); + + // Match any number of characters between "(" and ")" + + match = re.exec(str); + + val = match[1]; + } + + switch (effectName) { + case 'fade': + effects.opacity = val ? parseFloat(val) : 0; + + break; + case 'stagger': + self.staggerDuration = val ? parseFloat(val) : 100; + + // TODO: Currently stagger must be applied globally, but + // if seperate values are specified for in/out, this should + // be respected + + break; + default: + // All other effects are transforms following the same structure + + if (isOut && self.config.animation.reverseOut && effectName !== 'scale') { + effects[effectName].value = + (val ? parseFloat(val) : mixitup.transformDefaults[effectName].value) * -1; + } else { + effects[effectName].value = + (val ? parseFloat(val) : mixitup.transformDefaults[effectName].value); + } + + if (val) { + for (i = 0; unit = units[i]; i++) { + if (val.indexOf(unit) > -1) { + effects[effectName].unit = unit; + + break; + } + } + } else { + effects[effectName].unit = mixitup.transformDefaults[effectName].unit; + } + + transform.push( + effectName + + '(' + + effects[effectName].value + + effects[effectName].unit + + ')' + ); + } + + self.callActions('afterParseEffect', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {State} + */ + + buildState: function(operation) { + var self = this, + state = new mixitup.State(), + target = null, + i = -1; + + self.callActions('beforeBuildState', arguments); + + // Map target elements into state arrays. + // the real target objects should never be exposed + + for (i = 0; target = self.targets[i]; i++) { + if (!operation.toRemove.length || operation.toRemove.indexOf(target) < 0) { + state.targets.push(target.dom.el); + } + } + + for (i = 0; target = operation.matching[i]; i++) { + state.matching.push(target.dom.el); + } + + for (i = 0; target = operation.show[i]; i++) { + state.show.push(target.dom.el); + } + + for (i = 0; target = operation.hide[i]; i++) { + if (!operation.toRemove.length || operation.toRemove.indexOf(target) < 0) { + state.hide.push(target.dom.el); + } + } + + state.id = self.id; + state.container = self.dom.container; + state.activeFilter = operation.newFilter; + state.activeSort = operation.newSort; + state.activeDataset = operation.newDataset; + state.activeContainerClassName = operation.newContainerClassName; + state.hasFailed = operation.hasFailed; + state.totalTargets = self.targets.length; + state.totalShow = operation.show.length; + state.totalHide = operation.hide.length; + state.totalMatching = operation.matching.length; + state.triggerElement = operation.triggerElement; + + return self.callFilters('stateBuildState', state, arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {boolean} shouldAnimate + * @param {Operation} operation + * @return {void} + */ + + goMix: function(shouldAnimate, operation) { + var self = this, + deferred = null; + + self.callActions('beforeGoMix', arguments); + + // If the animation duration is set to 0ms, + // or no effects specified, + // or the container is hidden + // then abort animation + + if ( + !self.config.animation.duration || !self.config.animation.effects || !h.isVisible(self.dom.container) + ) { + shouldAnimate = false; + } + + if ( + !operation.toShow.length && + !operation.toHide.length && + !operation.willSort && + !operation.willChangeLayout + ) { + // If nothing to show or hide, and not sorting or + // changing layout + + shouldAnimate = false; + } + + if ( + !operation.startState.show.length && + !operation.show.length + ) { + // If nothing currently shown, nothing to show + + shouldAnimate = false; + } + + mixitup.events.fire('mixStart', self.dom.container, { + state: operation.startState, + futureState: operation.newState, + instance: self + }, self.dom.document); + + if (typeof self.config.callbacks.onMixStart === 'function') { + self.config.callbacks.onMixStart.call( + self.dom.container, + operation.startState, + operation.newState, + self + ); + } + + h.removeClass(self.dom.container, h.getClassname(self.config.classNames, 'container', self.config.classNames.modifierFailed)); + + if (!self.userDeferred) { + // Queue empty, no pending operations + + deferred = self.userDeferred = h.defer(mixitup.libraries); + } else { + // Use existing deferred + + deferred = self.userDeferred; + } + + self.isBusy = true; + + if (!shouldAnimate || !mixitup.features.has.transitions) { + // Abort + + if (self.config.debug.fauxAsync) { + setTimeout(function() { + self.cleanUp(operation); + }, self.config.animation.duration); + } else { + self.cleanUp(operation); + } + + return self.callFilters('promiseGoMix', deferred.promise, arguments); + } + + // If we should animate and the platform supports transitions, go for it + + if (window.pageYOffset !== operation.docState.scrollTop) { + window.scrollTo(operation.docState.scrollLeft, operation.docState.scrollTop); + } + + if (self.config.animation.applyPerspective) { + self.dom.parent.style[mixitup.features.perspectiveProp] = + self.config.animation.perspectiveDistance; + + self.dom.parent.style[mixitup.features.perspectiveOriginProp] = + self.config.animation.perspectiveOrigin; + } + + if ( + self.config.animation.animateResizeContainer && + operation.startHeight !== operation.newHeight && + operation.viewportDeltaY !== operation.startHeight - operation.newHeight + ) { + self.dom.parent.style.height = operation.startHeight + 'px'; + } + + if ( + self.config.animation.animateResizeContainer && + operation.startWidth !== operation.newWidth && + operation.viewportDeltaX !== operation.startWidth - operation.newWidth + ) { + self.dom.parent.style.width = operation.startWidth + 'px'; + } + + if (operation.startHeight === operation.newHeight) { + self.dom.parent.style.height = operation.startHeight + 'px'; + } + + if (operation.startWidth === operation.newWidth) { + self.dom.parent.style.width = operation.startWidth + 'px'; + } + + if (operation.startHeight === operation.newHeight && operation.startWidth === operation.newWidth) { + self.dom.parent.style.overflow = 'hidden'; + } + + requestAnimationFrame(function() { + self.moveTargets(operation); + }); + + return self.callFilters('promiseGoMix', deferred.promise, arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + getStartMixData: function(operation) { + var self = this, + parentStyle = window.getComputedStyle(self.dom.parent), + parentRect = self.dom.parent.getBoundingClientRect(), + target = null, + data = {}, + i = -1, + boxSizing = parentStyle[mixitup.features.boxSizingProp]; + + self.incPadding = (boxSizing === 'border-box'); + + self.callActions('beforeGetStartMixData', arguments); + + for (i = 0; target = operation.show[i]; i++) { + data = target.getPosData(); + + operation.showPosData[i] = { + startPosData: data + }; + } + + for (i = 0; target = operation.toHide[i]; i++) { + data = target.getPosData(); + + operation.toHidePosData[i] = { + startPosData: data + }; + } + + operation.startX = parentRect.left; + operation.startY = parentRect.top; + + operation.startHeight = self.incPadding ? + parentRect.height : + parentRect.height - + parseFloat(parentStyle.paddingTop) - + parseFloat(parentStyle.paddingBottom) - + parseFloat(parentStyle.borderTop) - + parseFloat(parentStyle.borderBottom); + + operation.startWidth = self.incPadding ? + parentRect.width : + parentRect.width - + parseFloat(parentStyle.paddingLeft) - + parseFloat(parentStyle.paddingRight) - + parseFloat(parentStyle.borderLeft) - + parseFloat(parentStyle.borderRight); + + self.callActions('afterGetStartMixData', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + setInter: function(operation) { + var self = this, + target = null, + i = -1; + + self.callActions('beforeSetInter', arguments); + + // Prevent scrollbar flicker on non-inertial scroll platforms by clamping height/width + + if (self.config.animation.clampHeight) { + self.dom.parent.style.height = operation.startHeight + 'px'; + self.dom.parent.style.overflow = 'hidden'; + } + + if (self.config.animation.clampWidth) { + self.dom.parent.style.width = operation.startWidth + 'px'; + self.dom.parent.style.overflow = 'hidden'; + } + + for (i = 0; target = operation.toShow[i]; i++) { + target.show(); + } + + if (operation.willChangeLayout) { + h.removeClass(self.dom.container, operation.startContainerClassName); + h.addClass(self.dom.container, operation.newContainerClassName); + } + + self.callActions('afterSetInter', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + getInterMixData: function(operation) { + var self = this, + target = null, + i = -1; + + self.callActions('beforeGetInterMixData', arguments); + + for (i = 0; target = operation.show[i]; i++) { + operation.showPosData[i].interPosData = target.getPosData(); + } + + for (i = 0; target = operation.toHide[i]; i++) { + operation.toHidePosData[i].interPosData = target.getPosData(); + } + + self.callActions('afterGetInterMixData', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + setFinal: function(operation) { + var self = this, + target = null, + i = -1; + + self.callActions('beforeSetFinal', arguments); + + operation.willSort && self.printSort(false, operation); + + for (i = 0; target = operation.toHide[i]; i++) { + target.hide(); + } + + self.callActions('afterSetFinal', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + getFinalMixData: function(operation) { + var self = this, + parentStyle = null, + parentRect = null, + target = null, + i = -1; + + self.callActions('beforeGetFinalMixData', arguments); + + for (i = 0; target = operation.show[i]; i++) { + operation.showPosData[i].finalPosData = target.getPosData(); + } + + for (i = 0; target = operation.toHide[i]; i++) { + operation.toHidePosData[i].finalPosData = target.getPosData(); + } + + // Remove clamping + + if (self.config.animation.clampHeight || self.config.animation.clampWidth) { + self.dom.parent.style.height = + self.dom.parent.style.width = + self.dom.parent.style.overflow = ''; + } + + if (!self.incPadding) { + parentStyle = window.getComputedStyle(self.dom.parent); + } + + parentRect = self.dom.parent.getBoundingClientRect(); + + operation.newX = parentRect.left; + operation.newY = parentRect.top; + + operation.newHeight = self.incPadding ? + parentRect.height : + parentRect.height - + parseFloat(parentStyle.paddingTop) - + parseFloat(parentStyle.paddingBottom) - + parseFloat(parentStyle.borderTop) - + parseFloat(parentStyle.borderBottom); + + operation.newWidth = self.incPadding ? + parentRect.width : + parentRect.width - + parseFloat(parentStyle.paddingLeft) - + parseFloat(parentStyle.paddingRight) - + parseFloat(parentStyle.borderLeft) - + parseFloat(parentStyle.borderRight); + + operation.viewportDeltaX = operation.docState.viewportWidth - this.dom.document.documentElement.clientWidth; + operation.viewportDeltaY = operation.docState.viewportHeight - this.dom.document.documentElement.clientHeight; + + if (operation.willSort) { + self.printSort(true, operation); + } + + for (i = 0; target = operation.toShow[i]; i++) { + target.hide(); + } + + for (i = 0; target = operation.toHide[i]; i++) { + target.show(); + } + + if (operation.willChangeLayout) { + h.removeClass(self.dom.container, operation.newContainerClassName); + h.addClass(self.dom.container, self.config.layout.containerClassName); + } + + self.callActions('afterGetFinalMixData', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Operation} operation + */ + + getTweenData: function(operation) { + var self = this, + target = null, + posData = null, + effectNames = Object.getOwnPropertyNames(self.effectsIn), + effectName = '', + effect = null, + widthChange = -1, + heightChange = -1, + i = -1, + j = -1; + + self.callActions('beforeGetTweenData', arguments); + + for (i = 0; target = operation.show[i]; i++) { + posData = operation.showPosData[i]; + posData.posIn = new mixitup.StyleData(); + posData.posOut = new mixitup.StyleData(); + posData.tweenData = new mixitup.StyleData(); + + // Process x and y + + if (target.isShown) { + posData.posIn.x = posData.startPosData.x - posData.interPosData.x; + posData.posIn.y = posData.startPosData.y - posData.interPosData.y; + } else { + posData.posIn.x = posData.posIn.y = 0; + } + + posData.posOut.x = posData.finalPosData.x - posData.interPosData.x; + posData.posOut.y = posData.finalPosData.y - posData.interPosData.y; + + // Process opacity + + posData.posIn.opacity = target.isShown ? 1 : self.effectsIn.opacity; + posData.posOut.opacity = 1; + posData.tweenData.opacity = posData.posOut.opacity - posData.posIn.opacity; + + // Adjust x and y if not nudging + + if (!target.isShown && !self.config.animation.nudge) { + posData.posIn.x = posData.posOut.x; + posData.posIn.y = posData.posOut.y; + } + + posData.tweenData.x = posData.posOut.x - posData.posIn.x; + posData.tweenData.y = posData.posOut.y - posData.posIn.y; + + // Process width, height, and margins + + if (self.config.animation.animateResizeTargets) { + posData.posIn.width = posData.startPosData.width; + posData.posIn.height = posData.startPosData.height; + + // "||" Prevents width/height change from including 0 width/height if hiding or showing + + widthChange = (posData.startPosData.width || posData.finalPosData.width) - posData.interPosData.width; + + posData.posIn.marginRight = posData.startPosData.marginRight - widthChange; + + heightChange = (posData.startPosData.height || posData.finalPosData.height) - posData.interPosData.height; + + posData.posIn.marginBottom = posData.startPosData.marginBottom - heightChange; + + posData.posOut.width = posData.finalPosData.width; + posData.posOut.height = posData.finalPosData.height; + + widthChange = (posData.finalPosData.width || posData.startPosData.width) - posData.interPosData.width; + + posData.posOut.marginRight = posData.finalPosData.marginRight - widthChange; + + heightChange = (posData.finalPosData.height || posData.startPosData.height) - posData.interPosData.height; + + posData.posOut.marginBottom = posData.finalPosData.marginBottom - heightChange; + + posData.tweenData.width = posData.posOut.width - posData.posIn.width; + posData.tweenData.height = posData.posOut.height - posData.posIn.height; + posData.tweenData.marginRight = posData.posOut.marginRight - posData.posIn.marginRight; + posData.tweenData.marginBottom = posData.posOut.marginBottom - posData.posIn.marginBottom; + } + + // Process transforms + + for (j = 0; effectName = effectNames[j]; j++) { + effect = self.effectsIn[effectName]; + + if (!(effect instanceof mixitup.TransformData) || !effect.value) continue; + + posData.posIn[effectName].value = effect.value; + posData.posOut[effectName].value = 0; + + posData.tweenData[effectName].value = + posData.posOut[effectName].value - posData.posIn[effectName].value; + + posData.posIn[effectName].unit = + posData.posOut[effectName].unit = + posData.tweenData[effectName].unit = + effect.unit; + } + } + + for (i = 0; target = operation.toHide[i]; i++) { + posData = operation.toHidePosData[i]; + posData.posIn = new mixitup.StyleData(); + posData.posOut = new mixitup.StyleData(); + posData.tweenData = new mixitup.StyleData(); + + // Process x and y + + posData.posIn.x = target.isShown ? posData.startPosData.x - posData.interPosData.x : 0; + posData.posIn.y = target.isShown ? posData.startPosData.y - posData.interPosData.y : 0; + posData.posOut.x = self.config.animation.nudge ? 0 : posData.posIn.x; + posData.posOut.y = self.config.animation.nudge ? 0 : posData.posIn.y; + posData.tweenData.x = posData.posOut.x - posData.posIn.x; + posData.tweenData.y = posData.posOut.y - posData.posIn.y; + + // Process width, height, and margins + + if (self.config.animation.animateResizeTargets) { + posData.posIn.width = posData.startPosData.width; + posData.posIn.height = posData.startPosData.height; + + widthChange = posData.startPosData.width - posData.interPosData.width; + + posData.posIn.marginRight = posData.startPosData.marginRight - widthChange; + + heightChange = posData.startPosData.height - posData.interPosData.height; + + posData.posIn.marginBottom = posData.startPosData.marginBottom - heightChange; + } + + // Process opacity + + posData.posIn.opacity = 1; + posData.posOut.opacity = self.effectsOut.opacity; + posData.tweenData.opacity = posData.posOut.opacity - posData.posIn.opacity; + + // Process transforms + + for (j = 0; effectName = effectNames[j]; j++) { + effect = self.effectsOut[effectName]; + + if (!(effect instanceof mixitup.TransformData) || !effect.value) continue; + + posData.posIn[effectName].value = 0; + posData.posOut[effectName].value = effect.value; + + posData.tweenData[effectName].value = + posData.posOut[effectName].value - posData.posIn[effectName].value; + + posData.posIn[effectName].unit = + posData.posOut[effectName].unit = + posData.tweenData[effectName].unit = + effect.unit; + } + } + + self.callActions('afterGetTweenData', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Operation} operation + * @return {void} + */ + + moveTargets: function(operation) { + var self = this, + target = null, + moveData = null, + posData = null, + statusChange = '', + willTransition = false, + staggerIndex = -1, + i = -1, + checkProgress = self.checkProgress.bind(self); + + self.callActions('beforeMoveTargets', arguments); + + // TODO: this is an extra loop in addition to the calcs + // done in getOperation, could some of this be done there? + + for (i = 0; target = operation.show[i]; i++) { + moveData = new mixitup.IMoveData(); + posData = operation.showPosData[i]; + + statusChange = target.isShown ? 'none' : 'show'; + + willTransition = self.willTransition( + statusChange, + operation.hasEffect, + posData.posIn, + posData.posOut + ); + + if (willTransition) { + // Prevent non-transitioning targets from incrementing the staggerIndex + + staggerIndex++; + } + + target.show(); + + moveData.posIn = posData.posIn; + moveData.posOut = posData.posOut; + moveData.statusChange = statusChange; + moveData.staggerIndex = staggerIndex; + moveData.operation = operation; + moveData.callback = willTransition ? checkProgress : null; + + target.move(moveData); + } + + for (i = 0; target = operation.toHide[i]; i++) { + posData = operation.toHidePosData[i]; + moveData = new mixitup.IMoveData(); + + statusChange = 'hide'; + + willTransition = self.willTransition(statusChange, posData.posIn, posData.posOut); + + moveData.posIn = posData.posIn; + moveData.posOut = posData.posOut; + moveData.statusChange = statusChange; + moveData.staggerIndex = i; + moveData.operation = operation; + moveData.callback = willTransition ? checkProgress : null; + + target.move(moveData); + } + + if (self.config.animation.animateResizeContainer) { + self.dom.parent.style[mixitup.features.transitionProp] = + 'height ' + self.config.animation.duration + 'ms ease, ' + + 'width ' + self.config.animation.duration + 'ms ease '; + + requestAnimationFrame(function() { + if ( + operation.startHeight !== operation.newHeight && + operation.viewportDeltaY !== operation.startHeight - operation.newHeight + ) { + self.dom.parent.style.height = operation.newHeight + 'px'; + } + + if ( + operation.startWidth !== operation.newWidth && + operation.viewportDeltaX !== operation.startWidth - operation.newWidth + ) { + self.dom.parent.style.width = operation.newWidth + 'px'; + } + }); + } + + if (operation.willChangeLayout) { + h.removeClass(self.dom.container, self.config.layout.ContainerClassName); + h.addClass(self.dom.container, operation.newContainerClassName); + } + + self.callActions('afterMoveTargets', arguments); + }, + + /** + * @private + * @instance + * @return {boolean} + */ + + hasEffect: function() { + var self = this, + EFFECTABLES = [ + 'scale', + 'translateX', 'translateY', 'translateZ', + 'rotateX', 'rotateY', 'rotateZ' + ], + effectName = '', + effect = null, + result = false, + value = -1, + i = -1; + + if (self.effectsIn.opacity !== 1) { + return self.callFilters('resultHasEffect', true, arguments); + } + + for (i = 0; effectName = EFFECTABLES[i]; i++) { + effect = self.effectsIn[effectName]; + value = (typeof effect && effect.value !== 'undefined') ? + effect.value : effect; + + if (value !== 0) { + result = true; + + break; + } + } + + return self.callFilters('resultHasEffect', result, arguments); + }, + + /** + * Determines if a target element will transition in + * some fasion and therefore requires binding of + * transitionEnd + * + * @private + * @instance + * @since 3.0.0 + * @param {string} statusChange + * @param {boolean} hasEffect + * @param {StyleData} posIn + * @param {StyleData} posOut + * @return {boolean} + */ + + willTransition: function(statusChange, hasEffect, posIn, posOut) { + var self = this, + result = false; + + if (!h.isVisible(self.dom.container)) { + // If the container is not visible, the transitionEnd + // event will not occur and MixItUp will hang + + result = false; + } else if ( + (statusChange !== 'none' && hasEffect) || + posIn.x !== posOut.x || + posIn.y !== posOut.y + ) { + // If opacity and/or translate will change + + result = true; + } else if (self.config.animation.animateResizeTargets) { + // Check if width, height or margins will change + + result = ( + posIn.width !== posOut.width || + posIn.height !== posOut.height || + posIn.marginRight !== posOut.marginRight || + posIn.marginTop !== posOut.marginTop + ); + } else { + result = false; + } + + return self.callFilters('resultWillTransition', result, arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + checkProgress: function(operation) { + var self = this; + + self.targetsDone++; + + if (self.targetsBound === self.targetsDone) { + self.cleanUp(operation); + } + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Operation} operation + * @return {void} + */ + + cleanUp: function(operation) { + var self = this, + target = null, + whitespaceBefore = null, + whitespaceAfter = null, + nextInQueue = null, + i = -1; + + self.callActions('beforeCleanUp', arguments); + + self.targetsMoved = + self.targetsImmovable = + self.targetsBound = + self.targetsDone = 0; + + for (i = 0; target = operation.show[i]; i++) { + target.cleanUp(); + + target.show(); + } + + for (i = 0; target = operation.toHide[i]; i++) { + target.cleanUp(); + + target.hide(); + } + + if (operation.willSort) { + self.printSort(false, operation); + } + + // Remove any styles applied to the parent container + + self.dom.parent.style[mixitup.features.transitionProp] = + self.dom.parent.style.height = + self.dom.parent.style.width = + self.dom.parent.style.overflow = + self.dom.parent.style[mixitup.features.perspectiveProp] = + self.dom.parent.style[mixitup.features.perspectiveOriginProp] = ''; + + if (operation.willChangeLayout) { + h.removeClass(self.dom.container, operation.startContainerClassName); + h.addClass(self.dom.container, operation.newContainerClassName); + } + + if (operation.toRemove.length) { + for (i = 0; target = self.targets[i]; i++) { + if (operation.toRemove.indexOf(target) > -1) { + if ( + (whitespaceBefore = target.dom.el.previousSibling) && whitespaceBefore.nodeName === '#text' && + (whitespaceAfter = target.dom.el.nextSibling) && whitespaceAfter.nodeName === '#text' + ) { + h.removeWhitespace(whitespaceBefore); + } + + if (!operation.willSort) { + // NB: Sorting will remove targets as a bi-product of `printSort()` + + self.dom.parent.removeChild(target.dom.el); + } + + self.targets.splice(i, 1); + + target.isInDom = false; + + i--; + } + } + + // Since targets have been removed, the original order must be updated + + self.origOrder = self.targets; + } + + if (operation.willSort) { + self.targets = operation.newOrder; + } + + self.state = operation.newState; + self.lastOperation = operation; + + self.dom.targets = self.state.targets; + + // mixEnd + + mixitup.events.fire('mixEnd', self.dom.container, { + state: self.state, + instance: self + }, self.dom.document); + + if (typeof self.config.callbacks.onMixEnd === 'function') { + self.config.callbacks.onMixEnd.call(self.dom.container, self.state, self); + } + + if (operation.hasFailed) { + // mixFail + + mixitup.events.fire('mixFail', self.dom.container, { + state: self.state, + instance: self + }, self.dom.document); + + if (typeof self.config.callbacks.onMixFail === 'function') { + self.config.callbacks.onMixFail.call(self.dom.container, self.state, self); + } + + h.addClass(self.dom.container, h.getClassname(self.config.classNames, 'container', self.config.classNames.modifierFailed)); + } + + // User-defined callback function + + if (typeof self.userCallback === 'function') { + self.userCallback.call(self.dom.container, self.state, self); + } + + if (typeof self.userDeferred.resolve === 'function') { + self.userDeferred.resolve(self.state); + } + + self.userCallback = null; + self.userDeferred = null; + self.lastClicked = null; + self.isToggling = false; + self.isBusy = false; + + if (self.queue.length) { + self.callActions('beforeReadQueueCleanUp', arguments); + + nextInQueue = self.queue.shift(); + + // Update non-public API properties stored in queue + + self.userDeferred = nextInQueue.deferred; + self.isToggling = nextInQueue.isToggling; + self.lastClicked = nextInQueue.triggerElement; + + if (nextInQueue.instruction.command instanceof mixitup.CommandMultimix) { + self.multimix.apply(self, nextInQueue.args); + } else { + self.dataset.apply(self, nextInQueue.args); + } + } + + self.callActions('afterCleanUp', arguments); + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Array<*>} args + * @return {mixitup.UserInstruction} + */ + + parseMultimixArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + arg = null, + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandMultimix(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (arg === null) continue; + + if (typeof arg === 'object') { + h.extend(instruction.command, arg); + } else if (typeof arg === 'boolean') { + instruction.animate = arg; + } else if (typeof arg === 'function') { + instruction.callback = arg; + } + } + + // Coerce arbitrary command arguments into typed command objects + + if (instruction.command.insert && !(instruction.command.insert instanceof mixitup.CommandInsert)) { + instruction.command.insert = self.parseInsertArgs([instruction.command.insert]).command; + } + + if (instruction.command.remove && !(instruction.command.remove instanceof mixitup.CommandRemove)) { + instruction.command.remove = self.parseRemoveArgs([instruction.command.remove]).command; + } + + if (instruction.command.filter && !(instruction.command.filter instanceof mixitup.CommandFilter)) { + instruction.command.filter = self.parseFilterArgs([instruction.command.filter]).command; + } + + if (instruction.command.sort && !(instruction.command.sort instanceof mixitup.CommandSort)) { + instruction.command.sort = self.parseSortArgs([instruction.command.sort]).command; + } + + if (instruction.command.changeLayout && !(instruction.command.changeLayout instanceof mixitup.CommandChangeLayout)) { + instruction.command.changeLayout = self.parseChangeLayoutArgs([instruction.command.changeLayout]).command; + } + + instruction = self.callFilters('instructionParseMultimixArgs', instruction, arguments); + + h.freeze(instruction); + + return instruction; + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Array<*>} args + * @return {mixitup.UserInstruction} + */ + + parseFilterArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + arg = null, + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandFilter(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (typeof arg === 'string') { + // Selector + + instruction.command.selector = arg; + } else if (arg === null) { + instruction.command.collection = []; + } else if (typeof arg === 'object' && h.isElement(arg, self.dom.document)) { + // Single element + + instruction.command.collection = [arg]; + } else if (typeof arg === 'object' && typeof arg.length !== 'undefined') { + // Multiple elements in array, NodeList or jQuery collection + + instruction.command.collection = h.arrayFromList(arg); + } else if (typeof arg === 'object') { + // Filter command + + h.extend(instruction.command, arg); + } else if (typeof arg === 'boolean') { + instruction.animate = arg; + } else if (typeof arg === 'function') { + instruction.callback = arg; + } + } + + if (instruction.command.selector && instruction.command.collection) { + throw new Error(mixitup.messages.errorFilterInvalidArguments()); + } + + instruction = self.callFilters('instructionParseFilterArgs', instruction, arguments); + + h.freeze(instruction); + + return instruction; + }, + + parseSortArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + arg = null, + sortString = '', + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandSort(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (arg === null) continue; + + switch (typeof arg) { + case 'string': + // Sort string + + sortString = arg; + + break; + case 'object': + // Array of element references + + if (arg.length) { + instruction.command.collection = h.arrayFromList(arg); + } + + break; + case 'boolean': + instruction.animate = arg; + + break; + case 'function': + instruction.callback = arg; + + break; + } + } + + if (sortString) { + instruction.command = self.parseSortString(sortString, instruction.command); + } + + instruction = self.callFilters('instructionParseSortArgs', instruction, arguments); + + h.freeze(instruction); + + return instruction; + }, + + /** + * @private + * @instance + * @since 2.0.0 + * @param {Array<*>} args + * @return {mixitup.UserInstruction} + */ + + parseInsertArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + arg = null, + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandInsert(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (arg === null) continue; + + if (typeof arg === 'number') { + // Insert index + + instruction.command.index = arg; + } else if (typeof arg === 'string' && ['before', 'after'].indexOf(arg) > -1) { + // 'before'/'after' + + instruction.command.position = arg; + } else if (typeof arg === 'string') { + // Markup + + instruction.command.collection = + h.arrayFromList(h.createElement(arg).childNodes); + } else if (typeof arg === 'object' && h.isElement(arg, self.dom.document)) { + // Single element + + !instruction.command.collection.length ? + (instruction.command.collection = [arg]) : + (instruction.command.sibling = arg); + } else if (typeof arg === 'object' && arg.length) { + // Multiple elements in array or jQuery collection + + !instruction.command.collection.length ? + (instruction.command.collection = arg) : + instruction.command.sibling = arg[0]; + } else if (typeof arg === 'object' && arg.childNodes && arg.childNodes.length) { + // Document fragment + + !instruction.command.collection.length ? + instruction.command.collection = h.arrayFromList(arg.childNodes) : + instruction.command.sibling = arg.childNodes[0]; + } else if (typeof arg === 'object') { + // Insert command + + h.extend(instruction.command, arg); + } else if (typeof arg === 'boolean') { + instruction.animate = arg; + } else if (typeof arg === 'function') { + instruction.callback = arg; + } + } + + if (instruction.command.index && instruction.command.sibling) { + throw new Error(mixitup.messages.errorInsertInvalidArguments()); + } + + if (!instruction.command.collection.length && self.config.debug.showWarnings) { + console.warn(mixitup.messages.warningInsertNoElements()); + } + + instruction = self.callFilters('instructionParseInsertArgs', instruction, arguments); + + h.freeze(instruction); + + return instruction; + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Array<*>} args + * @return {mixitup.UserInstruction} + */ + + parseRemoveArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + target = null, + arg = null, + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandRemove(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (arg === null) continue; + + switch (typeof arg) { + case 'number': + if (self.targets[arg]) { + instruction.command.targets[0] = self.targets[arg]; + } + + break; + case 'string': + instruction.command.collection = h.arrayFromList(self.dom.parent.querySelectorAll(arg)); + + break; + case 'object': + if (arg && arg.length) { + instruction.command.collection = arg; + } else if (h.isElement(arg, self.dom.document)) { + instruction.command.collection = [arg]; + } else { + // Remove command + + h.extend(instruction.command, arg); + } + + break; + case 'boolean': + instruction.animate = arg; + + break; + case 'function': + instruction.callback = arg; + + break; + } + } + + if (instruction.command.collection.length) { + for (i = 0; target = self.targets[i]; i++) { + if (instruction.command.collection.indexOf(target.dom.el) > -1) { + instruction.command.targets.push(target); + } + } + } + + if (!instruction.command.targets.length && self.config.debug.showWarnings) { + console.warn(mixitup.messages.warningRemoveNoElements()); + } + + h.freeze(instruction); + + return instruction; + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Array<*>} args + * @return {mixitup.UserInstruction} + */ + + parseDatasetArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + arg = null, + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandDataset(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (arg === null) continue; + + switch (typeof arg) { + case 'object': + if (Array.isArray(arg) || typeof arg.length === 'number') { + instruction.command.dataset = arg; + } else { + // Change layout command + + h.extend(instruction.command, arg); + } + + break; + case 'boolean': + instruction.animate = arg; + + break; + case 'function': + instruction.callback = arg; + + break; + } + } + + h.freeze(instruction); + + return instruction; + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Array<*>} args + * @return {mixitup.UserInstruction} + */ + + parseChangeLayoutArgs: function(args) { + var self = this, + instruction = new mixitup.UserInstruction(), + arg = null, + i = -1; + + instruction.animate = self.config.animation.enable; + instruction.command = new mixitup.CommandChangeLayout(); + + for (i = 0; i < args.length; i++) { + arg = args[i]; + + if (arg === null) continue; + + switch (typeof arg) { + case 'string': + instruction.command.containerClassName = arg; + + break; + case 'object': + // Change layout command + + h.extend(instruction.command, arg); + + break; + case 'boolean': + instruction.animate = arg; + + break; + case 'function': + instruction.callback = arg; + + break; + } + } + + h.freeze(instruction); + + return instruction; + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {mixitup.QueueItem} queueItem + * @return {Promise.} + */ + + queueMix: function(queueItem) { + var self = this, + deferred = null, + toggleSelector = ''; + + self.callActions('beforeQueueMix', arguments); + + deferred = h.defer(mixitup.libraries); + + if (self.config.animation.queue && self.queue.length < self.config.animation.queueLimit) { + queueItem.deferred = deferred; + + self.queue.push(queueItem); + + // Keep controls in sync with user interactions. Mixer will catch up as it drains the queue. + + if (self.config.controls.enable) { + if (self.isToggling) { + self.buildToggleArray(queueItem.instruction.command); + + toggleSelector = self.getToggleSelector(); + + self.updateControls({ + filter: { + selector: toggleSelector + } + }); + } else { + self.updateControls(queueItem.instruction.command); + } + } + } else { + if (self.config.debug.showWarnings) { + console.warn(mixitup.messages.warningMultimixInstanceQueueFull()); + } + + deferred.resolve(self.state); + + mixitup.events.fire('mixBusy', self.dom.container, { + state: self.state, + instance: self + }, self.dom.document); + + if (typeof self.config.callbacks.onMixBusy === 'function') { + self.config.callbacks.onMixBusy.call(self.dom.container, self.state, self); + } + } + + return self.callFilters('promiseQueueMix', deferred.promise, arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Array.} newDataset + * @return {Operation} + */ + + getDataOperation: function(newDataset) { + var self = this, + operation = new mixitup.Operation(), + startDataset = []; + + operation = self.callFilters('operationUnmappedGetDataOperation', operation, arguments); + + if (self.dom.targets.length && !(startDataset = (self.state.activeDataset || [])).length) { + throw new Error(mixitup.messages.errorDatasetNotSet()); + } + + operation.id = h.randomHex(); + operation.startState = self.state; + operation.startDataset = startDataset; + operation.newDataset = newDataset.slice(); + + self.diffDatasets(operation); + + operation.startOrder = self.targets; + operation.newOrder = operation.show; + + if (self.config.animation.enable) { + self.getStartMixData(operation); + self.setInter(operation); + + operation.docState = h.getDocumentState(self.dom.document); + + self.getInterMixData(operation); + self.setFinal(operation); + self.getFinalMixData(operation); + + self.parseEffects(); + + operation.hasEffect = self.hasEffect(); + + self.getTweenData(operation); + } + + self.targets = operation.show.slice(); + + operation.newState = self.buildState(operation); + + // NB: Targets to be removed must be included in `self.targets` for removal during clean up, + // but are added after state is built so that state is accurate + + Array.prototype.push.apply(self.targets, operation.toRemove); + + operation = self.callFilters('operationMappedGetDataOperation', operation, arguments); + + return operation; + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {mixitup.Operation} operation + * @return {void} + */ + + diffDatasets: function(operation) { + var self = this, + persistantStartIds = [], + persistantNewIds = [], + insertedTargets = [], + data = null, + target = null, + el = null, + frag = null, + nextEl = null, + uids = {}, + id = '', + i = -1; + + self.callActions('beforeDiffDatasets', arguments); + + for (i = 0; data = operation.newDataset[i]; i++) { + if (typeof (id = data[self.config.data.uidKey]) === 'undefined' || id.toString().length < 1) { + throw new TypeError(mixitup.messages.errorDatasetInvalidUidKey({ + uidKey: self.config.data.uidKey + })); + } + + if (!uids[id]) { + uids[id] = true; + } else { + throw new Error(mixitup.messages.errorDatasetDuplicateUid({ + uid: id + })); + } + + if ((target = self.cache[id]) instanceof mixitup.Target) { + // Already in cache + + if (self.config.data.dirtyCheck && !h.deepEquals(data, target.data)) { + // change detected + + el = target.render(data); + + target.data = data; + + if (el !== target.dom.el) { + // Update target element reference + + if (target.isInDom) { + target.unbindEvents(); + + self.dom.parent.replaceChild(el, target.dom.el); + } + + if (!target.isShown) { + el.style.display = 'none'; + } + + target.dom.el = el; + + if (target.isInDom) { + target.bindEvents(); + } + } + } + + el = target.dom.el; + } else { + // New target + + target = new mixitup.Target(); + + target.init(null, self, data); + + target.hide(); + } + + if (!target.isInDom) { + // Adding to DOM + + if (!frag) { + // Open frag + + frag = self.dom.document.createDocumentFragment(); + } + + if (frag.lastElementChild) { + frag.appendChild(self.dom.document.createTextNode(' ')); + } + + frag.appendChild(target.dom.el); + + target.isInDom = true; + + target.unbindEvents(); + target.bindEvents(); + target.hide(); + + operation.toShow.push(target); + + insertedTargets.push(target); + } else { + // Already in DOM + + nextEl = target.dom.el.nextElementSibling; + + persistantNewIds.push(id); + + if (frag) { + // Close and insert previously opened frag + + if (frag.lastElementChild) { + frag.appendChild(self.dom.document.createTextNode(' ')); + } + + self.insertDatasetFrag(frag, target.dom.el, insertedTargets); + + frag = null; + } + } + + operation.show.push(target); + } + + if (frag) { + // Unclosed frag remaining + + nextEl = nextEl || self.config.layout.siblingAfter; + + if (nextEl) { + frag.appendChild(self.dom.document.createTextNode(' ')); + } + + self.insertDatasetFrag(frag, nextEl, insertedTargets); + } + + for (i = 0; data = operation.startDataset[i]; i++) { + id = data[self.config.data.uidKey]; + + target = self.cache[id]; + + if (operation.show.indexOf(target) < 0) { + // Previously shown but now absent + + operation.hide.push(target); + operation.toHide.push(target); + operation.toRemove.push(target); + } else { + persistantStartIds.push(id); + } + } + + if (!h.isEqualArray(persistantStartIds, persistantNewIds)) { + operation.willSort = true; + } + + self.callActions('afterDiffDatasets', arguments); + }, + + /** + * @private + * @instance + * @since 3.1.5 + * @param {DocumentFragment} frag + * @param {(HTMLElement|null)} nextEl + * @param {Array.} targets + * @return {void} + */ + + insertDatasetFrag: function(frag, nextEl, targets) { + var self = this; + var insertAt = nextEl ? h.arrayFromList(self.dom.parent.children).indexOf(nextEl) : self.targets.length; + + self.dom.parent.insertBefore(frag, nextEl); + + while (targets.length) { + self.targets.splice(insertAt, 0, targets.shift()); + + insertAt++; + } + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {mixitup.CommandSort} sortCommandA + * @param {mixitup.CommandSort} sortCommandB + * @return {boolean} + */ + + willSort: function(sortCommandA, sortCommandB) { + var self = this, + result = false; + + if ( + self.config.behavior.liveSort || + sortCommandA.order === 'random' || + sortCommandA.attribute !== sortCommandB.attribute || + sortCommandA.order !== sortCommandB.order || + sortCommandA.collection !== sortCommandB.collection || + (sortCommandA.next === null && sortCommandB.next) || + (sortCommandA.next && sortCommandB.next === null) + ) { + result = true; + } else if (sortCommandA.next && sortCommandB.next) { + result = self.willSort(sortCommandA.next, sortCommandB.next); + } else { + result = false; + } + + return self.callFilters('resultWillSort', result, arguments); + }, + + /** + * A shorthand method for `.filter('all')`. Shows all targets in the container. + * + * @example + * + * .show() + * + * @example Example: Showing all targets + * + * mixer.show() + * .then(function(state) { + * console.log(state.totalShow === state.totalTargets); // true + * }); + * + * @public + * @instance + * @since 3.0.0 + * @return {Promise.} + */ + + show: function() { + var self = this; + + return self.filter('all'); + }, + + /** + * A shorthand method for `.filter('none')`. Hides all targets in the container. + * + * @example + * + * .hide() + * + * @example Example: Hiding all targets + * + * mixer.hide() + * .then(function(state) { + * console.log(state.totalShow === 0); // true + * console.log(state.totalHide === state.totalTargets); // true + * }); + * + * @public + * @instance + * @since 3.0.0 + * @return {Promise.} + */ + + hide: function() { + var self = this; + + return self.filter('none'); + }, + + /** + * Returns a boolean indicating whether or not a MixItUp operation is + * currently in progress. + * + * @example + * + * .isMixing() + * + * @example Example: Checking the status of a mixer + * + * mixer.sort('random', function() { + * console.log(mixer.isMixing()) // false + * }); + * + * console.log(mixer.isMixing()) // true + * + * @public + * @instance + * @since 2.0.0 + * @return {boolean} + */ + + isMixing: function() { + var self = this; + + return self.isBusy; + }, + + /** + * Filters all targets in the container by a provided selector string, or the values `'all'` + * or `'none'`. Only targets matching the selector will be shown. + * + * @example + * + * .filter(selector [, animate] [, callback]) + * + * @example Example 1: Filtering targets by a class selector + * + * mixer.filter('.category-a') + * .then(function(state) { + * console.log(state.totalShow === containerEl.querySelectorAll('.category-a').length); // true + * }); + * + * @example Example 2: Filtering targets by an attribute selector + * + * mixer.filter('[data-category~="a"]') + * .then(function(state) { + * console.log(state.totalShow === containerEl.querySelectorAll('[data-category~="a"]').length); // true + * }); + * + * @example Example 3: Filtering targets by a compound selector + * + * // Show only those targets with the classes 'category-a' AND 'category-b' + * + * mixer.filter('.category-a.category-c') + * .then(function(state) { + * console.log(state.totalShow === containerEl.querySelectorAll('.category-a.category-c').length); // true + * }); + * + * @example Example 4: Filtering via an element collection + * + * var collection = Array.from(container.querySelectorAll('.mix')); + * + * console.log(collection.length); // 34 + * + * // Filter the collection manually using Array.prototype.filter + * + * var filtered = collection.filter(function(target) { + * return parseInt(target.getAttribute('data-price')) > 10; + * }); + * + * console.log(filtered.length); // 22 + * + * // Pass the filtered collection to MixItUp + * + * mixer.filter(filtered) + * .then(function(state) { + * console.log(state.activeFilter.collection.length === 22); // true + * }); + * + * @public + * @instance + * @since 2.0.0 + * @param {(string|HTMLElement|Array.)} selector + * Any valid CSS selector (i.e. `'.category-a'`), or the values `'all'` or `'none'`. The filter method also accepts a reference to single target element or a collection of target elements to show. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + filter: function() { + var self = this, + instruction = self.parseFilterArgs(arguments); + + return self.multimix({ + filter: instruction.command + }, instruction.animate, instruction.callback); + }, + + /** + * Adds an additional selector to the currently active filter selector, concatenating + * as per the logic defined in `controls.toggleLogic`. + * + * @example + * + * .toggleOn(selector [, animate] [, callback]) + * + * @example Example: Toggling on a filter selector + * + * console.log(mixer.getState().activeFilter.selector); // '.category-a' + * + * mixer.toggleOn('.category-b') + * .then(function(state) { + * console.log(state.activeFilter.selector); // '.category-a, .category-b' + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {string} selector + * Any valid CSS selector (i.e. `'.category-a'`) + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + toggleOn: function() { + var self = this, + instruction = self.parseFilterArgs(arguments), + selector = instruction.command.selector, + toggleSelector = ''; + + self.isToggling = true; + + if (self.toggleArray.indexOf(selector) < 0) { + self.toggleArray.push(selector); + } + + toggleSelector = self.getToggleSelector(); + + return self.multimix({ + filter: toggleSelector + }, instruction.animate, instruction.callback); + }, + + /** + * Removes a selector from the active filter selector. + * + * @example + * + * .toggleOff(selector [, animate] [, callback]) + * + * @example Example: Toggling off a filter selector + * + * console.log(mixer.getState().activeFilter.selector); // '.category-a, .category-b' + * + * mixer.toggleOff('.category-b') + * .then(function(state) { + * console.log(state.activeFilter.selector); // '.category-a' + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {string} selector + * Any valid CSS selector (i.e. `'.category-a'`) + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + toggleOff: function() { + var self = this, + instruction = self.parseFilterArgs(arguments), + selector = instruction.command.selector, + selectorIndex = self.toggleArray.indexOf(selector), + toggleSelector = ''; + + self.isToggling = true; + + if (selectorIndex > -1) { + self.toggleArray.splice(selectorIndex, 1); + } + + toggleSelector = self.getToggleSelector(); + + return self.multimix({ + filter: toggleSelector + }, instruction.animate, instruction.callback); + }, + + /** + * Sorts all targets in the container according to a provided sort string. + * + * @example + * + * .sort(sortString [, animate] [, callback]) + * + * @example Example 1: Sorting by the default DOM order + * + * // Reverse the default order of the targets + * + * mixer.sort('default:desc') + * .then(function(state) { + * console.log(state.activeSort.attribute === 'default'); // true + * console.log(state.activeSort.order === 'desc'); // true + * }); + * + * @example Example 2: Sorting by a custom data-attribute + * + * // Sort the targets by the value of a `data-published-date` attribute + * + * mixer.sort('published-date:asc') + * .then(function(state) { + * console.log(state.activeSort.attribute === 'published-date'); // true + * console.log(state.activeSort.order === 'asc'); // true + * }); + * + * @example Example 3: Sorting by multiple attributes + * + * // Sort the targets by the value of a `data-published-date` attribute, then by `data-title` + * + * mixer.sort('published-date:desc data-title:asc') + * .then(function(state) { + * console.log(state.activeSort.attribute === 'published-date'); // true + * console.log(state.activeSort.order === 'desc'); // true + * + * console.log(state.activeSort.next.attribute === 'title'); // true + * console.log(state.activeSort.next.order === 'asc'); // true + * }); + * + * @example Example 4: Sorting by random + * + * mixer.sort('random') + * .then(function(state) { + * console.log(state.activeSort.order === 'random') // true + * }); + * + * @example Example 5: Sorting via an element collection + * + * var collection = Array.from(container.querySelectorAll('.mix')); + * + * // Swap the position of two elements in the collection: + * + * var temp = collection[1]; + * + * collection[1] = collection[0]; + * collection[0] = temp; + * + * // Pass the sorted collection to MixItUp + * + * mixer.sort(collection) + * .then(function(state) { + * console.log(state.targets[0] === collection[0]); // true + * }); + * + * @public + * @instance + * @since 2.0.0 + * @param {(string|Array.)} sortString + * A valid sort string (e.g. `'default'`, `'published-date:asc'`, or `'random'`). The sort method also accepts an array of all target elements in a user-defined order. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + sort: function() { + var self = this, + instruction = self.parseSortArgs(arguments); + + return self.multimix({ + sort: instruction.command + }, instruction.animate, instruction.callback); + }, + + /** + * Changes the layout of the container by adding, removing or updating a + * layout-specific class name. If `animation.animateResizetargets` is + * enabled, MixItUp will attempt to gracefully animate the width, height, + * and position of targets between layout states. + * + * @example + * + * .changeLayout(containerClassName [, animate] [, callback]) + * + * @example Example 1: Adding a new class name to the container + * + * mixer.changeLayout('container-list') + * .then(function(state) { + * console.log(state.activeContainerClass === 'container-list'); // true + * }); + * + * @example Example 2: Removing a previously added class name from the container + * + * mixer.changeLayout('') + * .then(function(state) { + * console.log(state.activeContainerClass === ''); // true + * }); + * + * @public + * @instance + * @since 2.0.0 + * @param {string} containerClassName + * A layout-specific class name to add to the container. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + changeLayout: function() { + var self = this, + instruction = self.parseChangeLayoutArgs(arguments); + + return self.multimix({ + changeLayout: instruction.command + }, instruction.animate, instruction.callback); + }, + + /** + * Updates the contents and order of the container to reflect the provided dataset, + * if the dataset API is in use. + * + * The dataset API is designed for use in API-driven JavaScript applications, and + * can be used instead of DOM-based methods such as `.filter()`, `.sort()`, + * `.insert()`, etc. When used, insertion, removal, sorting and pagination can be + * achieved purely via changes to your data model, without the uglyness of having + * to interact with or query the DOM directly. + * + * @example + * + * .dataset(dataset [, animate] [, callback]) + * + * @example Example 1: Rendering a dataset + * + * var myDataset = [ + * {id: 1, ...}, + * {id: 2, ...}, + * {id: 3, ...} + * ]; + * + * mixer.dataset(myDataset) + * .then(function(state) { + * console.log(state.totalShow === 3); // true + * }); + * + * @example Example 2: Sorting a dataset + * + * // Create a new dataset in reverse order + * + * var newDataset = myDataset.slice().reverse(); + * + * mixer.dataset(newDataset) + * .then(function(state) { + * console.log(state.activeDataset[0] === myDataset[2]); // true + * }); + * + * @example Example 3: Removing an item from the dataset + * + * console.log(myDataset.length); // 3 + * + * // Create a new dataset with the last item removed. + * + * var newDataset = myDataset.slice().pop(); + * + * mixer.dataset(newDataset) + * .then(function(state) { + * console.log(state.totalShow === 2); // true + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {Array.} dataset + * An array of objects, each one representing the underlying data model of a target to be rendered. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + dataset: function() { + var self = this, + instruction = self.parseDatasetArgs(arguments), + operation = null, + queueItem = null, + animate = false; + + self.callActions('beforeDataset', arguments); + + if (!self.isBusy) { + if (instruction.callback) self.userCallback = instruction.callback; + + animate = (instruction.animate ^ self.config.animation.enable) ? instruction.animate : self.config.animation.enable; + + operation = self.getDataOperation(instruction.command.dataset); + + return self.goMix(animate, operation); + } else { + queueItem = new mixitup.QueueItem(); + + queueItem.args = arguments; + queueItem.instruction = instruction; + + return self.queueMix(queueItem); + } + }, + + /** + * Performs simultaneous `filter`, `sort`, `insert`, `remove` and `changeLayout` + * operations as requested. + * + * @example + * + * .multimix(multimixCommand [, animate] [, callback]) + * + * @example Example 1: Performing simultaneous filtering and sorting + * + * mixer.multimix({ + * filter: '.category-b', + * sort: 'published-date:desc' + * }) + * .then(function(state) { + * console.log(state.activeFilter.selector === '.category-b'); // true + * console.log(state.activeSort.attribute === 'published-date'); // true + * }); + * + * @example Example 2: Performing simultaneous sorting, insertion, and removal + * + * console.log(mixer.getState().totalShow); // 6 + * + * // NB: When inserting via `multimix()`, an object should be provided as the value + * // for the `insert` portion of the command, allowing for a collection of elements + * // and an insertion index to be specified. + * + * mixer.multimix({ + * sort: 'published-date:desc', // Sort the container, including any new elements + * insert: { + * collection: [newElementReferenceA, newElementReferenceB], // Add 2 new elements at index 5 + * index: 5 + * }, + * remove: existingElementReference // Remove 1 existing element + * }) + * .then(function(state) { + * console.log(state.activeSort.attribute === 'published-date'); // true + * console.log(state.totalShow === 7); // true + * }); + * + * @public + * @instance + * @since 2.0.0 + * @param {object} multimixCommand + * An object containing one or more things to do + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + multimix: function() { + var self = this, + operation = null, + animate = false, + queueItem = null, + instruction = self.parseMultimixArgs(arguments); + + self.callActions('beforeMultimix', arguments); + + if (!self.isBusy) { + operation = self.getOperation(instruction.command); + + if (self.config.controls.enable) { + // Update controls for API calls + + if (instruction.command.filter && !self.isToggling) { + // As we are not toggling, reset the toggle array + // so new filter overrides existing toggles + + self.toggleArray.length = 0; + self.buildToggleArray(operation.command); + } + + if (self.queue.length < 1) { + self.updateControls(operation.command); + } + } + + if (instruction.callback) self.userCallback = instruction.callback; + + // Always allow the instruction to override the instance setting + + animate = (instruction.animate ^ self.config.animation.enable) ? + instruction.animate : + self.config.animation.enable; + + self.callFilters('operationMultimix', operation, arguments); + + return self.goMix(animate, operation); + } else { + queueItem = new mixitup.QueueItem(); + + queueItem.args = arguments; + queueItem.instruction = instruction; + queueItem.triggerElement = self.lastClicked; + queueItem.isToggling = self.isToggling; + + return self.queueMix(queueItem); + } + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {object} multimixCommand + * @param {boolean} [isPreFetch] + * An optional boolean indicating that the operation is being pre-fetched for execution at a later time. + * @return {Operation|null} + */ + + getOperation: function(multimixCommand) { + var self = this, + sortCommand = multimixCommand.sort, + filterCommand = multimixCommand.filter, + changeLayoutCommand = multimixCommand.changeLayout, + removeCommand = multimixCommand.remove, + insertCommand = multimixCommand.insert, + operation = new mixitup.Operation(); + + operation = self.callFilters('operationUnmappedGetOperation', operation, arguments); + + operation.id = h.randomHex(); + operation.command = multimixCommand; + operation.startState = self.state; + operation.triggerElement = self.lastClicked; + + if (self.isBusy) { + if (self.config.debug.showWarnings) { + console.warn(mixitup.messages.warningGetOperationInstanceBusy()); + } + + return null; + } + + if (insertCommand) { + self.insertTargets(insertCommand, operation); + } + + if (removeCommand) { + operation.toRemove = removeCommand.targets; + } + + operation.startSort = operation.newSort = operation.startState.activeSort; + operation.startOrder = operation.newOrder = self.targets; + + if (sortCommand) { + operation.startSort = operation.startState.activeSort; + operation.newSort = sortCommand; + + operation.willSort = self.willSort(sortCommand, operation.startState.activeSort); + + if (operation.willSort) { + self.sortOperation(operation); + } + } + + operation.startFilter = operation.startState.activeFilter; + + if (filterCommand) { + operation.newFilter = filterCommand; + } else { + operation.newFilter = h.extend(new mixitup.CommandFilter(), operation.startFilter); + } + + if (operation.newFilter.selector === 'all') { + operation.newFilter.selector = self.config.selectors.target; + } else if (operation.newFilter.selector === 'none') { + operation.newFilter.selector = ''; + } + + self.filterOperation(operation); + + operation.startContainerClassName = operation.startState.activeContainerClassName; + + if (changeLayoutCommand) { + operation.newContainerClassName = changeLayoutCommand.containerClassName; + + if (operation.newContainerClassName !== operation.startContainerClassName) { + operation.willChangeLayout = true; + } + } else { + operation.newContainerClassName = operation.startContainerClassName; + } + + if (self.config.animation.enable) { + // Populate the operation's position data + + self.getStartMixData(operation); + self.setInter(operation); + + operation.docState = h.getDocumentState(self.dom.document); + + self.getInterMixData(operation); + self.setFinal(operation); + self.getFinalMixData(operation); + + self.parseEffects(); + + operation.hasEffect = self.hasEffect(); + + self.getTweenData(operation); + } + + if (operation.willSort) { + self.targets = operation.newOrder; + } + + operation.newState = self.buildState(operation); + + return self.callFilters('operationMappedGetOperation', operation, arguments); + }, + + /** + * Renders a previously created operation at a specific point in its path, as + * determined by a multiplier between 0 and 1. + * + * @example + * .tween(operation, multiplier) + * + * @private + * @instance + * @since 3.0.0 + * @param {mixitup.Operation} operation + * An operation object created via the `getOperation` method + * + * @param {Float} multiplier + * Any number between 0 and 1 representing the percentage complete of the operation + * @return {void} + */ + + tween: function(operation, multiplier) { + var target = null, + posData = null, + toHideIndex = -1, + i = -1; + + multiplier = Math.min(multiplier, 1); + multiplier = Math.max(multiplier, 0); + + for (i = 0; target = operation.show[i]; i++) { + posData = operation.showPosData[i]; + + target.applyTween(posData, multiplier); + } + + for (i = 0; target = operation.hide[i]; i++) { + if (target.isShown) { + target.hide(); + } + + if ((toHideIndex = operation.toHide.indexOf(target)) > -1) { + posData = operation.toHidePosData[toHideIndex]; + + if (!target.isShown) { + target.show(); + } + + target.applyTween(posData, multiplier); + } + } + }, + + /** + * Inserts one or more new target elements into the container at a specified + * index. + * + * To be indexed as targets, new elements must match the `selectors.target` + * selector (`'.mix'` by default). + * + * @example + * + * .insert(newElements [, index] [, animate], [, callback]) + * + * @example Example 1: Inserting a single element via reference + * + * console.log(mixer.getState().totalShow); // 0 + * + * // Create a new element + * + * var newElement = document.createElement('div'); + * newElement.classList.add('mix'); + * + * mixer.insert(newElement) + * .then(function(state) { + * console.log(state.totalShow === 1); // true + * }); + * + * @example Example 2: Inserting a single element via HTML string + * + * console.log(mixer.getState().totalShow); // 1 + * + * // Create a new element via reference + * + * var newElementHtml = '<div class="mix"></div>'; + * + * // Create and insert the new element at index 1 + * + * mixer.insert(newElementHtml, 1) + * .then(function(state) { + * console.log(state.totalShow === 2); // true + * console.log(state.show[1].outerHTML === newElementHtml); // true + * }); + * + * @example Example 3: Inserting multiple elements via reference + * + * console.log(mixer.getState().totalShow); // 2 + * + * // Create an array of new elements to insert. + * + * var newElement1 = document.createElement('div'); + * var newElement2 = document.createElement('div'); + * + * newElement1.classList.add('mix'); + * newElement2.classList.add('mix'); + * + * var newElementsCollection = [newElement1, newElement2]; + * + * // Insert the new elements starting at index 1 + * + * mixer.insert(newElementsCollection, 1) + * .then(function(state) { + * console.log(state.totalShow === 4); // true + * console.log(state.show[1] === newElement1); // true + * console.log(state.show[2] === newElement2); // true + * }); + * + * @example Example 4: Inserting a jQuery collection object containing one or more elements + * + * console.log(mixer.getState().totalShow); // 4 + * + * var $newElement = $('<div class="mix"></div>'); + * + * // Insert the new elements starting at index 3 + * + * mixer.insert($newElement, 3) + * .then(function(state) { + * console.log(state.totalShow === 5); // true + * console.log(state.show[3] === $newElement[0]); // true + * }); + * + * @public + * @instance + * @since 2.0.0 + * @param {(HTMLElement|Array.|string)} newElements + * A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element. + * @param {number} index=0 + * The index at which to insert the new element(s). `0` by default. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + insert: function() { + var self = this, + args = self.parseInsertArgs(arguments); + + return self.multimix({ + insert: args.command + }, args.animate, args.callback); + }, + + /** + * Inserts one or more new elements before a provided reference element. + * + * @example + * + * .insertBefore(newElements, referenceElement [, animate] [, callback]) + * + * @example Example: Inserting a new element before a reference element + * + * // An existing reference element is chosen at index 2 + * + * var referenceElement = mixer.getState().show[2]; + * + * // Create a new element + * + * var newElement = document.createElement('div'); + * newElement.classList.add('mix'); + * + * mixer.insertBefore(newElement, referenceElement) + * .then(function(state) { + * // The new element is inserted into the container at index 2, before the reference element + * + * console.log(state.show[2] === newElement); // true + * + * // The reference element is now at index 3 + * + * console.log(state.show[3] === referenceElement); // true + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {(HTMLElement|Array.|string)} newElements + * A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element. + * @param {HTMLElement} referenceElement + * A reference to an existing element in the container to insert new elements before. + *@param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + insertBefore: function() { + var self = this, + args = self.parseInsertArgs(arguments); + + return self.insert(args.command.collection, 'before', args.command.sibling, args.animate, args.callback); + }, + + /** + * Inserts one or more new elements after a provided reference element. + * + * @example + * + * .insertAfter(newElements, referenceElement [, animate] [, callback]) + * + * @example Example: Inserting a new element after a reference element + * + * // An existing reference element is chosen at index 2 + * + * var referenceElement = mixer.getState().show[2]; + * + * // Create a new element + * + * var newElement = document.createElement('div'); + * newElement.classList.add('mix'); + * + * mixer.insertAfter(newElement, referenceElement) + * .then(function(state) { + * // The new element is inserted into the container at index 3, after the reference element + * + * console.log(state.show[3] === newElement); // true + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {(HTMLElement|Array.|string)} newElements + * A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element. + * @param {HTMLElement} referenceElement + * A reference to an existing element in the container to insert new elements after. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + insertAfter: function() { + var self = this, + args = self.parseInsertArgs(arguments); + + return self.insert(args.command.collection, 'after', args.command.sibling, args.animate, args.callback); + }, + + /** + * Inserts one or more new elements into the container before all existing targets. + * + * @example + * + * .prepend(newElements [,animate] [,callback]) + * + * @example Example: Prepending a new element + * + * // Create a new element + * + * var newElement = document.createElement('div'); + * newElement.classList.add('mix'); + * + * // Insert the element into the container + * + * mixer.prepend(newElement) + * .then(function(state) { + * console.log(state.show[0] === newElement); // true + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {(HTMLElement|Array.|string)} newElements + * A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + prepend: function() { + var self = this, + args = self.parseInsertArgs(arguments); + + return self.insert(0, args.command.collection, args.animate, args.callback); + }, + + /** + * Inserts one or more new elements into the container after all existing targets. + * + * @example + * + * .append(newElements [,animate] [,callback]) + * + * @example Example: Appending a new element + * + * // Create a new element + * + * var newElement = document.createElement('div'); + * newElement.classList.add('mix'); + * + * // Insert the element into the container + * + * mixer.append(newElement) + * .then(function(state) { + * console.log(state.show[state.show.length - 1] === newElement); // true + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {(HTMLElement|Array.|string)} newElements + * A reference to a single element to insert, an array-like collection of elements, or an HTML string representing a single element. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + append: function() { + var self = this, + args = self.parseInsertArgs(arguments); + + return self.insert(self.state.totalTargets, args.command.collection, args.animate, args.callback); + }, + + /** + * Removes one or more existing target elements from the container. + * + * @example + * + * .remove(elements [, animate] [, callback]) + * + * @example Example 1: Removing an element by reference + * + * var elementToRemove = containerEl.firstElementChild; + * + * mixer.remove(elementToRemove) + * .then(function(state) { + * console.log(state.targets.indexOf(elementToRemove) === -1); // true + * }); + * + * @example Example 2: Removing a collection of elements by reference + * + * var elementsToRemove = containerEl.querySelectorAll('.category-a'); + * + * console.log(elementsToRemove.length) // 3 + * + * mixer.remove(elementsToRemove) + * .then(function() { + * console.log(containerEl.querySelectorAll('.category-a').length); // 0 + * }); + * + * @example Example 3: Removing one or more elements by selector + * + * mixer.remove('.category-a') + * .then(function() { + * console.log(containerEl.querySelectorAll('.category-a').length); // 0 + * }); + * + * @example Example 4: Removing an element by index + * + * console.log(mixer.getState.totalShow); // 4 + * + * // Remove the element at index 3 + * + * mixer.remove(3) + * .then(function(state) { + * console.log(state.totalShow); // 3 + * console.log(state.show[3]); // undefined + * }); + * + * + * @public + * @instance + * @since 3.0.0 + * @param {(HTMLElement|Array.|string|number)} elements + * A reference to a single element to remove, an array-like collection of elements, a selector string, or the index of an element to remove. + * @param {boolean} [animate=true] + * An optional boolean dictating whether the operation should animate, or occur syncronously with no animation. `true` by default. + * @param {function} [callback=null] + * An optional callback function to be invoked after the operation has completed. + * @return {Promise.} + * A promise resolving with the current state object. + */ + + remove: function() { + var self = this, + args = self.parseRemoveArgs(arguments); + + return self.multimix({ + remove: args.command + }, args.animate, args.callback); + }, + + /** + * Retrieves the the value of any property or sub-object within the current + * mixitup configuration, or the whole configuration object. + * + * @example + * + * .getConfig([stringKey]) + * + * @example Example 1: retrieve the entire configuration object + * + * var config = mixer.getConfig(); // Config { ... } + * + * @example Example 2: retrieve a named sub-object of configuration object + * + * var animation = mixer.getConfig('animation'); // ConfigAnimation { ... } + * + * @example Example 3: retrieve a value of configuration object via a dot-notation string key + * + * var effects = mixer.getConfig('animation.effects'); // 'fade scale' + * + * @public + * @instance + * @since 2.0.0 + * @param {string} [stringKey] A "dot-notation" string key + * @return {*} + */ + + getConfig: function(stringKey) { + var self = this, + value = null; + + if (!stringKey) { + value = self.config; + } else { + value = h.getProperty(self.config, stringKey); + } + + return self.callFilters('valueGetConfig', value, arguments); + }, + + /** + * Updates the configuration of the mixer, after it has been instantiated. + * + * See the Configuration Object documentation for a full list of avilable + * configuration options. + * + * @example + * + * .configure(config) + * + * @example Example 1: Updating animation options + * + * mixer.configure({ + * animation: { + * effects: 'fade translateX(-100%)', + * duration: 300 + * } + * }); + * + * @example Example 2: Removing a callback after it has been set + * + * var mixer; + * + * function handleMixEndOnce() { + * // Do something .. + * + * // Then nullify the callback + * + * mixer.configure({ + * callbacks: { + * onMixEnd: null + * } + * }); + * }; + * + * // Instantiate a mixer with a callback defined + * + * mixer = mixitup(containerEl, { + * callbacks: { + * onMixEnd: handleMixEndOnce + * } + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {object} config + * An object containing one of more configuration options. + * @return {void} + */ + + configure: function(config) { + var self = this; + + self.callActions('beforeConfigure', arguments); + + h.extend(self.config, config, true, true); + + self.callActions('afterConfigure', arguments); + }, + + /** + * Returns an object containing information about the current state of the + * mixer. See the State Object documentation for more information. + * + * NB: State objects are immutable and should therefore be regenerated + * after any operation. + * + * @example + * + * .getState(); + * + * @example Example: Retrieving a state object + * + * var state = mixer.getState(); + * + * console.log(state.totalShow + 'targets are currently shown'); + * + * @public + * @instance + * @since 2.0.0 + * @return {mixitup.State} An object reflecting the current state of the mixer. + */ + + getState: function() { + var self = this, + state = null; + + state = new mixitup.State(); + + h.extend(state, self.state); + + h.freeze(state); + + return self.callFilters('stateGetState', state, arguments); + }, + + /** + * Forces the re-indexing all targets within the container. + * + * This should only be used if some other piece of code in your application + * has manipulated the contents of your container, which should be avoided. + * + * If you need to add or remove target elements from the container, use + * the built-in `.insert()` or `.remove()` methods, and MixItUp will keep + * itself up to date. + * + * @example + * + * .forceRefresh() + * + * @example Example: Force refreshing the mixer after external DOM manipulation + * + * console.log(mixer.getState().totalShow); // 3 + * + * // An element is removed from the container via some external DOM manipulation code: + * + * containerEl.removeChild(containerEl.firstElementChild); + * + * // The mixer does not know that the number of targets has changed: + * + * console.log(mixer.getState().totalShow); // 3 + * + * mixer.forceRefresh(); + * + * // After forceRefresh, the mixer is in sync again: + * + * console.log(mixer.getState().totalShow); // 2 + * + * @public + * @instance + * @since 2.1.2 + * @return {void} + */ + + forceRefresh: function() { + var self = this; + + self.indexTargets(); + }, + + /** + * Forces the re-rendering of all targets when using the Dataset API. + * + * By default, targets are only re-rendered when `data.dirtyCheck` is + * enabled, and an item's data has changed when `dataset()` is called. + * + * The `forceRender()` method allows for the re-rendering of all targets + * in response to some arbitrary event, such as the changing of the target + * render function. + * + * Targets are rendered against their existing data. + * + * @example + * + * .forceRender() + * + * @example Example: Force render targets after changing the target render function + * + * console.log(container.innerHTML); // ... <span class="mix">Foo</span> ... + * + * mixer.configure({ + * render: { + * target: (item) => `<a href="/${item.slug}/" class="mix">${item.title}</a>` + * } + * }); + * + * mixer.forceRender(); + * + * console.log(container.innerHTML); // ... <a href="/foo/" class="mix">Foo</a> ... + * + * @public + * @instance + * @since 3.2.1 + * @return {void} + */ + + forceRender: function() { + var self = this, + target = null, + el = null, + id = ''; + + for (id in self.cache) { + target = self.cache[id]; + + el = target.render(target.data); + + if (el !== target.dom.el) { + // Update target element reference + + if (target.isInDom) { + target.unbindEvents(); + + self.dom.parent.replaceChild(el, target.dom.el); + } + + if (!target.isShown) { + el.style.display = 'none'; + } + + target.dom.el = el; + + if (target.isInDom) { + target.bindEvents(); + } + } + } + + self.state = self.buildState(self.lastOperation); + }, + + /** + * Removes mixitup functionality from the container, unbinds all control + * event handlers, and deletes the mixer instance from MixItUp's internal + * cache. + * + * This should be performed whenever a mixer's container is removed from + * the DOM, such as during a page change in a single page application, + * or React's `componentWillUnmount()`. + * + * @example + * + * .destroy([cleanUp]) + * + * @example Example: Destroying the mixer before removing its container element + * + * mixer.destroy(); + * + * containerEl.parentElement.removeChild(containerEl); + * + * @public + * @instance + * @since 2.0.0 + * @param {boolean} [cleanUp=false] + * An optional boolean dictating whether or not to clean up any inline `display: none;` styling applied to hidden targets. + * @return {void} + */ + + destroy: function(cleanUp) { + var self = this, + control = null, + target = null, + i = 0; + + self.callActions('beforeDestroy', arguments); + + for (i = 0; control = self.controls[i]; i++) { + control.removeBinding(self); + } + + for (i = 0; target = self.targets[i]; i++) { + if (cleanUp) { + target.show(); + } + + target.unbindEvents(); + } + + if (self.dom.container.id.match(/^MixItUp/)) { + self.dom.container.removeAttribute('id'); + } + + delete mixitup.instances[self.id]; + + self.callActions('afterDestroy', arguments); + } + }); + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.IMoveData = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.posIn = null; + this.posOut = null; + this.operation = null; + this.callback = null; + this.statusChange = ''; + this.duration = -1; + this.staggerIndex = -1; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.IMoveData); + + mixitup.IMoveData.prototype = Object.create(mixitup.Base.prototype); + + mixitup.IMoveData.prototype.constructor = mixitup.IMoveData; + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.TargetDom = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.el = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.TargetDom); + + mixitup.TargetDom.prototype = Object.create(mixitup.Base.prototype); + + mixitup.TargetDom.prototype.constructor = mixitup.TargetDom; + + /** + * @constructor + * @namespace + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.Target = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.id = ''; + this.sortString = ''; + this.mixer = null; + this.callback = null; + this.isShown = false; + this.isBound = false; + this.isExcluded = false; + this.isInDom = false; + this.handler = null; + this.operation = null; + this.data = null; + this.dom = new mixitup.TargetDom(); + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.Target); + + mixitup.Target.prototype = Object.create(mixitup.Base.prototype); + + h.extend(mixitup.Target.prototype, { + constructor: mixitup.Target, + + /** + * Initialises a newly instantiated Target. + * + * @private + * @instance + * @since 3.0.0 + * @param {(Element|null)} el + * @param {object} mixer + * @param {object} [data] + * @return {void} + */ + + init: function(el, mixer, data) { + var self = this, + id = ''; + + self.callActions('beforeInit', arguments); + + self.mixer = mixer; + + if (!el) { + // If no element is provided, render it + + el = self.render(data); + } + + self.cacheDom(el); + + self.bindEvents(); + + if (self.dom.el.style.display !== 'none') { + self.isShown = true; + } + + if (data && mixer.config.data.uidKey) { + if (typeof (id = data[mixer.config.data.uidKey]) === 'undefined' || id.toString().length < 1) { + throw new TypeError(mixitup.messages.errorDatasetInvalidUidKey({ + uidKey: mixer.config.data.uidKey + })); + } + + self.id = id; + self.data = data; + + mixer.cache[id] = self; + } + + self.callActions('afterInit', arguments); + }, + + /** + * Renders the target element using a user-defined renderer function. + * + * @private + * @instance + * @since 3.1.4 + * @param {object} data + * @return {void} + */ + + render: function(data) { + var self = this, + render = null, + el = null, + temp = null, + output = ''; + + self.callActions('beforeRender', arguments); + + render = self.callFilters('renderRender', self.mixer.config.render.target, arguments); + + if (typeof render !== 'function') { + throw new TypeError(mixitup.messages.errorDatasetRendererNotSet()); + } + + output = render(data); + + if (output && typeof output === 'object' && h.isElement(output)) { + el = output; + } else if (typeof output === 'string') { + temp = document.createElement('div'); + temp.innerHTML = output; + + el = temp.firstElementChild; + } + + return self.callFilters('elRender', el, arguments); + }, + + /** + * Caches references of DOM elements neccessary for the target's functionality. + * + * @private + * @instance + * @since 3.0.0 + * @param {Element} el + * @return {void} + */ + + cacheDom: function(el) { + var self = this; + + self.callActions('beforeCacheDom', arguments); + + self.dom.el = el; + + self.callActions('afterCacheDom', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {string} attributeName + * @return {void} + */ + + getSortString: function(attributeName) { + var self = this, + value = self.dom.el.getAttribute('data-' + attributeName) || ''; + + self.callActions('beforeGetSortString', arguments); + + value = isNaN(value * 1) ? + value.toLowerCase() : + value * 1; + + self.sortString = value; + + self.callActions('afterGetSortString', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @return {void} + */ + + show: function() { + var self = this; + + self.callActions('beforeShow', arguments); + + if (!self.isShown) { + self.dom.el.style.display = ''; + + self.isShown = true; + } + + self.callActions('afterShow', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @return {void} + */ + + hide: function() { + var self = this; + + self.callActions('beforeHide', arguments); + + if (self.isShown) { + self.dom.el.style.display = 'none'; + + self.isShown = false; + } + + self.callActions('afterHide', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {mixitup.IMoveData} moveData + * @return {void} + */ + + move: function(moveData) { + var self = this; + + self.callActions('beforeMove', arguments); + + if (!self.isExcluded) { + self.mixer.targetsMoved++; + } + + self.applyStylesIn(moveData); + + requestAnimationFrame(function() { + self.applyStylesOut(moveData); + }); + + self.callActions('afterMove', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {object} posData + * @param {number} multiplier + * @return {void} + */ + + applyTween: function(posData, multiplier) { + var self = this, + propertyName = '', + tweenData = null, + posIn = posData.posIn, + currentTransformValues = [], + currentValues = new mixitup.StyleData(), + i = -1; + + self.callActions('beforeApplyTween', arguments); + + currentValues.x = posIn.x; + currentValues.y = posIn.y; + + if (multiplier === 0) { + self.hide(); + } else if (!self.isShown) { + self.show(); + } + + for (i = 0; propertyName = mixitup.features.TWEENABLE[i]; i++) { + tweenData = posData.tweenData[propertyName]; + + if (propertyName === 'x') { + if (!tweenData) continue; + + currentValues.x = posIn.x + (tweenData * multiplier); + } else if (propertyName === 'y') { + if (!tweenData) continue; + + currentValues.y = posIn.y + (tweenData * multiplier); + } else if (tweenData instanceof mixitup.TransformData) { + if (!tweenData.value) continue; + + currentValues[propertyName].value = + posIn[propertyName].value + (tweenData.value * multiplier); + + currentValues[propertyName].unit = tweenData.unit; + + currentTransformValues.push( + propertyName + '(' + currentValues[propertyName].value + tweenData.unit + ')' + ); + } else { + if (!tweenData) continue; + + currentValues[propertyName] = posIn[propertyName] + (tweenData * multiplier); + + self.dom.el.style[propertyName] = currentValues[propertyName]; + } + } + + if (currentValues.x || currentValues.y) { + currentTransformValues.unshift('translate(' + currentValues.x + 'px, ' + currentValues.y + 'px)'); + } + + if (currentTransformValues.length) { + self.dom.el.style[mixitup.features.transformProp] = currentTransformValues.join(' '); + } + + self.callActions('afterApplyTween', arguments); + }, + + /** + * Applies the initial styling to a target element before any transition + * is applied. + * + * @private + * @instance + * @param {mixitup.IMoveData} moveData + * @return {void} + */ + + applyStylesIn: function(moveData) { + var self = this, + posIn = moveData.posIn, + isFading = self.mixer.effectsIn.opacity !== 1, + transformValues = []; + + self.callActions('beforeApplyStylesIn', arguments); + + transformValues.push('translate(' + posIn.x + 'px, ' + posIn.y + 'px)'); + + if (self.mixer.config.animation.animateResizeTargets) { + if (moveData.statusChange !== 'show') { + // Don't apply posIn width or height or showing, as will be 0 + + self.dom.el.style.width = posIn.width + 'px'; + self.dom.el.style.height = posIn.height + 'px'; + } + + self.dom.el.style.marginRight = posIn.marginRight + 'px'; + self.dom.el.style.marginBottom = posIn.marginBottom + 'px'; + } + + isFading && (self.dom.el.style.opacity = posIn.opacity); + + if (moveData.statusChange === 'show') { + transformValues = transformValues.concat(self.mixer.transformIn); + } + + self.dom.el.style[mixitup.features.transformProp] = transformValues.join(' '); + + self.callActions('afterApplyStylesIn', arguments); + }, + + /** + * Applies a transition followed by the final styles for the element to + * transition towards. + * + * @private + * @instance + * @param {mixitup.IMoveData} moveData + * @return {void} + */ + + applyStylesOut: function(moveData) { + var self = this, + transitionRules = [], + transformValues = [], + isResizing = self.mixer.config.animation.animateResizeTargets, + isFading = typeof self.mixer.effectsIn.opacity !== 'undefined'; + + self.callActions('beforeApplyStylesOut', arguments); + + // Build the transition rules + + transitionRules.push(self.writeTransitionRule( + mixitup.features.transformRule, + moveData.staggerIndex + )); + + if (moveData.statusChange !== 'none') { + transitionRules.push(self.writeTransitionRule( + 'opacity', + moveData.staggerIndex, + moveData.duration + )); + } + + if (isResizing) { + transitionRules.push(self.writeTransitionRule( + 'width', + moveData.staggerIndex, + moveData.duration + )); + + transitionRules.push(self.writeTransitionRule( + 'height', + moveData.staggerIndex, + moveData.duration + )); + + transitionRules.push(self.writeTransitionRule( + 'margin', + moveData.staggerIndex, + moveData.duration + )); + } + + // If no callback was provided, the element will + // not transition in any way so tag it as "immovable" + + if (!moveData.callback) { + self.mixer.targetsImmovable++; + + if (self.mixer.targetsMoved === self.mixer.targetsImmovable) { + // If the total targets moved is equal to the + // number of immovable targets, the operation + // should be considered finished + + self.mixer.cleanUp(moveData.operation); + } + + return; + } + + // If the target will transition in some fasion, + // assign a callback function + + self.operation = moveData.operation; + self.callback = moveData.callback; + + // As long as the target is not excluded, increment + // the total number of targets bound + + !self.isExcluded && self.mixer.targetsBound++; + + // Tag the target as bound to differentiate from transitionEnd + // events that may come from stylesheet driven effects + + self.isBound = true; + + // Apply the transition + + self.applyTransition(transitionRules); + + // Apply width, height and margin negation + + if (isResizing && moveData.posOut.width > 0 && moveData.posOut.height > 0) { + self.dom.el.style.width = moveData.posOut.width + 'px'; + self.dom.el.style.height = moveData.posOut.height + 'px'; + self.dom.el.style.marginRight = moveData.posOut.marginRight + 'px'; + self.dom.el.style.marginBottom = moveData.posOut.marginBottom + 'px'; + } + + if (!self.mixer.config.animation.nudge && moveData.statusChange === 'hide') { + // If we're not nudging, the translation should be + // applied before any other transforms to prevent + // lateral movement + + transformValues.push('translate(' + moveData.posOut.x + 'px, ' + moveData.posOut.y + 'px)'); + } + + // Apply fade + + switch (moveData.statusChange) { + case 'hide': + isFading && (self.dom.el.style.opacity = self.mixer.effectsOut.opacity); + + transformValues = transformValues.concat(self.mixer.transformOut); + + break; + case 'show': + isFading && (self.dom.el.style.opacity = 1); + } + + if ( + self.mixer.config.animation.nudge || + (!self.mixer.config.animation.nudge && moveData.statusChange !== 'hide') + ) { + // Opposite of above - apply translate after + // other transform + + transformValues.push('translate(' + moveData.posOut.x + 'px, ' + moveData.posOut.y + 'px)'); + } + + // Apply transforms + + self.dom.el.style[mixitup.features.transformProp] = transformValues.join(' '); + + self.callActions('afterApplyStylesOut', arguments); + }, + + /** + * Combines the name of a CSS property with the appropriate duration and delay + * values to created a valid transition rule. + * + * @private + * @instance + * @since 3.0.0 + * @param {string} property + * @param {number} staggerIndex + * @param {number} duration + * @return {string} + */ + + writeTransitionRule: function(property, staggerIndex, duration) { + var self = this, + delay = self.getDelay(staggerIndex), + rule = ''; + + rule = property + ' ' + + (duration > 0 ? duration : self.mixer.config.animation.duration) + 'ms ' + + delay + 'ms ' + + (property === 'opacity' ? 'linear' : self.mixer.config.animation.easing); + + return self.callFilters('ruleWriteTransitionRule', rule, arguments); + }, + + /** + * Calculates the transition delay for each target element based on its index, if + * staggering is applied. If defined, A custom `animation.staggerSeqeuence` + * function can be used to manipulate the order of indices to produce custom + * stagger effects (e.g. for use in a grid with irregular row lengths). + * + * @private + * @instance + * @since 2.0.0 + * @param {number} index + * @return {number} + */ + + getDelay: function(index) { + var self = this, + delay = -1; + + if (typeof self.mixer.config.animation.staggerSequence === 'function') { + index = self.mixer.config.animation.staggerSequence.call(self, index, self.state); + } + + delay = !!self.mixer.staggerDuration ? index * self.mixer.staggerDuration : 0; + + return self.callFilters('delayGetDelay', delay, arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {string[]} rules + * @return {void} + */ + + applyTransition: function(rules) { + var self = this, + transitionString = rules.join(', '); + + self.callActions('beforeApplyTransition', arguments); + + self.dom.el.style[mixitup.features.transitionProp] = transitionString; + + self.callActions('afterApplyTransition', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Event} e + * @return {void} + */ + + handleTransitionEnd: function(e) { + var self = this, + propName = e.propertyName, + canResize = self.mixer.config.animation.animateResizeTargets; + + self.callActions('beforeHandleTransitionEnd', arguments); + + if ( + self.isBound && + e.target.matches(self.mixer.config.selectors.target) && + ( + propName.indexOf('transform') > -1 || + propName.indexOf('opacity') > -1 || + canResize && propName.indexOf('height') > -1 || + canResize && propName.indexOf('width') > -1 || + canResize && propName.indexOf('margin') > -1 + ) + ) { + self.callback.call(self, self.operation); + + self.isBound = false; + self.callback = null; + self.operation = null; + } + + self.callActions('afterHandleTransitionEnd', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {Event} e + * @return {void} + */ + + eventBus: function(e) { + var self = this; + + self.callActions('beforeEventBus', arguments); + + switch (e.type) { + case 'webkitTransitionEnd': + case 'transitionend': + self.handleTransitionEnd(e); + } + + self.callActions('afterEventBus', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @return {void} + */ + + unbindEvents: function() { + var self = this; + + self.callActions('beforeUnbindEvents', arguments); + + h.off(self.dom.el, 'webkitTransitionEnd', self.handler); + h.off(self.dom.el, 'transitionend', self.handler); + + self.callActions('afterUnbindEvents', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @return {void} + */ + + bindEvents: function() { + var self = this, + transitionEndEvent = ''; + + self.callActions('beforeBindEvents', arguments); + + transitionEndEvent = mixitup.features.transitionPrefix === 'webkit' ? 'webkitTransitionEnd' : 'transitionend'; + + self.handler = function(e) { + return self.eventBus(e); + }; + + h.on(self.dom.el, transitionEndEvent, self.handler); + + self.callActions('afterBindEvents', arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @param {boolean} [getBox] + * @return {PosData} + */ + + getPosData: function(getBox) { + var self = this, + styles = {}, + rect = null, + posData = new mixitup.StyleData(); + + self.callActions('beforeGetPosData', arguments); + + posData.x = self.dom.el.offsetLeft; + posData.y = self.dom.el.offsetTop; + + if (self.mixer.config.animation.animateResizeTargets || getBox) { + rect = self.dom.el.getBoundingClientRect(); + + posData.top = rect.top; + posData.right = rect.right; + posData.bottom = rect.bottom; + posData.left = rect.left; + + posData.width = rect.width; + posData.height = rect.height; + } + + if (self.mixer.config.animation.animateResizeTargets) { + styles = window.getComputedStyle(self.dom.el); + + posData.marginBottom = parseFloat(styles.marginBottom); + posData.marginRight = parseFloat(styles.marginRight); + } + + return self.callFilters('posDataGetPosData', posData, arguments); + }, + + /** + * @private + * @instance + * @since 3.0.0 + * @return {void} + */ + + cleanUp: function() { + var self = this; + + self.callActions('beforeCleanUp', arguments); + + self.dom.el.style[mixitup.features.transformProp] = ''; + self.dom.el.style[mixitup.features.transitionProp] = ''; + self.dom.el.style.opacity = ''; + + if (self.mixer.config.animation.animateResizeTargets) { + self.dom.el.style.width = ''; + self.dom.el.style.height = ''; + self.dom.el.style.marginRight = ''; + self.dom.el.style.marginBottom = ''; + } + + self.callActions('afterCleanUp', arguments); + } + }); + + /** + * A jQuery-collection-like wrapper around one or more `mixitup.Mixer` instances + * allowing simultaneous control of said instances similar to the MixItUp 2 API. + * + * @example + * new mixitup.Collection(instances) + * + * @constructor + * @namespace + * @memberof mixitup + * @private + * @since 3.0.0 + * @param {mixitup.Mixer[]} instances + */ + + mixitup.Collection = function(instances) { + var instance = null, + i = -1; + + this.callActions('beforeConstruct'); + + for (i = 0; instance = instances[i]; i++) { + this[i] = instance; + } + + this.length = instances.length; + + this.callActions('afterConstruct'); + + h.freeze(this); + }; + + mixitup.BaseStatic.call(mixitup.Collection); + + mixitup.Collection.prototype = Object.create(mixitup.Base.prototype); + + h.extend(mixitup.Collection.prototype, + /** @lends mixitup.Collection */ + { + constructor: mixitup.Collection, + + /** + * Calls a method on all instances in the collection by passing the method + * name as a string followed by any applicable parameters to be curried into + * to the method. + * + * @example + * .mixitup(methodName[,arg1][,arg2..]); + * + * @example + * var collection = new Collection([mixer1, mixer2]); + * + * return collection.mixitup('filter', '.category-a') + * .then(function(states) { + * state.forEach(function(state) { + * console.log(state.activeFilter.selector); // .category-a + * }); + * }); + * + * @public + * @instance + * @since 3.0.0 + * @param {string} methodName + * @return {Promise>} + */ + + mixitup: function(methodName) { + var self = this, + instance = null, + args = Array.prototype.slice.call(arguments), + tasks = [], + i = -1; + + this.callActions('beforeMixitup'); + + args.shift(); + + for (i = 0; instance = self[i]; i++) { + tasks.push(instance[methodName].apply(instance, args)); + } + + return self.callFilters('promiseMixitup', h.all(tasks, mixitup.libraries), arguments); + } + }); + + /** + * `mixitup.Operation` objects contain all data neccessary to describe the full + * lifecycle of any MixItUp operation. They can be used to compute and store an + * operation for use at a later time (e.g. programmatic tweening). + * + * @constructor + * @namespace + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.Operation = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.id = ''; + + this.args = []; + this.command = null; + this.showPosData = []; + this.toHidePosData = []; + + this.startState = null; + this.newState = null; + this.docState = null; + + this.willSort = false; + this.willChangeLayout = false; + this.hasEffect = false; + this.hasFailed = false; + + this.triggerElement = null; + + this.show = []; + this.hide = []; + this.matching = []; + this.toShow = []; + this.toHide = []; + this.toMove = []; + this.toRemove = []; + this.startOrder = []; + this.newOrder = []; + this.startSort = null; + this.newSort = null; + this.startFilter = null; + this.newFilter = null; + this.startDataset = null; + this.newDataset = null; + this.viewportDeltaX = 0; + this.viewportDeltaY = 0; + this.startX = 0; + this.startY = 0; + this.startHeight = 0; + this.startWidth = 0; + this.newX = 0; + this.newY = 0; + this.newHeight = 0; + this.newWidth = 0; + this.startContainerClassName = ''; + this.startDisplay = ''; + this.newContainerClassName = ''; + this.newDisplay = ''; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.Operation); + + mixitup.Operation.prototype = Object.create(mixitup.Base.prototype); + + mixitup.Operation.prototype.constructor = mixitup.Operation; + + /** + * `mixitup.State` objects expose various pieces of data detailing the state of + * a MixItUp instance. They are provided at the start and end of any operation via + * callbacks and events, with the most recent state stored between operations + * for retrieval at any time via the API. + * + * @constructor + * @namespace + * @memberof mixitup + * @public + * @since 3.0.0 + */ + + mixitup.State = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /** + * The ID of the mixer instance. + * + * @name id + * @memberof mixitup.State + * @instance + * @type {string} + * @default '' + */ + + this.id = ''; + + /** + * The currently active filter command as set by a control click or API call. + * + * @name activeFilter + * @memberof mixitup.State + * @instance + * @type {mixitup.CommandFilter} + * @default null + */ + + this.activeFilter = null; + + /** + * The currently active sort command as set by a control click or API call. + * + * @name activeSort + * @memberof mixitup.State + * @instance + * @type {mixitup.CommandSort} + * @default null + */ + + this.activeSort = null; + + /** + * The current layout-specific container class name, if applied. + * + * @name activeContainerClassName + * @memberof mixitup.State + * @instance + * @type {string} + * @default '' + */ + + this.activeContainerClassName = ''; + + /** + * A reference to the container element that the mixer is instantiated on. + * + * @name container + * @memberof mixitup.State + * @instance + * @type {Element} + * @default null + */ + + this.container = null; + + /** + * An array of all target elements indexed by the mixer. + * + * @name targets + * @memberof mixitup.State + * @instance + * @type {Array.} + * @default [] + */ + + this.targets = []; + + /** + * An array of all target elements not matching the current filter. + * + * @name hide + * @memberof mixitup.State + * @instance + * @type {Array.} + * @default [] + */ + + this.hide = []; + + /** + * An array of all target elements matching the current filter and any additional + * limits applied such as pagination. + * + * @name show + * @memberof mixitup.State + * @instance + * @type {Array.} + * @default [] + */ + + this.show = []; + + /** + * An array of all target elements matching the current filter irrespective of + * any additional limits applied such as pagination. + * + * @name matching + * @memberof mixitup.State + * @instance + * @type {Array.} + * @default [] + */ + + this.matching = []; + + /** + * An integer representing the total number of target elements indexed by the + * mixer. Equivalent to `state.targets.length`. + * + * @name totalTargets + * @memberof mixitup.State + * @instance + * @type {number} + * @default -1 + */ + + this.totalTargets = -1; + + /** + * An integer representing the total number of target elements matching the + * current filter and any additional limits applied such as pagination. + * Equivalent to `state.show.length`. + * + * @name totalShow + * @memberof mixitup.State + * @instance + * @type {number} + * @default -1 + */ + + this.totalShow = -1; + + /** + * An integer representing the total number of target elements not matching + * the current filter. Equivalent to `state.hide.length`. + * + * @name totalHide + * @memberof mixitup.State + * @instance + * @type {number} + * @default -1 + */ + + this.totalHide = -1; + + /** + * An integer representing the total number of target elements matching the + * current filter irrespective of any other limits applied such as pagination. + * Equivalent to `state.matching.length`. + * + * @name totalMatching + * @memberof mixitup.State + * @instance + * @type {number} + * @default -1 + */ + + this.totalMatching = -1; + + /** + * A boolean indicating whether the last operation "failed", i.e. no targets + * could be found matching the filter. + * + * @name hasFailed + * @memberof mixitup.State + * @instance + * @type {boolean} + * @default false + */ + + this.hasFailed = false; + + /** + * The DOM element that was clicked if the last operation was triggered by the + * clicking of a control and not an API call. + * + * @name triggerElement + * @memberof mixitup.State + * @instance + * @type {Element|null} + * @default null + */ + + this.triggerElement = null; + + /** + * The currently active dataset underlying the rendered targets, if the + * dataset API is in use. + * + * @name activeDataset + * @memberof mixitup.State + * @instance + * @type {Array.} + * @default null + */ + + this.activeDataset = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.State); + + mixitup.State.prototype = Object.create(mixitup.Base.prototype); + + mixitup.State.prototype.constructor = mixitup.State; + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.UserInstruction = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + this.command = {}; + this.animate = false; + this.callback = null; + + this.callActions('afterConstruct'); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.UserInstruction); + + mixitup.UserInstruction.prototype = Object.create(mixitup.Base.prototype); + + mixitup.UserInstruction.prototype.constructor = mixitup.UserInstruction; + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + */ + + mixitup.Messages = function() { + mixitup.Base.call(this); + + this.callActions('beforeConstruct'); + + /* Errors + ----------------------------------------------------------------------------- */ + + this.ERROR_FACTORY_INVALID_CONTAINER = + '[MixItUp] An invalid selector or element reference was passed to the mixitup factory function'; + + this.ERROR_FACTORY_CONTAINER_NOT_FOUND = + '[MixItUp] The provided selector yielded no container element'; + + this.ERROR_CONFIG_INVALID_ANIMATION_EFFECTS = + '[MixItUp] Invalid value for `animation.effects`'; + + this.ERROR_CONFIG_INVALID_CONTROLS_SCOPE = + '[MixItUp] Invalid value for `controls.scope`'; + + this.ERROR_CONFIG_INVALID_PROPERTY = + '[MixitUp] Invalid configuration object property "${erroneous}"${suggestion}'; + + this.ERROR_CONFIG_INVALID_PROPERTY_SUGGESTION = + '. Did you mean "${probableMatch}"?'; + + this.ERROR_CONFIG_DATA_UID_KEY_NOT_SET = + '[MixItUp] To use the dataset API, a UID key must be specified using `data.uidKey`'; + + this.ERROR_DATASET_INVALID_UID_KEY = + '[MixItUp] The specified UID key "${uidKey}" is not present on one or more dataset items'; + + this.ERROR_DATASET_DUPLICATE_UID = + '[MixItUp] The UID "${uid}" was found on two or more dataset items. UIDs must be unique.'; + + this.ERROR_INSERT_INVALID_ARGUMENTS = + '[MixItUp] Please provider either an index or a sibling and position to insert, not both'; + + this.ERROR_INSERT_PREEXISTING_ELEMENT = + '[MixItUp] An element to be inserted already exists in the container'; + + this.ERROR_FILTER_INVALID_ARGUMENTS = + '[MixItUp] Please provide either a selector or collection `.filter()`, not both'; + + this.ERROR_DATASET_NOT_SET = + '[MixItUp] To use the dataset API with pre-rendered targets, a starting dataset must be set using `load.dataset`'; + + this.ERROR_DATASET_PRERENDERED_MISMATCH = + '[MixItUp] `load.dataset` does not match pre-rendered targets'; + + this.ERROR_DATASET_RENDERER_NOT_SET = + '[MixItUp] To insert an element via the dataset API, a target renderer function must be provided to `render.target`'; + + this.ERROR_SORT_NON_EXISTENT_ELEMENT = + '[MixItUp] An element to be sorted does not already exist in the container'; + + /* Warnings + ----------------------------------------------------------------------------- */ + + this.WARNING_FACTORY_PREEXISTING_INSTANCE = + '[MixItUp] WARNING: This element already has an active MixItUp instance. The provided configuration object will be ignored.' + + ' If you wish to perform additional methods on this instance, please create a reference.'; + + this.WARNING_INSERT_NO_ELEMENTS = + '[MixItUp] WARNING: No valid elements were passed to `.insert()`'; + + this.WARNING_REMOVE_NO_ELEMENTS = + '[MixItUp] WARNING: No valid elements were passed to `.remove()`'; + + this.WARNING_MULTIMIX_INSTANCE_QUEUE_FULL = + '[MixItUp] WARNING: An operation was requested but the MixItUp instance was busy. The operation was rejected because the ' + + 'queue is full or queuing is disabled.'; + + this.WARNING_GET_OPERATION_INSTANCE_BUSY = + '[MixItUp] WARNING: Operations can be be created while the MixItUp instance is busy.'; + + this.WARNING_NO_PROMISE_IMPLEMENTATION = + '[MixItUp] WARNING: No Promise implementations could be found. If you wish to use promises with MixItUp please install' + + ' an ES6 Promise polyfill.'; + + this.WARNING_INCONSISTENT_SORTING_ATTRIBUTES = + '[MixItUp] WARNING: The requested sorting data attribute "${attribute}" was not present on one or more target elements' + + ' which may product unexpected sort output'; + + this.callActions('afterConstruct'); + + this.compileTemplates(); + + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.Messages); + + mixitup.Messages.prototype = Object.create(mixitup.Base.prototype); + + mixitup.Messages.prototype.constructor = mixitup.Messages; + + /** + * @return {void} + */ + + mixitup.Messages.prototype.compileTemplates = function() { + var errorKey = ''; + var errorMessage = ''; + + for (errorKey in this) { + if (typeof (errorMessage = this[errorKey]) !== 'string') continue; + + this[h.camelCase(errorKey)] = h.template(errorMessage); + } + }; + + mixitup.messages = new mixitup.Messages(); + + /** + * @constructor + * @memberof mixitup + * @private + * @since 3.0.0 + * @param {mixitup.Mixer} mixer + */ + + mixitup.Facade = function Mixer(mixer) { + mixitup.Base.call(this); + + this.callActions('beforeConstruct', arguments); + + this.configure = mixer.configure.bind(mixer); + this.show = mixer.show.bind(mixer); + this.hide = mixer.hide.bind(mixer); + this.filter = mixer.filter.bind(mixer); + this.toggleOn = mixer.toggleOn.bind(mixer); + this.toggleOff = mixer.toggleOff.bind(mixer); + this.sort = mixer.sort.bind(mixer); + this.changeLayout = mixer.changeLayout.bind(mixer); + this.multimix = mixer.multimix.bind(mixer); + this.dataset = mixer.dataset.bind(mixer); + this.tween = mixer.tween.bind(mixer); + this.insert = mixer.insert.bind(mixer); + this.insertBefore = mixer.insertBefore.bind(mixer); + this.insertAfter = mixer.insertAfter.bind(mixer); + this.prepend = mixer.prepend.bind(mixer); + this.append = mixer.append.bind(mixer); + this.remove = mixer.remove.bind(mixer); + this.destroy = mixer.destroy.bind(mixer); + this.forceRefresh = mixer.forceRefresh.bind(mixer); + this.forceRender = mixer.forceRender.bind(mixer); + this.isMixing = mixer.isMixing.bind(mixer); + this.getOperation = mixer.getOperation.bind(mixer); + this.getConfig = mixer.getConfig.bind(mixer); + this.getState = mixer.getState.bind(mixer); + + this.callActions('afterConstruct', arguments); + + h.freeze(this); + h.seal(this); + }; + + mixitup.BaseStatic.call(mixitup.Facade); + + mixitup.Facade.prototype = Object.create(mixitup.Base.prototype); + + mixitup.Facade.prototype.constructor = mixitup.Facade; + + if (typeof exports === 'object' && typeof module === 'object') { + module.exports = mixitup; + } else if (typeof define === 'function' && define.amd) { + define(function() { + return mixitup; + }); + } else if (typeof window.mixitup === 'undefined' || typeof window.mixitup !== 'function') { + window.mixitup = mixitup; + } + mixitup.BaseStatic.call(mixitup.constructor); + + mixitup.NAME = 'mixitup'; + mixitup.CORE_VERSION = '3.3.1'; +})(window); \ No newline at end of file diff --git a/themes/jamstackthemes/assets/js/scripts.js b/themes/jamstackthemes/assets/js/scripts.js new file mode 100644 index 0000000..e69de29 diff --git a/themes/jamstackthemes/assets/scss/_breadcrumbs.scss b/themes/jamstackthemes/assets/scss/_breadcrumbs.scss new file mode 100644 index 0000000..f168b6b --- /dev/null +++ b/themes/jamstackthemes/assets/scss/_breadcrumbs.scss @@ -0,0 +1,32 @@ +.breadcrumb { + padding: 0; + list-style: none; + display: flex; + flex-flow: row; + flex-wrap: wrap; + align-items: center; + a { + color: $primary; + padding: 8px; + &:first-of-type { + padding-left: 0; + } + } + .end { + padding: 8px; + font-weight: bold; + } + li { + &:not(:last-of-type) { + &::after { + content: '/'; + margin: 0 6px; + color: $steel; + } + } + &.active { + color: $primary;; + pointer-events: none; + } + } +} diff --git a/themes/jamstackthemes/assets/scss/_buttons.scss b/themes/jamstackthemes/assets/scss/_buttons.scss new file mode 100644 index 0000000..ec22c73 --- /dev/null +++ b/themes/jamstackthemes/assets/scss/_buttons.scss @@ -0,0 +1,54 @@ +.button { + border-radius: 3px; + color: $primary; + outline: none; + text-decoration: none; + padding: 5px 10px; + margin-right: 5px; + cursor: pointer; + background: none; + border: 1px solid $primary; + margin-bottom: 5px; + &:hover { + border-color: $primary; + opacity: 0.8; + } + &:focus { + outline: none; + } + &.selected, + &.mixitup-control-active { + background: $primary; + color: white; + } +} + +.sort-buttons { + margin-bottom: 10px; + display: flex; + align-items: flex-start; + flex-wrap: wrap; + @include media-breakpoint-up(sm) { + margin-bottom: 0; + margin-top: 23px; + } + .sort-by { + display: none; + margin-right: 5px; + color: #666; + @include media-breakpoint-up(sm) { + margin-bottom: 0; + } + } + button { + font-size: 14px; + flex-grow: 1; + @include media-breakpoint-up(sm) { + font-size: 16px; + margin-bottom: 0; + } + @include media-breakpoint-up(lg) { + flex-grow: 0; + } + } +} diff --git a/themes/jamstackthemes/assets/scss/_content.scss b/themes/jamstackthemes/assets/scss/_content.scss new file mode 100644 index 0000000..e635244 --- /dev/null +++ b/themes/jamstackthemes/assets/scss/_content.scss @@ -0,0 +1,103 @@ +.content { + p { + margin: 0; + padding: 0; + margin-bottom: 20px; + } + h1 { + font-size: 36px; + line-height: 1.3; + font-weight: normal; + margin-top: 20px; + margin-bottom: 20px; + &:first-of-type { + margin-top: 0; + } + } + h2 { + font-size: 28px; + line-height: 1.4; + font-weight: normal; + margin-top: 20px; + margin-bottom: 20px; + &:first-of-type { + margin-top: 0; + } + @include media-breakpoint-up(sm) { + font-size: 30px; + line-height: 1.4; + } + } + h3 { + font-size: 22px; + line-height: 1.4; + font-weight: bold; + margin-top: 20px; + margin-bottom: 20px; + } + ul, + ol { + margin-left: 0; + margin-top: 10px; + margin-bottom: 20px; + padding: 0; + list-style: disc; + li { + margin-bottom: 5px; + margin-left: 15px; + @include media-breakpoint-up(sm) { + margin-left: 20px; + } + } + @include media-breakpoint-up(sm) { + margin-left: 20px; + } + } + a { + text-decoration: underline; + color: $primary; + } + blockquote { + margin-left: 0; + margin-right: 0; + margin-top: 40px; + margin-bottom: 40px; + padding-left: 40px; + } + blockquote p { + display: inline; + } + img { + max-width: 100%; + height: auto; + } + .highlight { + border-radius: 2px; + margin-bottom: 20px; + } + code { + background: #f5f5f5; + padding: 3px 6px; + border-radius: 3px; + font-family: $font-family-mono; + font-size: 0.9rem; + line-height: 1.4; + } + pre { + font-family: $font-family-mono; + font-size: 0.9rem; + line-height: 1.4; + margin: 0; + padding: 10px; + border-radius: 1px; + overflow-x: auto; + code { + font-family: $font-family-mono; + font-size: 0.9rem; + line-height: 1.4; + padding: 0; + margin: 0; + background: none; + } + } +} diff --git a/themes/jamstackthemes/assets/scss/_demo.scss b/themes/jamstackthemes/assets/scss/_demo.scss new file mode 100644 index 0000000..afed9bd --- /dev/null +++ b/themes/jamstackthemes/assets/scss/_demo.scss @@ -0,0 +1,152 @@ +.iframe-preview { + position: absolute; + border: none; + width: 100%; + height: calc(100% - 60px); + margin-top: 60px; +} + +.iframe-preview--mobile { + width: 375px; + left: 50%; + -webkit-transform: translateX(-50%); + transform: translateX(-50%); } + +.demo { + background-color: white; + border-bottom: 1px solid $grey; + box-shadow: 0px 0px 9px 3px rgba(41,41,41,.25); + position: fixed; + width: 100%; + z-index: 10; + @include media-breakpoint-up(sm) { + padding-left: $layout-padding; + padding-right: $layout-padding; + } + @include media-breakpoint-up(md) { + padding-left: $layout-padding * 2; + padding-right: $layout-padding * 2; + } + @include media-breakpoint-up(lg) { + padding-left: $layout-padding * 4; + padding-right: $layout-padding * 4; + } + .container { + height: 60px; + display: flex; + align-items: center; + justify-content: space-between; + } + h1.demo-title { + font-size: 20px; + font-family: $font_family_heading; + margin: 0; + margin-left: 10px; + font-weight: 600; + margin-right: 10px; + a { + color: $primary; + } + } + .demo-heading { + display: flex; + } + .demo-info { + font-size: 14px; + } + .demo-buttons { + display: flex; + justify-content: flex-start; + flex-wrap: wrap; + .theme-buttons-group { + &:last-of-type { + margin-bottom: 0; + } + } + .theme-button { + @extend .button; + text-align: center; + flex: 1 0 auto; + font-size: 14px; + &.theme-button-fill { + background-color: $primary; + color: white; + } + &.theme-button-link { + border: none; + text-decoration: underline; + } + } + } + .submit { + display: flex; + align-items: center; + .submit-button { + padding: 10px; + border-radius: 3px; + text-decoration: none; + color: #000; + &:hover { + color: $primary; + } + } + .submit-button-mobile { + display: block; + @include media-breakpoint-up(sm) { + display: none; + } + } + .submit-button-desktop { + display: none; + @include media-breakpoint-up(sm) { + display: block; + } + } + svg { + width: 20px; + height: 20px; + @include media-breakpoint-up(sm) { + width: 24px; + height: 24px; + } + } + .github { + &:hover { + opacity: 0.7; + } + } + } + .main-menu { + display: none; + @include media-breakpoint-up(md) { + display: block; + } + > ul { + margin: 0; + padding: 0; + display: flex; + align-items: center; + justify-content: flex-start; + > li { + flex: 1 0 auto; + list-style: none; + margin: 0; + padding: 0; + > a { + text-decoration: none; + color: $primary; + padding: 5px 8px; + &:hover { + text-decoration: underline; + } + } + &.active { + > a { + font-weight: bold; + text-decoration: none; + } + } + } + } + } +} diff --git a/themes/jamstackthemes/assets/scss/_filter.scss b/themes/jamstackthemes/assets/scss/_filter.scss new file mode 100644 index 0000000..908cfb2 --- /dev/null +++ b/themes/jamstackthemes/assets/scss/_filter.scss @@ -0,0 +1,298 @@ +.filters { + .filter { + padding: 15px 0px; + &.closed { + .filter-list { + display: none; + } + .filter-title { + margin: 0; + } + .toggle-icon { + transform: rotate(270deg); + } + } + &:first-of-type { + padding-top: 0; + } + } + .filter-title { + display: flex; + flex-wrap: wrap; + margin-bottom: 10px; + @include media-breakpoint-up(md) { + display: flex; + align-items: center; + } + span { + display: flex; + align-items: center; + } + h3 { + margin-bottom: 0; + font-size: 14px; + line-height: 18px; + @include media-breakpoint-up(md) { + font-size: 17px; + line-height: 22px; + } + } + .toggle-info { + margin-left: 4px; + display: flex; + @include media-breakpoint-up(md) { + margin-left: 6px; + margin-bottom: 1px; + } + a { + display: flex; + align-items: center; + } + svg { + width: 18px; + height: auto; + fill: $primary; + path { + fill: $primary; + } + @include media-breakpoint-up(md) { + width: 19px; + fill: $primary; + path { + fill: $primary; + } + } + } + &:hover { + opacity: 0.6; + } + } + .toggle-icon { + margin-left: auto; + cursor: pointer; + display: none; + @include media-breakpoint-up(md) { + display: flex; + } + img { + width: 14px; + } + svg { + width: 14px; + color: $primary; + } + &:hover { + opacity: 0.6; + } + } + } + .filter-list { + margin: 0; + padding: 0; + &.closed { + .filter-item { + display: none; + } + .toggle-more { + .toggle-more-icon-plus { + display: inline-block; + } + .toggle-more-icon-minus { + display: none; + } + } + } + .toggle-more { + text-transform: uppercase; + line-height: 18px; + color: $primary; + margin-left: 26px; + display: flex; + cursor: pointer; + padding: 6px 2px; + &:hover { + opacity: 0.8; + } + .toggle-more-icon { + height: 14px; + width: 14px; + svg { + color: $primary; + } + } + .toggle-more-icon-plus { + display: none; + } + .toggle-more-icon-minus { + display: inline-block; + } + } + .filter-item { + margin: 0; + padding: 0; + list-style: none; + display: flex; + align-items: center; + justify-content: flex-start; + margin-bottom: 6px; + font-size: 13px; + line-height: 18px; + &:last-of-type { + margin-bottom: 0; + } + .filter-button { + margin-right: 8px; + display: inline-block; + vertical-align: text-top; + width: 18px; + height: 18px; + background: lighten($grey,6%); + border: 1px solid darken($grey, 40%); + border-radius: 3px; + cursor: pointer; + &:hover { + border: 2px solid $primary; + } + &:focus { + outline: none; + box-shadow: 0 0 5px $primary; + } + &:focus:not(:focus-visible) { + // for modern browsers, remove outline if mouse focus + box-shadow: none; + } + &.mixitup-control-active { + background: $primary; + border: 2px solid $primary; + background-image: url("../images/ui/check-solid.svg"); + background-size: 12px 12px; + background-position: center center; + background-repeat: no-repeat; + } + } + .filter-icon { + img { + + } + width: 18px; + height: 18px; + margin-right: 8px; + } + .filter-name { + display: none; + color: $carbon; + a { + color: black; + text-decoration: none; + } + @include media-breakpoint-up(md) { + display: block; + } + } + .filter-link { + display: none; + color: $primary; + text-decoration: none; + padding: 2px 5px; + @include media-breakpoint-up(md) { + display: block; + } + &:hover { + text-decoration: underline; + } + } + .filter-count { + color: #666; + @include media-breakpoint-up(md) { + margin: 0 0 0 auto; + } + } + } + } +} + +.styled-checkbox { + position: absolute; // take it out of document flow + opacity: 0; // hide it + + & + label { + position: relative; + cursor: pointer; + padding: 0; + } + + // Box. + & + label:before { + content: ''; + margin-right: 10px; + display: inline-block; + vertical-align: text-top; + width: 20px; + height: 20px; + background: lighten($grey,6%); + border: 2px solid darken($grey, 0%); + border-radius: 3px; + // box-shadow: inset 0 0 10px rgba(0,0,0,0.5); + } + + // Box hover + &:hover + label:before { + // background: #f35429; + // box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.12); + border: 2px solid $primary; + } + + // Box focus + &:focus + label:before { + // box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.12); + } + + // Box checked + &:checked + label:before { + background: $primary; + border: 2px solid $primary; + } + + // Disabled state label. + &:disabled + label { + color: #b8b8b8; + cursor: auto; + } + + // Disabled box. + &:disabled + label:before { + box-shadow: none; + background: #ddd; + } + + // Checkmark. Could be replaced with an image + &:checked + label:after { + content: ''; + position: absolute; + left: 5px; + top: 9px; + background: white; + width: 2px; + height: 2px; + box-shadow: 2px 0 0 white, 4px 0 0 white, 4px -2px 0 white, 4px -4px 0 white, + 4px -6px 0 white, 4px -8px 0 white; + transform: rotate(45deg); + } +} + +.fade-in { + opacity: 1; + animation-name: fadeInOpacity; + animation-iteration-count: 1; + animation-timing-function: ease-in; + animation-duration: 300ms; +} + +@keyframes fadeInOpacity { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} diff --git a/themes/jamstackthemes/assets/scss/_footer.scss b/themes/jamstackthemes/assets/scss/_footer.scss new file mode 100644 index 0000000..6dd3ef1 --- /dev/null +++ b/themes/jamstackthemes/assets/scss/_footer.scss @@ -0,0 +1,66 @@ +.footer { + background: #fff; + .footer-column { + margin-bottom: 30px; + @include media-breakpoint-up(sm) { + width: 235px; + margin-right: 50px; + margin-bottom: 0px; + } + &:last-of-type { + margin-bottom: 0; + } + } + .footer-column-brand { + @include media-breakpoint-up(sm) { + width: 235px; + } + } + p { + line-height: 18px; + margin: 0; + margin-bottom: 20px; + } + a { + display: block; + margin-bottom: 8px; + color: $primary; + text-decoration: none; + &:hover { + text-decoration: underline; + } + } + h3 { + margin-bottom: 10px; + } + ul { + margin: 0; + padding: 0; + list-style: none; + margin-right: 20px; + li { + + line-height: 18px; + a { + // color: darken($steel, 10%); + color: $primary; + text-decoration: none; + &:hover { + text-decoration: underline; + } + } + } + } + .footer-social { + margin-bottom: 20px; + .social-icon { + margin-right: 5px; + a { + text-decoration: none; + img { + width: 18px; + } + } + } + } +} diff --git a/themes/jamstackthemes/assets/scss/_github-metrics.scss b/themes/jamstackthemes/assets/scss/_github-metrics.scss new file mode 100644 index 0000000..fe2794f --- /dev/null +++ b/themes/jamstackthemes/assets/scss/_github-metrics.scss @@ -0,0 +1,30 @@ +.github-metrics { + display: inline-flex; + align-content: center; + align-items: flex-start; + border-radius: 3px; + margin: 0px -9px 10px -9px; + div { + border-right: 1px solid $grey; + padding: 7px 9px; + line-height: 1; + display: flex; + align-content: center; + &:last-of-type { + border-right: none; + } + img, svg { + opacity: 0.7; + width: 15px; + height: 15px; + margin-right: 4px; + path { + fill: $carbon; + } + } + span { + margin: 0; + line-height: 16px; + } + } +} \ No newline at end of file diff --git a/themes/jamstackthemes/assets/scss/_header.scss b/themes/jamstackthemes/assets/scss/_header.scss new file mode 100644 index 0000000..7523a25 --- /dev/null +++ b/themes/jamstackthemes/assets/scss/_header.scss @@ -0,0 +1,114 @@ +.header { + background-color: white; + border-bottom: 1px solid $grey; + position: fixed; + width: 100%; + z-index: 10; + //@include media-breakpoint-up(sm) { + // padding-left: $layout-padding; + // padding-right: $layout-padding; + //} + //@include media-breakpoint-up(md) { + // padding-left: $layout-padding * 2; + // padding-right: $layout-padding * 2; + //} + //@include media-breakpoint-up(lg) { + // padding-left: $layout-padding * 4; + // padding-right: $layout-padding * 4; + //} + .container { + height: 60px; + display: flex; + align-items: center; + justify-content: space-between; + } + .submit { + display: flex; + align-items: center; + .submit-button { + padding: 10px; + border-radius: 3px; + text-decoration: none; + color: #000; + &:hover { + color: $primary; + } + } + .submit-button-mobile { + display: block; + @include media-breakpoint-up(sm) { + display: none; + } + } + .submit-button-desktop { + display: none; + @include media-breakpoint-up(sm) { + display: block; + } + } + svg { + width: 20px; + height: 20px; + @include media-breakpoint-up(sm) { + width: 24px; + height: 24px; + } + } + .github { + &:hover { + opacity: 0.7; + } + } + } + .main-menu { + display: none; + @include media-breakpoint-up(md) { + display: block; + } + > ul { + margin: 0; + padding: 0; + display: flex; + align-items: center; + justify-content: flex-start; + > li { + flex: 1 0 auto; + list-style: none; + margin: 0; + padding: 0; + > a { + text-decoration: none; + color: $primary; + padding: 5px 8px; + &:hover { + text-decoration: underline; + } + } + &.active { + > a { + font-weight: bold; + text-decoration: none; + } + } + } + } + } +} + +.logo { + margin: 0; + font-size: 18px; + font-weight: 700; + font-family: $font_family_heading; + margin-right: 20px; + align-items: center; + display: flex; + a { + color: black; + text-decoration: none; + display: block; + &:hover { + color: $primary; + } + } +} \ No newline at end of file diff --git a/themes/jamstackthemes/assets/scss/_icon.scss b/themes/jamstackthemes/assets/scss/_icon.scss new file mode 100644 index 0000000..8dbd321 --- /dev/null +++ b/themes/jamstackthemes/assets/scss/_icon.scss @@ -0,0 +1,44 @@ +.icon { + vertical-align: middle; + display: inline-block; + position: relative; + img, svg { + display: inline-block; + vertical-align: bottom; + width: 20px; + height: 20px; + //filter: grayscale(100%) contrast(70%) brightness(80%); + } +} + +a.icon { + &:hover { + opacity: 0.7; + } +} + +.icon-card { + //border: 1px solid $grey; + //border-radius: 3px; + //background-color: white; + //box-shadow: 1px 1px 8px 2px adjust-color(black, $alpha: -0.9); + //padding: 5px; + @include media-breakpoint-up(sm) { + padding: 10px; + } + //&:hover { + // transform: translateY(-3px); + // box-shadow: 1px 5px 16px 2px adjust-color($primary, $alpha: -0.8); + // color: currentColor; + //} + img { + display: inline-block; + vertical-align: bottom; + width: 24px; + height: 24px; + @include media-breakpoint-up(sm) { + width: 32px; + height: 32px; + } + } +} diff --git a/themes/jamstackthemes/assets/scss/_intro.scss b/themes/jamstackthemes/assets/scss/_intro.scss new file mode 100644 index 0000000..f1c86e2 --- /dev/null +++ b/themes/jamstackthemes/assets/scss/_intro.scss @@ -0,0 +1,152 @@ +.intro { + .intro-heading { + margin-bottom: 10px; + } + .intro-icon { + display: inline-block; + vertical-align: middle; + margin-right: 2px; + img { + display: inline-block; + vertical-align: bottom; + width: 24px; + @include media-breakpoint-up(sm) { + width: 28px; + } + @include media-breakpoint-up(md) { + width: 36px; + } + @include media-breakpoint-up(xl) { + width: 46px; + } + } + } + .intro-title { + display: inline-block; + vertical-align: middle; + font-weight: 600; + font-size: 24px; + margin: 0; + @include media-breakpoint-up(sm) { + font-size: 28px; + } + @include media-breakpoint-up(md) { + font-size: 36px; + } + @include media-breakpoint-up(xl) { + font-size: 46px; + } + } + .intro-description { + @include media-breakpoint-up(md) { + font-size: 18px; + } + @include media-breakpoint-up(lg) { + font-size: 20px; + width: 85%; + } + } + .intro-backlink { + display: none; + margin-top: 6px; + padding: 4px 10px; + border: 1px solid $grey; + border-radius: 3px; + @include media-breakpoint-up(md) { + margin-left: auto; + display: block; + } + a { + color: $primary; + padding: 8px; + &:first-of-type { + padding-left: 0; + } + } + } + .intro-bar { + display: flex; + align-items: center; + } + .intro-hint { + font-size: 14px; + padding: 11px; + background: aliceblue; + border-radius: 2px; + color: #647f96; + display: flex; + justify-content: space-between; + align-items: center; + p { + margin: 0; + } + .button { + color: #647f96; + border-color: #647f96; + margin: 0; + } + } + .count { + color: lighten($steel, 20%); + font-weight: 400; + } + .page-number { + font-weight: 400; + } + + .stackbit-banner { + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + padding: 40px 24px; + gap: 16px; + background: #02001D; + border-radius: 4px; + margin-bottom: 32px; + background-image: url("/images/banner/bg.svg"); + background-repeat: no-repeat; + background-position: right; + background-size: contain; + + .banner-title-row { + display: flex; + flex-direction: row; + align-items: center; + padding: 0px; + gap: 17px; + font-size: 32px; + font-weight: 400; + color: white; + line-height: 1; + } + + .banner-subtitle-row { + font-size: 18px; + color: white; + } + + .banner-primary-button { + padding: 8px 16px; + background: #8efbf7; + border-style: none; + border-radius: 3px; + outline: none; + font-weight: 400; + font-size: 16px; + line-height: 20px; + color: #014847; + text-decoration: none; + a { + color: #014847; + } + a:hover { + text-decoration: none; + } + a:visited { + color: inherit; + } + } + } +} + diff --git a/themes/jamstackthemes/assets/scss/_label.scss b/themes/jamstackthemes/assets/scss/_label.scss new file mode 100644 index 0000000..007e1ce --- /dev/null +++ b/themes/jamstackthemes/assets/scss/_label.scss @@ -0,0 +1,26 @@ +.label { + text-transform: uppercase; + margin: 3px 0 3px 0; + display: flex; + align-items: center; + font-size: 12px; + @include media-breakpoint-up(lg) { + font-size: 14px; + } + .label-key { + display: inline-block; + margin-right: 4px; + color: $steel; + } + .label-icon { + margin-right: 2px; + svg, img { + height: 14px; + width: 14px; + } + } + .label-value { + display: inline-block; + color: black; + } +} diff --git a/themes/jamstackthemes/assets/scss/_layout.scss b/themes/jamstackthemes/assets/scss/_layout.scss new file mode 100644 index 0000000..f572351 --- /dev/null +++ b/themes/jamstackthemes/assets/scss/_layout.scss @@ -0,0 +1,172 @@ +$layout-padding: 15px; + +.page { + display: flex; + justify-content: space-between; + flex-direction: column; + min-height: 100vh; + &.has-sidebar { + .header { + @include media-breakpoint-up(sm) { + padding-left: $layout-padding; + padding-right: $layout-padding; + } + @include media-breakpoint-up(md) { + padding-left: $layout-padding * 2; + padding-right: $layout-padding * 2; + } + @include media-breakpoint-up(lg) { + padding-left: $layout-padding * 4; + padding-right: $layout-padding * 4; + } + .container { + max-width: none !important; + } + } + .wrapper { + display: flex; + .main { + flex: 1; + @include media-breakpoint-up(sm) { + padding: $layout-padding; + } + @include media-breakpoint-up(md) { + padding: $layout-padding * 2; + } + @include media-breakpoint-up(lg) { + padding: $layout-padding * 4; + } + } + .sidebar { + flex-basis: 110px; + @include media-breakpoint-up(sm) { + flex-basis: 140px; + } + @include media-breakpoint-up(md) { + flex-basis: 240px; + } + @include media-breakpoint-up(lg) { + flex-basis: 360px; + } + } + } + } + .header { + position: fixed; + width: 100%; + z-index: 10; + + .container { + height: 60px; + display: flex; + align-items: center; + justify-content: space-between; + } + } + .wrapper { + padding-top: 60px; + .main { + padding-top: $layout-padding; + padding-bottom: $layout-padding; + @include media-breakpoint-up(sm) { + padding-top: $layout-padding; + padding-bottom: $layout-padding; + } + @include media-breakpoint-up(md) { + padding-top: $layout-padding * 2; + padding-bottom: $layout-padding * 2; + } + @include media-breakpoint-up(lg) { + padding-top: $layout-padding * 4; + padding-bottom: $layout-padding * 4; + } + } + .sidebar { + position: sticky; + top: 60px; + z-index: 1; + width: 120px; + overflow: auto; + height: calc(100vh - 60px); + display: flex; + flex-direction: column; + background-color: $bone; + padding-top: $grid-gutter-width / 2; + padding-bottom: $grid-gutter-width / 2; + @include media-breakpoint-up(sm) { + width: 140px; + padding: $layout-padding; + } + @include media-breakpoint-up(md) { + width: 240px; + padding: $layout-padding * 2; + } + @include media-breakpoint-up(lg) { + width: 360px; + padding: $layout-padding * 4; + } + .container { + display: flex; + flex-direction: column; + justify-content: space-between; + height: 100%; + .stackbit { + padding: 6px 0; + display: flex; + gap: 4px; + align-items: center; + font-size: 14px; + a { + color: inherit; + text-decoration: underline; + } + } + } + } + } + .footer { + width: 100%; + border-top: 1px solid $grey; + padding-top: $grid-gutter-width / 2; + padding-bottom: $grid-gutter-width / 2; + @include media-breakpoint-up(sm) { + padding: $layout-padding; + } + @include media-breakpoint-up(md) { + padding: $layout-padding * 2; + } + @include media-breakpoint-up(lg) { + padding: $layout-padding * 4; + } + .container { + .footer-columns { + display: flex; + flex-direction: column; + @include media-breakpoint-up(md) { + flex-direction: row; + display: flex; + justify-items: flex-end; + } + .footer-column { + margin-bottom: 20px; + } + } + } + } +} + +.not-found { + height: 100vh; + width: 100vw; + display: flex; + justify-content: center; + align-items: center; +} + +.strip-bone { + background: $bone; +} +.strip-title { + font-size: 38px; + font-weight: 400; +} diff --git a/themes/jamstackthemes/assets/scss/_pagination.scss b/themes/jamstackthemes/assets/scss/_pagination.scss new file mode 100644 index 0000000..dfdd633 --- /dev/null +++ b/themes/jamstackthemes/assets/scss/_pagination.scss @@ -0,0 +1,62 @@ +ul.pagination { + display: flex; + justify-content: center; + list-style: none; + margin: 20px 0 20px 0; + padding: 0; + li { + list-style: none; + text-decoration: none; + border: 2px solid $primary; + border-right: none; + text-align: center; + vertical-align: middle; + &:hover { + border-color: $primary; + background-color: $primary; + color: white; + a { + color: white; + } + } + &.pagination-active { + border-color: $primary; + background-color: $primary; + color: white; + a { + color: white; + } + } + a { + padding: 9px 13px; + display: block; + color: $primary; + text-decoration: none; + vertical-align: middle; + @include media-breakpoint-up(lg) { + padding: 10px 16px; + } + } + &:last-of-type { + border-right: 2px solid $primary; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + &:hover { + border-color: $primary; + } + &.active { + border-color: $primary; + } + } + &:first-of-type { + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; + } + svg { + width: 8px; + line-height: 10px; + margin-bottom: 2px; + vertical-align: middle; + } + } +} diff --git a/themes/jamstackthemes/assets/scss/_star.scss b/themes/jamstackthemes/assets/scss/_star.scss new file mode 100644 index 0000000..9105c4a --- /dev/null +++ b/themes/jamstackthemes/assets/scss/_star.scss @@ -0,0 +1,30 @@ +.star { + display: inline-block; + border: 1px solid $bone; + padding: 4px 7px 4px 22px; + border-radius: 2px; + margin-bottom: 8px; + background-image: url("../images/ui/star.svg"); + background-repeat: no-repeat; + background-position: 6px 6px; + background-size: 12px 12px; + img { + display: inline-block; + width: 12px; + height: 12px; + } + svg { + fill: $yellow; + } + .star-count { + display: inline-block; + } +} + +.star-large { + background-image: url("../images/ui/star.svg"); + background-repeat: no-repeat; + background-position: 6px 6px; + background-size: 22px 22px; + padding-left: 32px; +} diff --git a/themes/jamstackthemes/assets/scss/_terms.scss b/themes/jamstackthemes/assets/scss/_terms.scss new file mode 100644 index 0000000..6fcf70e --- /dev/null +++ b/themes/jamstackthemes/assets/scss/_terms.scss @@ -0,0 +1,55 @@ +.terms { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + @include media-breakpoint-up(sm) { + margin-left: -10px; + margin-right: -10px; + justify-content: flex-start; + } + .term { + padding: 40px; + border: 1px solid $grey; + margin-bottom: 15px; + width: 48%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background-color: white; + box-shadow: 1px 1px 8px 2px adjust-color(black, $alpha: -0.9); + transition: color 0.3s, box-shadow 0.3s, transform 0.3s; + position: relative; + z-index: 1; + @include media-breakpoint-up(sm) { + flex: 0; + min-width: 180px; + margin: 10px; + } + &:hover { + transform: translateY(-3px); + box-shadow: 1px 5px 16px 2px adjust-color($primary, $alpha: -0.8); + color: currentColor; + } + .term-overlay { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + } + h2 { + margin-top: 15px; + margin-bottom: 0; + font-weight: 400; + text-align: center; + a { + text-decoration: none; + color: black; + } + } + img { + display: block; + } + } +} diff --git a/themes/jamstackthemes/assets/scss/_theme-buttons.scss b/themes/jamstackthemes/assets/scss/_theme-buttons.scss new file mode 100644 index 0000000..672f4ad --- /dev/null +++ b/themes/jamstackthemes/assets/scss/_theme-buttons.scss @@ -0,0 +1,67 @@ +.action-buttons { + display: flex; + justify-content: flex-start; + flex-wrap: wrap; + .action-buttons-group { + &:last-of-type { + margin-bottom: 0; + + } + } + .action-button { + @extend .button; + padding: 5px 8px; + text-align: center; + flex: 1 0 auto; + font-size: 14px; + &.action-button-fill { + background-color: $primary; + color: white; + } + &.action-button-link { + border: none; + text-decoration: underline; + } + &:last-of-type { + margin-right: 0; + } + } +} +.action-buttons-detail { + display: flex; + margin-top: 20px; + margin-bottom: 20px; + .action-button { + display: inline-block; + padding: 7px 15px; + border: 1px solid $primary; + border-radius: 4px; + font-size: 16px; + margin-right: 10px; + color: $primary; + text-decoration: none; + &:hover { + color: $primary; + opacity: 0.8; + } + &:last-of-type { + margin-right: 0; + } + &.action-button-fill { + background-color: $primary; + color: white; + } + &.action-button-link { + border: none; + text-decoration: underline; + } + } +} + +.tag-button { + color: $steel; + font-size: 14px; + padding: 5px 8px; + border: 1px solid $steel; + border-radius: 4px; +} \ No newline at end of file diff --git a/themes/jamstackthemes/assets/scss/_theme-card.scss b/themes/jamstackthemes/assets/scss/_theme-card.scss new file mode 100644 index 0000000..fc22117 --- /dev/null +++ b/themes/jamstackthemes/assets/scss/_theme-card.scss @@ -0,0 +1,122 @@ +.theme-card { + overflow: hidden; + display: flex; + flex-direction: column; + justify-content: space-between; + border: 1px solid $grey; + border-radius: 4px; + background-color: white; + position: relative; + z-index: 1; + height: 100%; + .theme-header { + border-bottom: 1px solid $grey; + max-height: 260px; + overflow: hidden; + img { + max-width: 100%; + width: 100%; + height: auto; + vertical-align: bottom; + transition: opacity 0.2s ease-out; + &:hover { + opacity: 0.7; + } + } + } + .theme-body { + //min-height: 200px; + background: white; + padding: 20px; + flex: 1; + display: flex; + flex-direction: column; + h2 { + font-size: 20px; + font-weight: 500; + margin-bottom: 10px; + @include media-breakpoint-up(xl) { + font-size: 26px; + } + } + p { + font-size: 14px; + @include media-breakpoint-up(xl) { + font-size: 16px; + } + &.description { + padding: 20px 0 20px 0; + padding: 0; + margin: 0; + } + } + .theme-description { + margin-bottom: 20px; + } + .action-buttons { + margin-top: auto; + } + } + .theme-footer { + //border-top: 1px solid $grey; + background: $bone; + padding: 8px 13px 8px 13px; + display: flex; + justify-content: flex-start; + flex-wrap: wrap; + a { + flex: 0; + padding: 7px; + //border-right: 1px solid $grey; + //border-bottom: 1px solid $grey; + } + } + .theme-buttons { + display: flex; + justify-content: flex-start; + flex-wrap: wrap; + .theme-buttons-group { + &:last-of-type { + margin-bottom: 0; + } + } + .theme-button { + @extend .button; + text-align: center; + flex: 1 0 auto; + font-size: 14px; + &.theme-button-fill { + background-color: $primary; + color: white; + } + &.theme-button-link { + border: none; + text-decoration: underline; + } + } + } + .theme-stars { + display: inline-block; + border: 1px solid $bone; + padding: 4px 6px 4px 24px; + border-radius: 2px; + color: $steel; + background-image: url("../images/ui/star.svg"); + background-repeat: no-repeat; + background-position: 6px; + background-size: 13px; + background-color: $bone; + font-size: 14px; + font-weight: 600; + @include media-breakpoint-up(xl) { + background-size: 15px; + background-position: 8px; + padding: 4px 6px 4px 28px; + font-size: 16px; + font-weight: normal; + } + .star-count { + display: inline-block; + } + } +} diff --git a/themes/jamstackthemes/assets/scss/_theme-detail.scss b/themes/jamstackthemes/assets/scss/_theme-detail.scss new file mode 100644 index 0000000..ffc864a --- /dev/null +++ b/themes/jamstackthemes/assets/scss/_theme-detail.scss @@ -0,0 +1,133 @@ +.theme-detail { + .theme-detail-bar { + display: flex; + align-items: center; + font-weight: bold; + text-transform: uppercase; + font-size: 12px; + margin-bottom: 5px; + color: $steel; + .theme-detail-icon { + margin-right: 4px; + width: 16px; + } + } + .theme-detail-heading { + display: flex; + align-items: flex-start; + margin-bottom: 10px; + } + .theme-detail-title { + font-size: 24px; + line-height: 28px; + font-weight: normal; + margin-bottom: 15px; + } + .theme-detail-stars { + float: right; + height: 18px; + margin-top: 4px; + font-size: 16px; + background-position: center left; + background-size: 18px; + background-repeat: no-repeat; + padding-left: 24px; + background-image: url("../images/ui/star.svg"); + span { + line-height: 22px; + } + } + .theme-detail-description { + font-size: 16px; + line-height: 22px; + color: $steel; + margin-bottom: 20px; + } + .theme-detail-github { + padding: 10px 0 10px 0; + display: flex; + align-items: center; + > div { + font-size: 16px; + line-height: 18px; + margin-right: 14px; + background-size: 18px 18px; + background-repeat: no-repeat; + padding-left: 26px; + vertical-align: middle; + } + .stars { + background-image: url("../images/ui/star.svg"); + } + .issues { + background-image: url("../images/ui/issues.svg"); + } + .forks { + background-image: url("../images/ui/forks.svg"); + } + } + .theme-detail-image { + img { + //box-shadow: 1px 2px 14px 2px rgba(0, 0, 0, 0.1); + border-radius: 3px; + border: 1px solid $grey; + width: 100%; + max-width: 100%; + height: auto; + vertical-align: bottom; + } + } + .theme-detail-tags { + margin-bottom: 20px; + .icon { + width: 36px; + height: 36px; + margin: 0; + display: inline-flex; + justify-content: center; + align-items: center; + } + } + .theme-detail-bottom { + padding-top: $layout-padding; + padding-bottom: $layout-padding; + @include media-breakpoint-up(sm) { + padding-top: $layout-padding; + padding-bottom: $layout-padding; + } + @include media-breakpoint-up(md) { + padding-top: $layout-padding * 2; + padding-bottom: $layout-padding * 2; + } + @include media-breakpoint-up(lg) { + padding-top: $layout-padding * 4; + padding-bottom: $layout-padding * 4; + } + } + +} + +.page-default-single .wrapper { + //position: relative; + //overflow: hidden; +} + +.page-default-single .wrapper::before { + //content: ""; + position: absolute; + width: 200%; + height: 200%; + top: -50%; + left: -50%; + z-index: -1; + transform: rotate(30deg); + overflow: hidden; + //background-image: url(/images/material-design-4k-wallpaper-greyscale.png); + background-image: url(/images/1.png); + background-position: top right; + background-repeat: no-repeat; + background-size: cover; + //background-color: antiquewhite; + //background-color: #fdf5dd; + //background-color: #cdf3fd; +} diff --git a/themes/jamstackthemes/assets/scss/bootstrap/_alert.scss b/themes/jamstackthemes/assets/scss/bootstrap/_alert.scss new file mode 100644 index 0000000..da2a98a --- /dev/null +++ b/themes/jamstackthemes/assets/scss/bootstrap/_alert.scss @@ -0,0 +1,51 @@ +// +// Base styles +// + +.alert { + position: relative; + padding: $alert-padding-y $alert-padding-x; + margin-bottom: $alert-margin-bottom; + border: $alert-border-width solid transparent; + @include border-radius($alert-border-radius); +} + +// Headings for larger alerts +.alert-heading { + // Specified to prevent conflicts of changing $headings-color + color: inherit; +} + +// Provide class for links that match alerts +.alert-link { + font-weight: $alert-link-font-weight; +} + + +// Dismissible alerts +// +// Expand the right padding and account for the close button's positioning. + +.alert-dismissible { + padding-right: $close-font-size + $alert-padding-x * 2; + + // Adjust close link position + .close { + position: absolute; + top: 0; + right: 0; + padding: $alert-padding-y $alert-padding-x; + color: inherit; + } +} + + +// Alternate styles +// +// Generate contextual modifier classes for colorizing the alert. + +@each $color, $value in $theme-colors { + .alert-#{$color} { + @include alert-variant(theme-color-level($color, $alert-bg-level), theme-color-level($color, $alert-border-level), theme-color-level($color, $alert-color-level)); + } +} diff --git a/themes/jamstackthemes/assets/scss/bootstrap/_badge.scss b/themes/jamstackthemes/assets/scss/bootstrap/_badge.scss new file mode 100644 index 0000000..42c5d08 --- /dev/null +++ b/themes/jamstackthemes/assets/scss/bootstrap/_badge.scss @@ -0,0 +1,54 @@ +// Base class +// +// Requires one of the contextual, color modifier classes for `color` and +// `background-color`. + +.badge { + display: inline-block; + padding: $badge-padding-y $badge-padding-x; + @include font-size($badge-font-size); + font-weight: $badge-font-weight; + line-height: 1; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + @include border-radius($badge-border-radius); + @include transition($badge-transition); + + @at-root a#{&} { + @include hover-focus() { + text-decoration: none; + } + } + + // Empty badges collapse automatically + &:empty { + display: none; + } +} + +// Quick fix for badges in buttons +.btn .badge { + position: relative; + top: -1px; +} + +// Pill badges +// +// Make them extra rounded with a modifier to replace v3's badges. + +.badge-pill { + padding-right: $badge-pill-padding-x; + padding-left: $badge-pill-padding-x; + @include border-radius($badge-pill-border-radius); +} + +// Colors +// +// Contextual variations (linked badges get darker on :hover). + +@each $color, $value in $theme-colors { + .badge-#{$color} { + @include badge-variant($value); + } +} diff --git a/themes/jamstackthemes/assets/scss/bootstrap/_breadcrumb.scss b/themes/jamstackthemes/assets/scss/bootstrap/_breadcrumb.scss new file mode 100644 index 0000000..d748894 --- /dev/null +++ b/themes/jamstackthemes/assets/scss/bootstrap/_breadcrumb.scss @@ -0,0 +1,42 @@ +.breadcrumb { + display: flex; + flex-wrap: wrap; + padding: $breadcrumb-padding-y $breadcrumb-padding-x; + margin-bottom: $breadcrumb-margin-bottom; + @include font-size($breadcrumb-font-size); + list-style: none; + background-color: $breadcrumb-bg; + @include border-radius($breadcrumb-border-radius); +} + +.breadcrumb-item { + // The separator between breadcrumbs (by default, a forward-slash: "/") + + .breadcrumb-item { + padding-left: $breadcrumb-item-padding; + + &::before { + display: inline-block; // Suppress underlining of the separator in modern browsers + padding-right: $breadcrumb-item-padding; + color: $breadcrumb-divider-color; + content: escape-svg($breadcrumb-divider); + } + } + + // IE9-11 hack to properly handle hyperlink underlines for breadcrumbs built + // without `