From 7076343338ae3439f3c86f01144818abe8c31978 Mon Sep 17 00:00:00 2001 From: George Halkiadakis Date: Sun, 12 Mar 2023 07:16:23 +0200 Subject: add container helpers; constuct public directory-tree --- html/content/js/Cart/miniCart.js | 96 +++++++ html/content/js/Filters/filterStatus.js | 113 ++++++++ html/content/js/Filters/filters.js | 110 ++++++++ html/content/js/Filters/filtersBar.js | 32 +++ html/content/js/Filters/range.js | 98 +++++++ html/content/js/Forms/ajaxForm.js | 188 +++++++++++++ html/content/js/Forms/clearinput.js | 16 ++ html/content/js/Forms/customValidators.js | 11 + html/content/js/Forms/password.js | 15 ++ html/content/js/Forms/select.js | 52 ++++ html/content/js/Forms/serialize.js | 30 +++ html/content/js/Forms/textarea.js | 26 ++ html/content/js/Forms/validateform.js | 3 + html/content/js/Images/lazyload.js | 61 +++++ html/content/js/Modals/modal.js | 6 + html/content/js/Plugins/accordion.js | 43 +++ html/content/js/Plugins/breadcrumb.js | 9 + html/content/js/Plugins/copy.js | 25 ++ html/content/js/Plugins/expand.js | 38 +++ html/content/js/Plugins/expandText.js | 5 + html/content/js/Plugins/fileinput.js | 20 ++ html/content/js/Plugins/gallery.js | 51 ++++ html/content/js/Plugins/infoPopup.js | 8 + html/content/js/Plugins/initialize.js | 11 + html/content/js/Plugins/intersecting.js | 18 ++ html/content/js/Plugins/menu.js | 97 +++++++ html/content/js/Plugins/quantity.js | 55 ++++ html/content/js/Plugins/scrollShadowHor.js | 42 +++ html/content/js/Plugins/scrollShadowVert.js | 57 ++++ html/content/js/Plugins/tabs.js | 28 ++ html/content/js/Plugins/tabtoggle.js | 27 ++ html/content/js/Plugins/tooltip.js | 45 ++++ html/content/js/Products/productHeader.js | 21 ++ html/content/js/Products/productImageModal.js | 47 ++++ html/content/js/Products/productattr.js | 11 + html/content/js/Sliders/productsSlider.js | 45 ++++ html/content/js/Sliders/slider.js | 42 +++ html/content/js/Utils/Event.js | 13 + html/content/js/Utils/EventUtils.js | 75 ++++++ html/content/js/Utils/apiready.js | 15 ++ html/content/js/Utils/confirm.js | 27 ++ html/content/js/Utils/debounce.js | 16 ++ html/content/js/Utils/defineApi.js | 9 + html/content/js/Utils/destroyevent.js | 11 + html/content/js/Utils/dom.js | 68 +++++ html/content/js/Utils/domHelper.js | 375 ++++++++++++++++++++++++++ html/content/js/Utils/enabled.js | 6 + html/content/js/Utils/getApis.js | 17 ++ html/content/js/Utils/throttle.js | 15 ++ html/content/js/Utils/whenAll.js | 13 + html/content/js/Wishlist/wishlistbutton.js | 25 ++ 51 files changed, 2287 insertions(+) create mode 100644 html/content/js/Cart/miniCart.js create mode 100644 html/content/js/Filters/filterStatus.js create mode 100644 html/content/js/Filters/filters.js create mode 100644 html/content/js/Filters/filtersBar.js create mode 100644 html/content/js/Filters/range.js create mode 100644 html/content/js/Forms/ajaxForm.js create mode 100644 html/content/js/Forms/clearinput.js create mode 100644 html/content/js/Forms/customValidators.js create mode 100644 html/content/js/Forms/password.js create mode 100644 html/content/js/Forms/select.js create mode 100644 html/content/js/Forms/serialize.js create mode 100644 html/content/js/Forms/textarea.js create mode 100644 html/content/js/Forms/validateform.js create mode 100644 html/content/js/Images/lazyload.js create mode 100644 html/content/js/Modals/modal.js create mode 100644 html/content/js/Plugins/accordion.js create mode 100644 html/content/js/Plugins/breadcrumb.js create mode 100644 html/content/js/Plugins/copy.js create mode 100644 html/content/js/Plugins/expand.js create mode 100644 html/content/js/Plugins/expandText.js create mode 100644 html/content/js/Plugins/fileinput.js create mode 100644 html/content/js/Plugins/gallery.js create mode 100644 html/content/js/Plugins/infoPopup.js create mode 100644 html/content/js/Plugins/initialize.js create mode 100644 html/content/js/Plugins/intersecting.js create mode 100644 html/content/js/Plugins/menu.js create mode 100644 html/content/js/Plugins/quantity.js create mode 100644 html/content/js/Plugins/scrollShadowHor.js create mode 100644 html/content/js/Plugins/scrollShadowVert.js create mode 100644 html/content/js/Plugins/tabs.js create mode 100644 html/content/js/Plugins/tabtoggle.js create mode 100644 html/content/js/Plugins/tooltip.js create mode 100644 html/content/js/Products/productHeader.js create mode 100644 html/content/js/Products/productImageModal.js create mode 100644 html/content/js/Products/productattr.js create mode 100644 html/content/js/Sliders/productsSlider.js create mode 100644 html/content/js/Sliders/slider.js create mode 100644 html/content/js/Utils/Event.js create mode 100644 html/content/js/Utils/EventUtils.js create mode 100644 html/content/js/Utils/apiready.js create mode 100644 html/content/js/Utils/confirm.js create mode 100644 html/content/js/Utils/debounce.js create mode 100644 html/content/js/Utils/defineApi.js create mode 100644 html/content/js/Utils/destroyevent.js create mode 100644 html/content/js/Utils/dom.js create mode 100644 html/content/js/Utils/domHelper.js create mode 100644 html/content/js/Utils/enabled.js create mode 100644 html/content/js/Utils/getApis.js create mode 100644 html/content/js/Utils/throttle.js create mode 100644 html/content/js/Utils/whenAll.js create mode 100644 html/content/js/Wishlist/wishlistbutton.js (limited to 'html/content/js') diff --git a/html/content/js/Cart/miniCart.js b/html/content/js/Cart/miniCart.js new file mode 100644 index 0000000..c02cc38 --- /dev/null +++ b/html/content/js/Cart/miniCart.js @@ -0,0 +1,96 @@ +$$plugins.define('miniCart', ['dom'], function (element, options, dom) { + const plugin = this, + selectors = { + openMiniCart: '.open-mini-cart', // Link that opens cart + closeMiniCart: '.close-mini-cart', // Link that closes cart + contentOuterContainer: '.miniCart', // Container for loader and inner content inner + loaderContainer: '.miniCart__holder', // Spinner or any other loader + contentInnerContainer: '.miniCart__main', // Content of mini cart + cartPluginElement: '[data-plugin-cart]' + }, cssClasses = { + opened: 'opened', // Added to 'openMinicart' link when opened + containerOpened: 'active' + }; + let pendingXhr, clearOnClick; + + function onBodyClick(e) { + if (element.contains(e.target)) { + return; + } + // case where node is removed from DOM. This means we dont know it's parent. Usually the case of cart remove. + if (!document.body.contains(e.target)) { + return; + } + close(); + } + + function close() { + // Remove opened class from link + element.querySelector(selectors.openMiniCart).classList.remove(cssClasses.opened); + + //Hide outer element + var outterContainer = element.querySelector(selectors.contentOuterContainer); + // dom.hide(outterContainer); + outterContainer.classList.remove(cssClasses.containerOpened); + // Hide and reset inner element + const inner = element.querySelector(selectors.contentInnerContainer); + //dom.empty(inner); + // dom.hide(inner); + + + // Show loader again to be ready for next time + //dom.show(element.querySelector(selectors.loaderContainer)); + + // Unbind body click + clearOnClick && clearOnClick(); + } + + function open() { + // Add opened class to link + element.querySelector(selectors.openMiniCart).classList.add(cssClasses.opened); + + // Show outer container + //dom.show(); + var outterContainer = element.querySelector(selectors.contentOuterContainer); + outterContainer.style.display = "block"; + outterContainer.classList.add(cssClasses.containerOpened); + // Listen for body clicks to close + clearOnClick && clearOnClick(); + clearOnClick = plugin.on('click', document.body, onBodyClick); + return true; + } + + function updateContent() { + return; + if (pendingXhr) { + return; + } + pendingXhr = true; + http.post(options.RefreshUrl, { responseType: "text" }) + .then(function (r) { + const container = element.querySelector(selectors.contentInnerContainer); + container.innerHTML = r.data; + dom.hide(element.querySelector(selectors.loaderContainer)); + dom.show(container); + }) + .finally(function () { + pendingXhr = false; + }); + } + + const openedStateSelector = selectors.openMiniCart + '.' + cssClasses.opened, + closedStateSelector = selectors.openMiniCart + ':not(.' + cssClasses.opened + ')'; + + plugin.on('click', element, function (e) { + if (dom.closest(e.target, openedStateSelector)) { + e.preventDefault(); + close(); + } else if (dom.closest(e.target, closedStateSelector)) { + + if (open()) { + e.preventDefault(); + updateContent(); + } + } + }); +}); diff --git a/html/content/js/Filters/filterStatus.js b/html/content/js/Filters/filterStatus.js new file mode 100644 index 0000000..1338b4c --- /dev/null +++ b/html/content/js/Filters/filterStatus.js @@ -0,0 +1,113 @@ +$$plugins.define('filterStatus', ['domHelper'], function (element, options, $) { + var filterName = element.dataset.inputname, + filterInput = element.querySelectorAll('.filter__input'), + clear = element.querySelectorAll('.filtersClear'), + clearAll = document.querySelectorAll('.filtersClearAll'), + selectedRadios, + selectedRadio; + + //on radiobutton, checkbox change + if (filterInput.length > 0) { + for (var i = 0; i < filterInput.length; i++) { + filterInput[i].addEventListener('change', function (e) { + filterGroupStatus(); + selectedFilters(e.target); + filterBarStatus(); + activeFilters(); + }); + } + } + + //clear filter group + for (var i = 0; i < clear.length; i++) { + clear[i].addEventListener('click', function (e) { + clearFilterGroup(); + filterBarStatus(); + activeFilters(); + }) + } + + //clearAll + for (var i = 0; i < clearAll.length; i++) { + clearAll[i].addEventListener('click', function (e) { + document.querySelectorAll('.selectedFilters__remove').forEach((elem) => elem.click()); + filterBarStatus(); + activeFilters(); + }) + } + + //check filter group status + function filterGroupStatus() { + if ($('.filter__input[name=' + filterName + ']:checked').length >= 1) { + element.classList.add('filter--checked'); + } + else { + element.classList.remove('filter--checked'); + } + } + + //add-remove selected filters on filters bar and on filter + function selectedFilters(inputElm) { + if (inputElm.checked) { + var selectedValue = $(inputElm).siblings('.filter__value').text(), + selectedItemId = inputElm.id, + selectedItem = document.createElement('li'), + selectedItemValue = document.createElement('span'), + selectedItemButton = document.createElement('button'); + + selectedItem.dataset.id = selectedItemId; + selectedItem.dataset.name = filterName; + selectedItem.setAttribute("class", "selectedFilters__item"); + selectedItemValue.setAttribute("class", "selectedFilters__value"); + selectedItemValue.innerHTML = selectedValue; + selectedItemButton.setAttribute("class", "selectedFilters__remove"); + + selectedItem.prepend(selectedItemButton); + selectedItem.prepend(selectedItemValue); + document.querySelector('.selectedFilters__list').prepend(selectedItem); + + if (inputElm.classList.contains('radiobutton__input')) { + + selectedRadios = Object.values(element.querySelectorAll('[name=' + filterName + ']')).filter(selectedRadio => selectedRadio !== inputElm); + for (var i = 0; i < selectedRadios.length; i++) { + selectedRadio = document.querySelector('[data-id=' + selectedRadios[i].id + ']') + if (typeof(selectedRadio) != 'undefined' && selectedRadio != null) { + selectedRadio.remove(); + //$('.filter__selectedValItem[data-id=' + $(this).attr('id') + ']').remove(); + } + } + } + } else { + if (inputElm.classList.contains('checkbox__input')) { + document.querySelector('[data-id=' + inputElm.id + ']').remove(); + //$('.filter__selectedValItem[data-id=' + inputElm.attr('id') + ']').remove(); + } + } + } + + //clear filter group + function clearFilterGroup() { + document.querySelectorAll('input[name=' + filterName + ']').forEach((elem) => elem.checked = false); + document.querySelectorAll('[data-name=' + filterName + ']').forEach((elem) => elem.remove()); + //$('.filter__selectedVal[data-name=' + filterName + ']').empty(); + filterGroupStatus(); + } + + function filterBarStatus() { + if ($('.selectedFilters__item').length > 1) { + $('.selectedFilters').addClass('active'); + } else { + $('.selectedFilters').removeClass('active'); + } + } + + //no of active filter groups + function activeFilters() { + document.getElementsByClassName("filters__mobButton")[0].dataset.activefilters = $('.filter--checked').length; + if ($('.filter--checked').length > 0) { + $('.filters').addClass('filters--checked'); + } else { + $('.filters').removeClass('filters--checked'); + } + } +}); \ No newline at end of file diff --git a/html/content/js/Filters/filters.js b/html/content/js/Filters/filters.js new file mode 100644 index 0000000..e5ade01 --- /dev/null +++ b/html/content/js/Filters/filters.js @@ -0,0 +1,110 @@ +$$plugins.define('filters', ['domHelper','throttle'], function (element, options, $, throttle) { + var filterTitle = element.querySelectorAll(".filter__title"), + closeButton = element.querySelectorAll(".filters__close"), + filterBack = element.querySelectorAll(".filters__mobTitle--prev"), + filterContent; + + document.documentElement.style.setProperty('--windowHeight', window.innerHeight + "px"); + + //filter accordions + for (var i = 0; i < filterTitle.length; i++) { + filterTitle[i].addEventListener('click', function (e) { + filterContent = $(e.target).siblings('.filter__content'); + if ($(e.target).parents('.filter').hasClass('filter--closed')) { + $(e.target).parents('.filter').removeClass('filter--closed'); + filterContent.slideDown(240, function () { + filterContent.removeAttr('style'); + }); + if ($('.filter:not(.filter--closed)').not($(e.target).parents('.filter')).length > 0) { + $('.filter:not(.filter--closed)').not($(e.target).parents('.filter')).addClass('filter--closed'); + //$('.filter:not(.filter--closed)').not($(e.target).parents('.filter')).find('.filter__title').trigger('click'); + } + } else { + filterContent.slideUp(240, function () { + $(e.target).parents('.filter').addClass('filter--closed'); + filterContent.removeAttr('style'); + }); + } + }); + }; + + //mobile filter back + for (var i = 0; i < filterBack.length; i++) { + filterBack[i].addEventListener('click', function (e) { + e.target.closest('.filters__item').classList.remove('filters__item--opened'); + element.classList.remove('filters--2levelOpened'); + }) + } + + //mobile filter button + //function scrollEvents() { + // var button = document.getElementsByClassName("filters__mobButton")[0]; + // if (window.scrollY > window.innerHeight * 0.5) { + // button.classList.add("filters__mobButton--mini"); + // } else { + // button.classList.remove("filters__mobButton--mini"); + // } + //} + + function resizeEvents() { + //close mobile menu on desktop + if (window.innerWidth >= 1024 && document.documentElement.classList.contains('filtersOpened')) { + element.classList.remove('filters--opened'); + element.classList.remove('filters--2levelOpened'); + element.querySelector('.filters__item').classList.remove('filters__item--opened'); + document.documentElement.classList.remove('filtersOpened'); + element.style.setProperty('--mobFiltersTop', 0 + "px"); + document.querySelector('.mainContainer').style.transform = ''; + } + + ////mobile filters Height + if (window.innerWidth < 1024) { + document.documentElement.style.setProperty('--windowHeight', window.innerHeight + "px"); + } + } + + //scrollEvents(); + + //window.addEventListener('scroll', EventUtils.throttle(scrollEvents, 50)); + window.addEventListener('resize', throttle(resizeEvents, 200)); + + element.querySelector('.filters__mobButton').addEventListener('click', function (e) { + openFilters(); + }); + + for (var i = 0; i < closeButton.length; i++) { + closeButton[i].addEventListener('click', function (e) { + closeFilters(); + }) + } + + var positionTop; + function openFilters() { + positionTop = -window.pageYOffset; + element.classList.add('filters--opened'); + document.documentElement.classList.add('filtersOpened'); + element.style.setProperty('--mobFiltersTop', -positionTop + "px"); + document.querySelector('.mainContainer').style.transform = "translateY(" + positionTop + "px)"; + } + + function closeFilters() { + element.classList.remove('filters--opened'); + element.classList.remove('filters--2levelOpened'); + setTimeout(function () { element.querySelector('.filters__item--opened').classList.remove('filters__item--opened'); }, 350); + document.documentElement.classList.remove('filtersOpened'); + element.style.setProperty('--mobFiltersTop', 0 + "px"); + document.querySelector('.mainContainer').style.transform = ''; + + window.scrollTo({ + top: -positionTop + }); + } + + document.addEventListener('click', function (event) { + if (!$('.filter').not('.filter--closed').length || $(event.target).parents('.filter').length) { + return; + } + + $('.filter').not('.filter--closed').find('.filter__title').trigger('click'); + }); +}); \ No newline at end of file diff --git a/html/content/js/Filters/filtersBar.js b/html/content/js/Filters/filtersBar.js new file mode 100644 index 0000000..feb3cd9 --- /dev/null +++ b/html/content/js/Filters/filtersBar.js @@ -0,0 +1,32 @@ +$$plugins.define('filtersBar', ['domHelper'], function (element, options, $) { + //clear filter item + //$('.selectedFilters').on('click', '.selectedFilters__remove', function () { + // var filterItemId = $(this).parents('.selectedFilters__item').data('id'), + // filterName = $(this).parents('.selectedFilters__item').data('name'), + // filterGroup = $('.filters__item[data-input-name=' + filterName + ']'); + + // if ($("#" + filterItemId).hasClass('checkbox__input') || $("#" + filterItemId).hasClass('radiobutton__input')) { + // $("#" + filterItemId).prop("checked", false); + // $(this).parents('.selectedFilters__item').remove(); + // $('.filter__selectedValItem[data-id=' + filterItemId + ']').remove(); + // } + + // if ($("#" + filterItemId).hasClass('range')) { + // $(this).parents('.selectedFilters__item').remove(); + // $("#" + filterItemId).parents('.filters__item').find('.filtersClear').trigger('click'); + // } + + // if (filterGroup.find('.filter__input:checked').length >= 1) { + // filterGroup.addClass('filter--checked'); + // } + // else { + // filterGroup.removeClass('filter--checked'); + // } + + // if ($('.selectedFilters__item').length > 1) { + // $('.selectedFilters').addClass('active'); + // } else { + // $('.selectedFilters').removeClass('active'); + // } + //}); +}); diff --git a/html/content/js/Filters/range.js b/html/content/js/Filters/range.js new file mode 100644 index 0000000..fb8c941 --- /dev/null +++ b/html/content/js/Filters/range.js @@ -0,0 +1,98 @@ +$$plugins.define('range', ['domHelper', 'nouislider', 'wNumb', 'debounce'], function (element, options, $, noUiSlider, wNumb, debounce) { + var slider = element.querySelector('.range__slider'), + inputs = $('.range__input--min,.range__input--max'), + selectedMin = element.closest('.filters__item').querySelector('.filter__selectedPriceMin'), + selectedMax = element.closest('.filters__item').querySelector('.filter__selectedPriceMax'); + + + noUiSlider.create(slider, { + start: [options.selectedMin, options.selectedMax], + connect: true, + tooltips: [wNumb({ decimals: 0, postfix: '€' }), wNumb({ decimals: 0, postfix: '€' })], + range: { + 'min': options.initMin, + 'max': options.initMax + } + }); + + + slider.noUiSlider.on('update', function (values, handle) { + var input = inputs.get(handle); + var previousValue = input.value; + + input.value = parseFloat(values[handle]).toFixed(0); + if (input.value == previousValue) { + return; + } + changing = true; + + $(input).trigger('change'); + changing = false; + + selectedMin.innerHTML = slider.noUiSlider.get(true)[0].toFixed(0); + selectedMax.innerHTML = slider.noUiSlider.get(true)[1].toFixed(0); + }); + + var changing = false; + var setValue = debounce(function (handle, value) { + slider.noUiSlider.setHandle(handle, value); + $(inputs.get(handle)).trigger('change'); + }, 500); + + inputs.each(function (input, handle) { + $(input).on('change', function () { + if (changing) { + return; + } + slider.noUiSlider.setHandle(handle, this.value); + }); + + $(input).on('keydown', function (e) { + var value = Number($(this).val()); + var steps = slider.noUiSlider.steps(); + + var step = steps[handle]; + var position; + switch (e.which) { + //enter + case 13: + slider.noUiSlider.setHandle(handle, this.value); + break; + // key up + case 38: + e.preventDefault(); + // Get step to go increase slider value (up) + position = step[1]; + + // false = no step is set + if (position === false) { + position = 1; + } + + // null = edge of slider + if (position !== null) { + setValue(handle, input.value = value + position) + } + + break; + // key down + case 40: + e.preventDefault(); + position = step[0]; + + if (position === false) { + position = 1; + } + + if (position !== null) { + setValue(handle, input.value = value - 1) + } + + break; + } + input.focus(); + }); + }); + + +}); diff --git a/html/content/js/Forms/ajaxForm.js b/html/content/js/Forms/ajaxForm.js new file mode 100644 index 0000000..6ade446 --- /dev/null +++ b/html/content/js/Forms/ajaxForm.js @@ -0,0 +1,188 @@ +$.defineAttributePlugin('ajaxform', ['serialize', 'debounce'], function ($element, options, serialize, debounce) { + options = options || {}; + options.Url = options.Url || $element.attr('action'); + + var loader = ''; + var submit; + var onError = debounce(scrollToFirstError, 100); + + onError(); + + function getElementToReplace() { + var $toReplace; + if (options.ElementID) { + $toReplace = $("#" + options.ElementID); + } + else { + $toReplace = $element.closest('.ajax-replace-container'); + if ($toReplace.length === 0) { + $toReplace = $element.closest('div.form'); + } + if ($toReplace.length === 0) { + $toReplace = $element; + } + } + return $toReplace; + } + + function ajaxComplete(response, $element) { + var flow = response.getResponseHeader("X-FlowComplete"); + + if (flow) { + + $element.trigger('flowcomplete', flow); + } + } + + function createReplacement(content) { + return $(content); // $('
' + content + '
'); //.find('> *'); + } + var submitting = false; + submit = function () { + if (!options.Url || submitting) { + return; + } + submitting = true; + mask(); + var headers = { + "X-NoRedirect": "true" + }; + var $toReplace = getElementToReplace(); + if ($toReplace.is('.ajax-modal')) { + headers["X-Modal"] = "true"; + } + var result = { + redirected: false + }; + + var ajaxOptions = { + url: options.Url, + headers: headers, + beforeSend: function () { + $('body').trigger('TopLoader:Show'); + $element.find('[type="submit"]').attr('disabled', 'disabled'); + }, + type: 'post', + success: function (content, status, response) { + if (content.RedirectUrl) { + result.redirected = true; + window.location.href = content.RedirectUrl; + return; + } + if (content) { + var $replacement = createReplacement(content); + $toReplace.replaceWith($replacement); + $replacement.parseAttributePlugins(); + ajaxComplete(response, $replacement); + $replacement.trigger('ajaxForm.submitted'); + } + else { + ajaxComplete(response, $element); + } + + + }, + complete: function () { + $('body').trigger('TopLoader:Hide'); + if (!result.redirected) { + unmask(); + $element.find('[clicked]').removeAttr('clicked'); + $element.find('[type="submit"]').removeAttr('disabled'); + submitting = false; + } + }, + error: function (r) { + if (r.status === 401) { + location.reload(); + return; + } + } + }; + + if ($element.is("form") && $element.prop("enctype") === "multipart/form-data") { + ajaxOptions.data = new FormData($element.get(0)); + ajaxOptions.contentType = false; + ajaxOptions.processData = false; + } else { + var $elements = $element.find('input[name], textarea[name], select[name], :submit[name][clicked=true]') + ajaxOptions.data = serialize($elements); + } + + $.ajax(ajaxOptions); + }; + + function mask() { + $element.find('[clicked]').addClass('in-progress').append(loader); + $element.trigger('ajaxform.submitting'); + } + function unmask() { + $element.find('[clicked]').removeClass('in-progress').find('.ajax-loader').remove(); + $element.trigger('ajaxform.complete'); + } + + if (options.LazyVerification) { + var s = submit; + submit = function () { + $.ajax({ + url: options.LazyVerification, + beforeSend: function () { + $element.find('[type="submit"]').attr('disabled', 'disabled'); + }, + type: 'post', + success: function (content, status, response) { + $element.append(content); + s(); + }, + complete: function () { + $element.find('[type="submit"]').removeAttr('disabled'); + } + }); + }; + } + $element.find("[type=submit]").click(function () { + $element.find("[type=submit]").removeAttr("clicked"); + $(this).attr("clicked", "true"); + }); + if ($element.is('form')) { + $element.on('submit', function (e) { + + if (options.Url) { + e.preventDefault(); + } + var clickedButton = $element.find('[clicked]'); + if (!clickedButton.is('.cancel')) { + if (!$element.valid()) { + console.warn('validation error'); + onError(); + return; + } + } + submit(); + }); + } + else { + $element.find('[type="submit"]').click(function (e) { + e.preventDefault(); + e.stopPropagation(); + submit(); + }); + } + $element.data('ajaxform.submit', submit); + + function scrollToFirstError() { + var $erroneousElement = $element.find(".field-validation-error").eq(0).parents(".field"); + + if ($erroneousElement.length === 1) { + var top = $erroneousElement.offset().top; + var $header = $('.headerWrp'); + if ($header.length) { + top -= $header.height() + 80; + } + $('html,body').animate({ + scrollTop: top + }, 500); + $erroneousElement.find("input").focus(); + } + } + +}); diff --git a/html/content/js/Forms/clearinput.js b/html/content/js/Forms/clearinput.js new file mode 100644 index 0000000..903244f --- /dev/null +++ b/html/content/js/Forms/clearinput.js @@ -0,0 +1,16 @@ +$$plugins.define('clearinput', function (element, options) { + var input = element.previousElementSibling; + + input.addEventListener("keyup", function (e) { + e.preventDefault(); + if (input.value !== "") { + element.classList.add("active"); + } + }); + + element.addEventListener("click", function (e) { + e.preventDefault(); + input.value = ""; + element.classList.remove("active"); + }); +}); diff --git a/html/content/js/Forms/customValidators.js b/html/content/js/Forms/customValidators.js new file mode 100644 index 0000000..3ebecf9 --- /dev/null +++ b/html/content/js/Forms/customValidators.js @@ -0,0 +1,11 @@ +define(['jQuery', 'jquery.validate', 'jquery.validate.unobtrusive'], function () { + $.validator.unobtrusive.adapters.add("requirechecked", [], + function (options) { + options.rules['requirechecked'] = 'requirechecked'; + options.messages['requirechecked'] = options.message; + } + ); + $.validator.addMethod("requirechecked", function (value, element, e) { + return $(element).is(':checked'); + }); +}); \ No newline at end of file diff --git a/html/content/js/Forms/password.js b/html/content/js/Forms/password.js new file mode 100644 index 0000000..c0ccbef --- /dev/null +++ b/html/content/js/Forms/password.js @@ -0,0 +1,15 @@ +$$plugins.define('password', function (element, options) { + var input = element.previousElementSibling; + + element.addEventListener("click", function (e) { + e.preventDefault(); + if (input.type === 'password') { + input.type = 'text'; + element.classList.add('disabled'); + } + else { + input.type = 'password'; + element.classList.remove('disabled'); + } + }); +}); diff --git a/html/content/js/Forms/select.js b/html/content/js/Forms/select.js new file mode 100644 index 0000000..3ea5435 --- /dev/null +++ b/html/content/js/Forms/select.js @@ -0,0 +1,52 @@ +$$plugins.define('select', ['domHelper'], function (element, options, $) { + var list = element.querySelector('.select__listWrap'), + selected = $('.select__button'), + selectedMob = element.querySelector('.select__button--mobile'), + input = element.querySelector('.select__input'), + options = $('.select__option'); + + function open() { + element.classList.add('select--open'); + $(list).slideDown(250); + } + + function close() { + $(list).slideUp(250, function () { + element.classList.remove('select--open'); + }); + } + + document.addEventListener('click', function (event) { + if (!isOpen() || element == event.target || $(event.target).parents().contains(element)) { + return; + } + close(); + }); + + selected.on("click", function (e) { + e.preventDefault(); + if (element.classList.contains('select--open')) { + close(); + } else { + open(); + } + }); + + function isOpen() { + return element.classList.contains('select--open'); + } + + options.on('click', function (e) { + e.target.classList.add('selected'); + prevActiveOption = Object.values(element.querySelectorAll('.selected')).filter(prevActive => prevActive !== e.target); + if (prevActiveOption.length != 0) { + prevActiveOption[0].classList.remove('selected'); + } + selected.text(e.target.innerHTML); + if (selectedMob) { + selectedMob.innerHTML = e.target.innerHTML; + } + $(input).val(e.target.dataset.value).trigger('change'); + close(); + }); +}); diff --git a/html/content/js/Forms/serialize.js b/html/content/js/Forms/serialize.js new file mode 100644 index 0000000..0bdd44c --- /dev/null +++ b/html/content/js/Forms/serialize.js @@ -0,0 +1,30 @@ +define(function () { + return function ($elements) { + var values = {}; + $elements.each(function () { + var $input = $(this); + var value = $input.val(); + if (!value) { + return; + } + if ($input.is(':checkbox, :radio') && !$input.is(':checked')) { + return; + } + var name = $(this).attr('name'); + var v = values[name] || []; + values[name] = v; + v.push(value); + }); + var s = ''; + for (var k in values) { + for (var i = 0; i < values[k].length; i++) { + s += '&' + k + '=' + encodeURIComponent(values[k][i]); + } + } + if (s) { + s = s.substr(1); + } + return s; + }; + +}); \ No newline at end of file diff --git a/html/content/js/Forms/textarea.js b/html/content/js/Forms/textarea.js new file mode 100644 index 0000000..493c78f --- /dev/null +++ b/html/content/js/Forms/textarea.js @@ -0,0 +1,26 @@ +$$plugins.define('textarea', function (element, options) { + const textarea = element.querySelector('textarea'); + + //style focus + textarea.addEventListener('focus', (event) => { + textarea.closest(".textarea").classList.add("focus"); + }); + + textarea.addEventListener('blur', (event) => { + textarea.closest(".textarea").classList.remove("focus"); + }); + + //characters count + textarea.addEventListener("input", event => { + const target = event.currentTarget; + const maxLength = target.getAttribute("maxlength"); + const currentLength = target.value.length; + const currentLengthText = document.getElementsByClassName("textarea__remaining")[0]; + + currentLengthText.innerHTML = currentLength; + + if (currentLength >= maxLength) { + return; + } + }); +}); diff --git a/html/content/js/Forms/validateform.js b/html/content/js/Forms/validateform.js new file mode 100644 index 0000000..38629e2 --- /dev/null +++ b/html/content/js/Forms/validateform.js @@ -0,0 +1,3 @@ +$.defineAttributePlugin('validateform', ['jQuery', 'jquery.validate', 'jquery.validate.unobtrusive', 'customValidators'], function ($element) { + $.validator.unobtrusive.parse($element); +}); \ No newline at end of file diff --git a/html/content/js/Images/lazyload.js b/html/content/js/Images/lazyload.js new file mode 100644 index 0000000..4f954af --- /dev/null +++ b/html/content/js/Images/lazyload.js @@ -0,0 +1,61 @@ +$$plugins.define('lazyload', function (element, options) { + const plugin = this; + plugin.logger.debug("lazy loading"); + const mutationObserver = + new MutationObserver(function (mutations) { + mutations.forEach(function (mutation, index) { + if (mutation.type === 'childList') { + mutation.addedNodes.forEach(update); + } + }); + }), + observer = + new IntersectionObserver(function (entries, self) { + entries.forEach(function (entry) { + if (entry.isIntersecting) { + load(entry.target); + self.unobserve(entry.target); + } + }); + }, options || { + rootMargin: '0px 0px 1250px 0px', + threshold: 0 + }), + elms = element.querySelectorAll('.lazy'); + elms.forEach(elm => observer.observe(elm)); + mutationObserver.observe(element, { childList: true, subtree: true }); + + function update(element) { + if (!(element instanceof (HTMLElement))) { + return; + } + const container = element || document; + if (!container.querySelectorAll) { + return; + } + const elms = container.querySelectorAll('.lazy'); + Array.prototype.forEach.call(elms, (elm) => !elm.getAttribute('data-loaded') && observer.observe(elm)); + } + + function load(elm) { + let i = 0, source, attr, picture = elm.parentElement, sources = picture.querySelectorAll('.lazysrcset'); + if (sources.length) { + for (; i < sources.length; i++) { + source = sources[i]; + attr = source.getAttribute('data-srcset'); + if (attr) { + source.srcset = source.getAttribute('data-srcset'); + } + } + } + + if (!elm.getAttribute('data-loaded') && elm.getAttribute('data-src')) { + elm.src = elm.getAttribute('data-src'); + elm.setAttribute('data-loaded', true); + } + } + + return { + update: update + }; +}); \ No newline at end of file diff --git a/html/content/js/Modals/modal.js b/html/content/js/Modals/modal.js new file mode 100644 index 0000000..7ce9145 --- /dev/null +++ b/html/content/js/Modals/modal.js @@ -0,0 +1,6 @@ +$$plugins.define('modal', ['domHelper'], function (element, options, $) { + var close = $(element).find('.modal__close'); + close.on('click', function () { + $(element).removeClass('open'); + }); +}); diff --git a/html/content/js/Plugins/accordion.js b/html/content/js/Plugins/accordion.js new file mode 100644 index 0000000..62e25ad --- /dev/null +++ b/html/content/js/Plugins/accordion.js @@ -0,0 +1,43 @@ +$$plugins.define('accordion', ['domHelper'], function (element, options, $) { + var animating = false, + $header = $(element).find('.acc:not(.acc--disabled) .acc__header'), + $content, + $prevActiveAcc, + $this, + $accItem, + singleOpen = options.singleOpen ? options.singleOpen : false; + + $header.on('click', function (e) { + $this = $(this); + $accItem = $this.parent('.acc'); + $content = $this.next('.acc__content'); + if (animating === true) { + return; + } + animating = true; + if ($accItem.is('.open')) { + $content.slideUp(300, function () { + $accItem.removeClass('open'); + $content.removeAttr('style'); + animating = false; + }); + } else { + $content.slideDown(300, function () { + $accItem.addClass('open'); + $content.removeAttr('style'); + animating = false; + }); + + if (singleOpen) { + $prevActiveAcc = $accItem.siblings('.acc.open'); + if ($prevActiveAcc.length != 0) { + $prevActiveAcc.find('.acc__content').slideUp(300, function () { + $prevActiveAcc.removeClass('open'); + $prevActiveAcc.find('.acc__content').removeAttr('style'); + animating = false; + }); + } + } + } + }); +}); diff --git a/html/content/js/Plugins/breadcrumb.js b/html/content/js/Plugins/breadcrumb.js new file mode 100644 index 0000000..112824b --- /dev/null +++ b/html/content/js/Plugins/breadcrumb.js @@ -0,0 +1,9 @@ +$$plugins.define('breadcrumb', function (element, options) { + var scrollPos = element.querySelector('.breadcrumb__item:last-child').offsetLeft - element.offsetLeft; + + element.scroll({ + top: 0, + left: scrollPos, + behavior: 'smooth' + }); +}); diff --git a/html/content/js/Plugins/copy.js b/html/content/js/Plugins/copy.js new file mode 100644 index 0000000..c33430f --- /dev/null +++ b/html/content/js/Plugins/copy.js @@ -0,0 +1,25 @@ +$$plugins.define('copy', function (element, options) { + //copyButton: data-copy-btn + //content to be copied: data-copy-text + //tooltip: data-copy-tooltip + + const copyButton = element, + copyButtonId = copyButton.getAttribute('data-copy-btn'), + copyText = document.querySelector(`[data-copy-text='${copyButtonId}']`), + tooltip = document.querySelector(`[data-copy-tooltip='${copyButtonId}']`); + + function copy() { + var r = document.createRange(); + r.selectNode(copyText); + window.getSelection().removeAllRanges(); + window.getSelection().addRange(r); + document.execCommand('copy'); + window.getSelection().removeAllRanges(); + tooltip.classList.add('copyText__tooltip--open'); + setTimeout(function () { + tooltip.classList.remove('copyText__tooltip--open'); + }, 1600); + } + + copyButton.addEventListener("click", copy); +}); diff --git a/html/content/js/Plugins/expand.js b/html/content/js/Plugins/expand.js new file mode 100644 index 0000000..533e978 --- /dev/null +++ b/html/content/js/Plugins/expand.js @@ -0,0 +1,38 @@ +$$plugins.define('expand', ['domHelper'], function (element, options, $) { + //triggerButton: data-trigger-expand + //content to be initialized: data-content-expand + const triggerButton = element.querySelector('[data-trigger-expand]'), + triggerButtonId = triggerButton.getAttribute('data-trigger-expand'), + content = $(`[data-content-expand='${triggerButtonId}']`); + + var $triggerButton = $(triggerButton); + + $triggerButton.on('click', function (e) { + e.preventDefault(); + if ($triggerButton.hasClass('active')) { + content.slideUp(300, function () { + $triggerButton.removeClass('active'); + content.removeAttr('style'); + }); + } else { + $triggerButton.addClass('active'); + content.slideDown(300, function () { + if (content.find('.scrollbarShadow').length > 0 && !content.find('.scrollbarShadow').hasClass('initialized')) { + content.find('.scrollbarShadow').addClass('initialized'); + } + }); + } + }); + + if (options.closeOnCLickAway == true) { + document.addEventListener('click', function (event) { + if (!$triggerButton.hasClass('active') || element == event.target || $(event.target).parents().contains(element)) { + return; + } + content.slideUp(300, function () { + $triggerButton.removeClass('active'); + content.removeAttr('style'); + }); + }); + } +}); diff --git a/html/content/js/Plugins/expandText.js b/html/content/js/Plugins/expandText.js new file mode 100644 index 0000000..0b58cb7 --- /dev/null +++ b/html/content/js/Plugins/expandText.js @@ -0,0 +1,5 @@ +$$plugins.define('expandText', function (element, options) { + element.querySelector('.expandText__button').addEventListener('click', () => { + element.classList.toggle('open'); + }); +}); diff --git a/html/content/js/Plugins/fileinput.js b/html/content/js/Plugins/fileinput.js new file mode 100644 index 0000000..10ba891 --- /dev/null +++ b/html/content/js/Plugins/fileinput.js @@ -0,0 +1,20 @@ +$$plugins.define('fileinput', function (element, options) { + const inputfile = element.querySelector('.file-upload__input'); + const filedelete = element.querySelector('.file-upload__delete'); + + inputfile.addEventListener('change', (event) => { + const inputname = element.querySelector('.file-upload__name'); + const filename = inputfile.files[0].name; + inputname.innerText = filename; + element.classList.add('file-upload__filled'); + }); + + filedelete.addEventListener('click', (event) => { + const inputname = element.querySelector('.file-upload__name'); + + inputname.innerText = ""; + element.classList.remove('file-upload__filled'); + inputfile.value = ""; + }); + +}); diff --git a/html/content/js/Plugins/gallery.js b/html/content/js/Plugins/gallery.js new file mode 100644 index 0000000..6ac147a --- /dev/null +++ b/html/content/js/Plugins/gallery.js @@ -0,0 +1,51 @@ +$$plugins.define('gallery', ['swiper-bundle', 'domHelper'], function (element, options, Swiper, $) { + var $galleryImg = $('.gallery__figure img'), + thumbs = document.querySelector('.galleryThumbs'), + sliderModal = document.querySelector('.gallerySliderModal'), + $image; + + var swiperThumbs = new Swiper(".galleryThumbs", { + direction: "horizontal", + freeMode: true, + init: false, + slidesPerView: 4, + spaceBetween: 8, + speed: 800, + watchSlidesProgress: true, + pagination: { + el: '.swiper-pagination', + }, + breakpoints: { + 768: { + slidesPerView: 5, + spaceBetween: 17, + } + }, + }); + + var swiperMain = new Swiper(".galleryMainImg", { + init: false, + loop: true, + navigation: { + nextEl: ".swiper-button-next", + prevEl: ".swiper-button-prev", + }, + speed: 800, + thumbs: { + swiper: swiperThumbs, + }, + }); + + $galleryImg.on('click', function () { + if (!thumbs.classList.contains('swiper-initialized')) { + swiperThumbs.init(); + swiperMain.init(); + } + sliderModal.classList.add('open'); + $image = $(this).parents('.gallery__figure'); + swiperMain.slideTo($image.index() + 1, 0); + + //document.documentElement.classList.add('noScroll'); + + }); +}); \ No newline at end of file diff --git a/html/content/js/Plugins/infoPopup.js b/html/content/js/Plugins/infoPopup.js new file mode 100644 index 0000000..e11ad16 --- /dev/null +++ b/html/content/js/Plugins/infoPopup.js @@ -0,0 +1,8 @@ +$$plugins.define('infoPopup', function (element, options) { + element.addEventListener('click', event => { + element.closest('.infoPopup').classList.add('moveOut'); + setTimeout(function () { + element.closest('.infoPopup').remove() + }, 500); + }); +}); diff --git a/html/content/js/Plugins/initialize.js b/html/content/js/Plugins/initialize.js new file mode 100644 index 0000000..ecdca48 --- /dev/null +++ b/html/content/js/Plugins/initialize.js @@ -0,0 +1,11 @@ +$$plugins.define('initialize', function (element, options) { + //triggerButton: data-trigger-initialize + //content to be initialized: data-content-initialize + + const triggerButton = element.getAttribute('data-trigger-initialize'), + content = document.querySelector(`[data-content-initialize='${triggerButton}']`); + + element.addEventListener('click', function (event) { + content.classList.add("initialized"); + }); +}); diff --git a/html/content/js/Plugins/intersecting.js b/html/content/js/Plugins/intersecting.js new file mode 100644 index 0000000..87faf61 --- /dev/null +++ b/html/content/js/Plugins/intersecting.js @@ -0,0 +1,18 @@ +$$plugins.define('intersecting', function (element, options) { + const el = document.querySelector(options.target); + + const observer = new window.IntersectionObserver(([entry]) => { + const intersectionRatio = entry.intersectionRatio.toFixed(2); + if (entry.isIntersecting) { + element.classList.add("sticky"); + } else { + element.classList.remove("sticky"); + } + }, { + root: null, + rootMargin: '-30% 0px -70%', + threshold: 0 + }) + + observer.observe(el) +}); diff --git a/html/content/js/Plugins/menu.js b/html/content/js/Plugins/menu.js new file mode 100644 index 0000000..d6dced2 --- /dev/null +++ b/html/content/js/Plugins/menu.js @@ -0,0 +1,97 @@ +$$plugins.define('menu', ['domHelper'], function (element, options, $) { + var level1 = $('.mainNav__link--hasChildren', element), + closeElements = $('.mainLayer, .megamenu__close'), + body = $('body'), + burgerMenu = $('.burgerMenu'), + mainContainer = $('.mainContainer'), + header = $('.header'), + megaMenuBack = $('.megamenu__back'), + positionTopDesktop, + scrollBarWidth; + + + level1.on('click', function (event) { + event.preventDefault(); + var $this = $(this); + if ($this.parent('.mainNav__item').is('.mainNav__item--active')) { + closeMenuLevel1($this); + } + else { + openMenuLevel1($this); + } + }); + + //close menu + closeElements.on('click', function () { + if (window.innerWidth < 1024) { + closeMobileMenu(); + } else { + closeMenuLevel1($('.mainNav__item--active .mainNav__link')); + } + }); + + function openMenuLevel1($activeItem) { + var $activeItemParent = $activeItem.parent('.mainNav__item'); + + positionTopDesktop = -window.scrollY; + scrollBarWidth = window.innerWidth - mainContainer.innerWidth(); + + var activeItemsExceptCurrent = $('.mainNav__item--active').not($activeItemParent); + if (activeItemsExceptCurrent.length > 0) { + activeItemsExceptCurrent.removeClass('mainNav__item--active').addClass('mainNav__item--fadeOut'); + setTimeout(function () { + activeItemsExceptCurrent.removeClass('mainNav__item--fadeOut'); + }, 350); + } + + $activeItemParent.addClass('mainNav__item--active'); + body.addClass('menuOpened menuOpened--level2').each(e => e.style.paddingRight = scrollBarWidth + "px"); + burgerMenu.addClass('burgerMenu--opened'); + + mainContainer.each(e => e.style.transform = "translateY(" + positionTopDesktop + "px)"); + header.each(e => e.style.width = "calc(100vw - " + scrollBarWidth + "px)"); + } + + function closeMenuLevel1($activeItem) { + var $activeItemParent = $activeItem.parent('.mainNav__item'); + $activeItemParent.removeClass('mainNav__item--active'); + body.removeClass('menuOpened').each(e => e.style.paddingRight = ""); + burgerMenu.removeClass('burgerMenu--opened'); + mainContainer.each(e => e.style.transform = ""); + header.each(e => e.style.width = ""); + body.scroll({ + top: -positionTopDesktop + }); + } + + burgerMenu.on('click', function (e) { + e.preventDefault(); + if (e.target.matches('.burgerMenu--opened')) { + closeMobileMenu(); + } else { + openMobileMenu(); + } + }); + + var positionTop; + function openMobileMenu() { + positionTop = -window.scrollY; + burgerMenu.addClass('burgerMenu--opened'); + body.addClass('menuOpened menuOpened--level2'); + mainContainer.each(e => e.style.transform = "translateY(" + positionTop + "px)"); + } + + function closeMobileMenu() { + burgerMenu.removeClass('burgerMenu--opened'); + body.removeClass('menuOpened menuOpened--level2'); + mainContainer.each(e => e.style.transform = ""); + body.scroll({ + top: -positionTop + }); + } + + megaMenuBack.on('click', function (event) { + $('.mainNav__item--active').removeClass('mainNav__item--active'); + body.removeClass('menuOpened--level2'); + }); +}); diff --git a/html/content/js/Plugins/quantity.js b/html/content/js/Plugins/quantity.js new file mode 100644 index 0000000..f124c63 --- /dev/null +++ b/html/content/js/Plugins/quantity.js @@ -0,0 +1,55 @@ +$$plugins.define('quantity', ['dom'], function (element, options, dom) { + //const plugin = this, + // increaseButton = element.querySelector('.quantity__btn--inc'), + // decreaseButton = element.querySelector('.quantity__btn--dec'), + // input = element.querySelector('input'), + // maxQuantity = options.MaxQuantity, + // minQuantity = options.MinQuantity || 1; + + //let currentValue = parseInt(input.value); + + //checkButtons(); + //plugin.on('click', increaseButton, updateValue); + //plugin.on('click', decreaseButton, updateValue); + + //function updateValue(e) { + // const button = dom.closest(e.target, '[data-ref="quantity-control"]'), + // previousValue = currentValue; + // if (!button || button.classList.contains('disabled')) { + // return false; + // } + // if (button === increaseButton) { + // currentValue += 1; + // } else { + // currentValue -= 1; + // } + + // console.log(button) + // input.value = currentValue; + // checkButtons(); + // if (options.ItemId) { + // plugin.emit('cart:quantity.changed', { + // quantity: currentValue, + // item: options.ItemId, + // element: e.target, + // fail: function onFail() { + // currentValue = previousValue; + // } + // }); + // } + //} + + //function checkButtons() { + // if (currentValue > minQuantity) { + // decreaseButton.classList.remove('disabled'); + // } else { + // decreaseButton.classList.add('disabled'); + // } + + // if (currentValue < maxQuantity) { + // increaseButton.classList.remove('disabled'); + // } else { + // increaseButton.classList.add('disabled'); + // } + //} +}); diff --git a/html/content/js/Plugins/scrollShadowHor.js b/html/content/js/Plugins/scrollShadowHor.js new file mode 100644 index 0000000..eed0325 --- /dev/null +++ b/html/content/js/Plugins/scrollShadowHor.js @@ -0,0 +1,42 @@ +$$plugins.define('scrollShadowHor', function (element, options) { + var scrollElm = element.querySelector('.scrollbar'), + listInner = element.querySelector('.scrollbar__inner'); + + const observer = new MutationObserver(function(mutations) { + var childrenMutations = mutations.filter((x) => x.type === "childList"); + if (childrenMutations.length > 0){ + checkScrollPos(scrollElm); + } + }); + + observer.observe(listInner, { subtree: true, childList: true }); + + checkScrollPos(scrollElm); + + scrollElm.addEventListener("scroll", function () { + checkScrollPos(this); + }); + + function checkScrollPos(scrollContainer) { + if (scrollContainer.scrollLeft + element.offsetWidth > listInner.offsetWidth - 8) { + element.classList.add('scrollbarShadow--right'); + } else { + element.classList.remove('scrollbarShadow--right'); + } + if (scrollContainer.scrollLeft < 8) { + element.classList.add('scrollbarShadow--left'); + } else { + element.classList.remove('scrollbarShadow--left'); + } + } + + //checkScrollInit(); + + //function checkScrollInit() { + // if (element.offsetWidth >= listInner.offsetWidth) { + // element.classList.add('scrollbarShadow--noScroll'); + // } else { + // element.classList.remove('scrollbarShadow--noScroll'); + // } + //} +}); diff --git a/html/content/js/Plugins/scrollShadowVert.js b/html/content/js/Plugins/scrollShadowVert.js new file mode 100644 index 0000000..2824c22 --- /dev/null +++ b/html/content/js/Plugins/scrollShadowVert.js @@ -0,0 +1,57 @@ +$$plugins.define('scrollShadowVert', function (element, options) { + var scrollElm = element.querySelector('.scrollbar'), + listInner = element.querySelector('.scrollbar__inner'); + + //observer for adding/removing children elements + const observer = new MutationObserver(function (mutations) { + var childrenMutations = mutations.filter((x) => x.type === "childList"); + if (childrenMutations.length > 0) { + checkScrollPos(scrollElm); + } + }); + + observer.observe(listInner, { subtree: true, childList: true }); + + //observer for initialized class + if (options.initialize == true) { + const targetNode = element; + + const observer2 = new MutationObserver(function (mutations) { + var classMutations = mutations.filter((x) => x.type === "attributes"); + if (classMutations.length > 0 && targetNode.classList.contains('initialized')) { + checkScrollPos(scrollElm); + observer2.disconnect(); + } + }); + observer2.observe(targetNode, { attributes: true, attributeFilter: ['class'] }); + } + + checkScrollPos(scrollElm); + + scrollElm.addEventListener("scroll", function () { + checkScrollPos(this); + }); + + function checkScrollPos(scrollContainer) { + if (scrollContainer.scrollTop + element.offsetHeight > listInner.offsetHeight - 8) { + element.classList.add('scrollbarShadow--bottom'); + } else { + element.classList.remove('scrollbarShadow--bottom'); + } + if (scrollContainer.scrollTop < 8) { + element.classList.add('scrollbarShadow--top'); + } else { + element.classList.remove('scrollbarShadow--top'); + } + } + + //checkScrollInit(); + + //function checkScrollInit() { + // if (element.offsetHeight >= listInner.offsetHeight) { + // element.classList.add('scrollbarShadow--noScroll'); + // } else { + // element.classList.remove('scrollbarShadow--noScroll'); + // } + //} +}); diff --git a/html/content/js/Plugins/tabs.js b/html/content/js/Plugins/tabs.js new file mode 100644 index 0000000..74d4efc --- /dev/null +++ b/html/content/js/Plugins/tabs.js @@ -0,0 +1,28 @@ +$.defineAttributePlugin('tabs', function ($element, options) { + var animating = false, + dataTab, + slideSpeed = options.slideSpeed ? options.slideSpeed : 400; + + if (!$element.find("[data-tab-trigger]").hasClass('active')) { + setTimeout(function () { $element.find("[data-tab-trigger]").first().trigger('click'); }, 50); + } + + $element.find("[data-tab-trigger]").click(function (e) { + e.preventDefault(); + if (animating === true || $(this).hasClass('active')) { + return; + } + animating = true; + $(this).addClass('active').siblings().removeClass('active'); + dataTab = $(this).data('tab-trigger'); + + $('[data-tab-content="' + dataTab + '"]').siblings('.active').slideUp(slideSpeed, function () { + $(this).removeClass('active').removeAttr('style'); + }); + + $('[data-tab-content="' + dataTab + '"]').slideDown(slideSpeed, function () { + $(this).addClass('active').removeAttr('style'); + animating = false; + }); + }); +}); diff --git a/html/content/js/Plugins/tabtoggle.js b/html/content/js/Plugins/tabtoggle.js new file mode 100644 index 0000000..84a0508 --- /dev/null +++ b/html/content/js/Plugins/tabtoggle.js @@ -0,0 +1,27 @@ +$$plugins.define('tabtoggle', function (element, options) { + var activeTab = element.querySelector(".active"); + var sliderNav = element.querySelector(".tabToggle__tabs"); + var activeTabContent = document.querySelector("[data-tabToggle-content].active"); + + if (element.querySelector(".tabToggle__tab:nth-child(2)").classList.contains("active")) { + sliderNav.classList.add("slide"); + } + + element.addEventListener("click", function (e) { + if (!(e.target.classList.contains("active"))) { + activeTab.classList.remove("active"); + activeTab = e.target; + + activeTab.classList.add("active"); + sliderNav.classList.toggle("slide"); + activeTabContent.classList.remove("active"); + activeTabContent = document.querySelector("[data-tabToggle-content=" + activeTab.dataset.tabtoggleTarget) + activeTabContent.classList.add("active"); + + window.scrollTo({ + top: activeTabContent.getBoundingClientRect().top + window.scrollY - document.querySelector(".header").offsetHeight, + behavior: 'smooth' + }); + } + }); +}); diff --git a/html/content/js/Plugins/tooltip.js b/html/content/js/Plugins/tooltip.js new file mode 100644 index 0000000..2326caf --- /dev/null +++ b/html/content/js/Plugins/tooltip.js @@ -0,0 +1,45 @@ +$$plugins.define('tooltip', ['throttle'], function (element, options, throttle) { + var openBtn = element.querySelector('.tooltip__btn'), + tooltipMain = element.querySelector('.tooltip__main'); + + openBtn.addEventListener('click', (event) => { + event.preventDefault(); + if (element.classList.contains('tooltip--open')) { + element.classList.remove('tooltip--open'); + } else { + $('.tooltip').not(element).removeClass('tooltip--open'); + element.classList.add('tooltip--open'); + } + }); + + document.addEventListener('click', function (event) { + if (element !== event.target && !element.contains(event.target) && element.classList.contains('tooltip--open')) { + element.classList.remove('tooltip--open'); + } + }); + + function tooltipPosition() { + element.classList.remove('tooltip--bottom'); + element.classList.remove('tooltip--right'); + element.classList.remove('tooltip--left'); + + if (element.getBoundingClientRect().top < window.innerHeight / 2) { + element.classList.add('tooltip--bottom'); + } else { + element.classList.remove('tooltip--bottom'); + } + + if (element.getBoundingClientRect().left < tooltipMain.offsetWidth / 2) { + element.classList.add('tooltip--right'); + } + + if (document.body.clientWidth - element.getBoundingClientRect().left < tooltipMain.offsetWidth / 2) { + element.classList.add('tooltip--left'); + } + } + + tooltipPosition(); + + window.addEventListener('scroll', throttle(tooltipPosition, 50)); + window.addEventListener('resize', throttle(tooltipPosition, 200)); +}); diff --git a/html/content/js/Products/productHeader.js b/html/content/js/Products/productHeader.js new file mode 100644 index 0000000..3e12eee --- /dev/null +++ b/html/content/js/Products/productHeader.js @@ -0,0 +1,21 @@ +$$plugins.define('productHeader', function (element, options) { + const el = document.querySelector('#addToCartButton'); + const header = document.querySelector(".productHeader"); + + const observer = new window.IntersectionObserver(([entry]) => { + if (entry.isIntersecting) { + header.classList.remove("active"); + return + } + if (entry.boundingClientRect.top > 0) { + header.classList.remove("active"); + } else { + header.classList.add("active"); + } + }, { + root: null, + threshold: 0, + }) + + observer.observe(el) +}); diff --git a/html/content/js/Products/productImageModal.js b/html/content/js/Products/productImageModal.js new file mode 100644 index 0000000..9873bed --- /dev/null +++ b/html/content/js/Products/productImageModal.js @@ -0,0 +1,47 @@ +$$plugins.define('productImageModal', ['swiper-bundle', 'domHelper'], function (element, options, Swiper, $) { + var $productImg = $('.productPage__image'), + thumbs = document.querySelector('.productThumbs'), + sliderModal = document.querySelector('.productSliderModal'), + $image; + + var swiperThumbs = new Swiper(".productThumbs", { + direction: "vertical", + freeMode: true, + init: false, + slidesPerView: 'auto', + spaceBetween: 8, + speed: 800, + watchSlidesProgress: true, + breakpoints: { + 0: { + direction: "horizontal", + }, + 640: { + direction: "vertical", + } + } + }); + + var swiperMain = new Swiper(".productMainImg", { + init: false, + loop: true, + navigation: { + nextEl: ".swiper-button-next", + prevEl: ".swiper-button-prev", + }, + speed: 800, + thumbs: { + swiper: swiperThumbs, + }, + }); + + $productImg.on('click', function () { + if (!thumbs.classList.contains('swiper-initialized')) { + swiperThumbs.init(); + swiperMain.init(); + } + sliderModal.classList.add('open'); + $image = $(this).parents('.productPage__image'); + swiperMain.slideTo($image.index() + 1, 0); + }) +}); diff --git a/html/content/js/Products/productattr.js b/html/content/js/Products/productattr.js new file mode 100644 index 0000000..d28c0a7 --- /dev/null +++ b/html/content/js/Products/productattr.js @@ -0,0 +1,11 @@ +$$plugins.define('productattr', function (element, options) { + const selected = element.querySelector('.productAttr__selected'); + element.querySelectorAll('.radiobutton__input').forEach(input => { + if (input.checked) { + selected.innerHTML = input.parentElement.querySelector('.radiobutton__text').innerText; + } + input.addEventListener('change', function (event) { + selected.innerHTML = event.target.parentElement.querySelector('.radiobutton__text').innerText; + }); + }) +}); diff --git a/html/content/js/Sliders/productsSlider.js b/html/content/js/Sliders/productsSlider.js new file mode 100644 index 0000000..cc043b9 --- /dev/null +++ b/html/content/js/Sliders/productsSlider.js @@ -0,0 +1,45 @@ +// Modules included: +// 'virtual', +// 'mousewheel', +// 'navigation', +// 'pagination', +// 'zoom', +// 'lazy', +// 'autoplay', +// 'thumbs', +// 'manipulation', +// 'effect-fade', + +$$plugins.define('productsSlider', ['swiper-bundle'], function (element, options, Swiper) { + if (element.childNodes.length == 0) { + return; + } + + // init Swiper + const swiper = new Swiper(element, { + breakpoints: { + 0: { + slidesPerGroup: 1 + }, + 768: { + slidesPerGroup: 2 + }, + 1024: { + slidesPerGroup: 3 + } + }, + navigation: { + nextEl: element.querySelector('.swiper-button-next'), + prevEl: element.querySelector('.swiper-button-prev'), + }, + pagination: { + el: element.querySelector('.swiper-pagination'), + type: 'bullets', + clickable: 'true' + }, + slidesPerGroup: 3, + slidesPerView: 'auto', + spaceBetween: 16, + speed: 1000 + }); +}); diff --git a/html/content/js/Sliders/slider.js b/html/content/js/Sliders/slider.js new file mode 100644 index 0000000..a9f5824 --- /dev/null +++ b/html/content/js/Sliders/slider.js @@ -0,0 +1,42 @@ +// Modules included: +// 'virtual', +// 'mousewheel', +// 'navigation', +// 'pagination', +// 'zoom', +// 'lazy', +// 'autoplay', +// 'thumbs', +// 'manipulation', +// 'effect-fade', + +$$plugins.define('slider', ['swiper-bundle'], function (element, options, Swiper) { + if (element.childNodes.length == 0) { + return; + } + + // init Swiper + const swiper = new Swiper(element, { + autoHeight: options.autoHeight ? options.autoHeight : false, + breakpoints: options.breakpoints, + centerInsufficientSlides: options.centerInsufficientSlides ? options.centerInsufficientSlides : false, + centeredSlides: options.centeredSlides ? options.centeredSlides : false, + direction: options.direction ? options.direction : 'horizontal', + effect: options.effect ? options.effect : 'slide', + loop: options.loop ? options.loop : false, + navigation: { + nextEl: element.querySelector('.swiper-button-next'), + prevEl: element.querySelector('.swiper-button-prev'), + }, + pagination: { + el: element.querySelector('.swiper-pagination'), + type: 'bullets', + clickable: 'true' + }, + slidesPerGroup: options.slidesPerGroup ? options.slidesPerGroup : 1, + slidesPerView: options.slidesPerView ? options.slidesPerView : 1, + spaceBetween: options.spaceBetween ? options.spaceBetween : 0, + speed: options.speed ? options.speed : 800, + watchSlidesProgress: true + }); +}); diff --git a/html/content/js/Utils/Event.js b/html/content/js/Utils/Event.js new file mode 100644 index 0000000..7a5ada8 --- /dev/null +++ b/html/content/js/Utils/Event.js @@ -0,0 +1,13 @@ +define(function () { + return function () { + var handlers = []; + this.bind = function (handler) { + handlers.push(handler); + } + this.trigger = function (_this, arguments) { + for (var i = 0; i < handlers.length; i++) { + handlers[i].apply(_this, arguments); + } + } + } +}); \ No newline at end of file diff --git a/html/content/js/Utils/EventUtils.js b/html/content/js/Utils/EventUtils.js new file mode 100644 index 0000000..21a1ce9 --- /dev/null +++ b/html/content/js/Utils/EventUtils.js @@ -0,0 +1,75 @@ +(function(){ + var _ = {}; + _.now = Date.now || function(){ + return new Date().getTime(); + }; + // Returns a function, that, when invoked, will only be triggered at most once + // during a given window of time. Normally, the throttled function will run + // as much as it can, without ever going more than once per `wait` duration; + // but if you'd like to disable the execution on the leading edge, pass + // `{leading: false}`. To disable execution on the trailing edge, ditto. + _.throttle = function(func, wait, options) { + var context, args, result; + var timeout = null; + var previous = 0; + if (!options) options = {}; + var later = function() { + previous = options.leading === false ? 0 : _.now(); + timeout = null; + result = func.apply(context, args); + if (!timeout) context = args = null; + }; + return function() { + var now = _.now(); + if (!previous && options.leading === false) previous = now; + var remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0 || remaining > wait) { + if (timeout) { + clearTimeout(timeout); + timeout = null; + } + previous = now; + result = func.apply(context, args); + if (!timeout) context = args = null; + } else if (!timeout && options.trailing !== false) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }; + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // N milliseconds. If `immediate` is passed, trigger the function on the + // leading edge, instead of the trailing. + _.debounce = function(func, wait, immediate) { + var timeout, args, context, timestamp, result; + var later = function() { + var last = _.now() - timestamp; + if (last < wait && last >= 0) { + timeout = setTimeout(later, wait - last); + } else { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + if (!timeout) context = args = null; + } + } + }; + return function() { + context = this; + args = arguments; + timestamp = _.now(); + var callNow = immediate && !timeout; + if (!timeout) timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + context = args = null; + } + return result; + }; + }; + + window.EventUtils = _; +}()); \ No newline at end of file diff --git a/html/content/js/Utils/apiready.js b/html/content/js/Utils/apiready.js new file mode 100644 index 0000000..17f1ad4 --- /dev/null +++ b/html/content/js/Utils/apiready.js @@ -0,0 +1,15 @@ +define(function () { + return function ($element, name) { + return new Promise(function (resolve, reject) { + var api = $element.data(name); + if (api) { + resolve(api); + } + else { + $element.one(name + '.ready', function () { + resolve($element.data(name)); + }); + } + }); + }; +}); \ No newline at end of file diff --git a/html/content/js/Utils/confirm.js b/html/content/js/Utils/confirm.js new file mode 100644 index 0000000..ef59cbb --- /dev/null +++ b/html/content/js/Utils/confirm.js @@ -0,0 +1,27 @@ +define(function () { + return function (options, callback) { + var $dialog = $(options.template instanceof jQuery ? options.template.html() : options.template); + $dialog.hide(); + $('body').append($dialog); + $dialog.parseAttributePlugins(); + + function close() { + $dialog.remove(); + } + + function open() { + $dialog.show(); + } + + open(); + $dialog.find('.button.yes').click(function (e) { + e.preventDefault(); + close(); + callback(); + }); + $dialog.find('.button.no').click(function (e) { + e.preventDefault(); + close(); + }); + }; +}); \ No newline at end of file diff --git a/html/content/js/Utils/debounce.js b/html/content/js/Utils/debounce.js new file mode 100644 index 0000000..f81d9a5 --- /dev/null +++ b/html/content/js/Utils/debounce.js @@ -0,0 +1,16 @@ +define('debounce', function () { + return function (func, wait, immediate) { + var timeout; + return function () { + var context = this, args = arguments; + var later = function () { + timeout = null; + if (!immediate) func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) func.apply(context, args); + }; + }; +}); diff --git a/html/content/js/Utils/defineApi.js b/html/content/js/Utils/defineApi.js new file mode 100644 index 0000000..3215714 --- /dev/null +++ b/html/content/js/Utils/defineApi.js @@ -0,0 +1,9 @@ +define(function () { + return function ($element, name, api) { + if (typeof (api) === 'undefined' || !api) { + throw 'undefined api'; + } + $element.data(name, api); + $element.trigger(name + '.ready'); + }; +}); \ No newline at end of file diff --git a/html/content/js/Utils/destroyevent.js b/html/content/js/Utils/destroyevent.js new file mode 100644 index 0000000..e6fe48a --- /dev/null +++ b/html/content/js/Utils/destroyevent.js @@ -0,0 +1,11 @@ +define(function () { + (function ($) { + $.event.special.destroyed = { + remove: function (o) { + if ((o.handler && o.type !== 'destroyed')) { + o.handler() + } + } + } + })(jQuery) +}); \ No newline at end of file diff --git a/html/content/js/Utils/dom.js b/html/content/js/Utils/dom.js new file mode 100644 index 0000000..78f3562 --- /dev/null +++ b/html/content/js/Utils/dom.js @@ -0,0 +1,68 @@ +define(function () { + const reduce = (elements, fn, initialValue) => { + return Array.prototype.reduce.call(elements, fn, initialValue); + } + + const forEach = (elements, fn) => { + Array.prototype.forEach.call(elements, fn); + } + const map = (elements, fn) => { + return Array.prototype.map.call(elements, fn); + } + + const replace = (toReplace, replacement) => { + var parentElement = toReplace.parentElement + if (parentElement) { + parentElement.replaceChild(replacement, toReplace); + } + } + + const matches = (element, selector) => { + var m = element.matches + || Element.prototype.matchesSelector + || Element.prototype.mozMatchesSelector + || Element.prototype.msMatchesSelector + || Element.prototype.oMatchesSelector + || Element.prototype.webkitMatchesSelector; + return m.call(element, selector); + } + + const closest = (element, selector) => { + while (element) { + if (matches(element, selector)) { + return element; + } else { + element = element.parentElement; + } + } + return null; + } + const isFunction = (input) => (typeof input === "function"); + const dispatchEvent = (eventName, data) => { + let event; + if (window.CustomEvent && isFunction(window.CustomEvent)) { + event = new CustomEvent(eventName, { detail: data, cancelable: true, bubbles: false }); + } else { + event = document.createEvent('CustomEvent'); + event.initCustomEvent(eventName, false, true, data); + } + return window.document.dispatchEvent(event); + } + + const parseHtml = (htmlString) => { + const c = document.createElement('div'); + c.innerHTML = htmlString.trim(); + return c.firstChild; + } + + return { + dispatchEvent, + closest, + matches, + reduce, + map, + forEach, + replace, + parseHtml + }; +}); \ No newline at end of file diff --git a/html/content/js/Utils/domHelper.js b/html/content/js/Utils/domHelper.js new file mode 100644 index 0000000..f36ab1a --- /dev/null +++ b/html/content/js/Utils/domHelper.js @@ -0,0 +1,375 @@ +define(function () { + + function isElement(obj) { + //instanceof fails for elements that have been created by another document (iframe case) + //in that case we check other properties existing in all element nodes + return obj instanceof HTMLElement || ((typeof obj === "object") && + (obj.nodeType === 1) && (typeof obj.style === "object") && + (typeof obj.ownerDocument === "object")); + } + + let slideUp = (target, duration = 500, callback = null) => { + target.style.transitionProperty = 'height, margin, padding'; + target.style.transitionDuration = duration + 'ms'; + target.style.boxSizing = 'border-box'; + target.style.height = target.offsetHeight + 'px'; + target.offsetHeight; + target.style.overflow = 'hidden'; + target.style.height = 0; + target.style.paddingTop = 0; + target.style.paddingBottom = 0; + target.style.marginTop = 0; + target.style.marginBottom = 0; + window.setTimeout(() => { + target.style.display = 'none'; + target.style.removeProperty('height'); + target.style.removeProperty('padding-top'); + target.style.removeProperty('padding-bottom'); + target.style.removeProperty('margin-top'); + target.style.removeProperty('margin-bottom'); + target.style.removeProperty('overflow'); + target.style.removeProperty('transition-duration'); + target.style.removeProperty('transition-property'); + if (callback) { + callback(); + } + }, duration); + } + + let slideDown = (target, duration = 500, callback = null) => { + target.style.removeProperty('display'); + let display = window.getComputedStyle(target).display; + + if (display === 'none') + display = 'block'; + + target.style.display = display; + let height = target.offsetHeight; + target.style.overflow = 'hidden'; + target.style.height = 0; + target.style.paddingTop = 0; + target.style.paddingBottom = 0; + target.style.marginTop = 0; + target.style.marginBottom = 0; + target.offsetHeight; + target.style.boxSizing = 'border-box'; + target.style.transitionProperty = "height, margin, padding"; + target.style.transitionDuration = duration + 'ms'; + target.style.height = height + 'px'; + target.style.removeProperty('padding-top'); + target.style.removeProperty('padding-bottom'); + target.style.removeProperty('margin-top'); + target.style.removeProperty('margin-bottom'); + window.setTimeout(() => { + target.style.removeProperty('height'); + target.style.removeProperty('overflow'); + target.style.removeProperty('transition-duration'); + target.style.removeProperty('transition-property'); + if (callback) { + callback(); + } + }, duration); + } + + var slideToggle = (target, duration = 500, callback = null) => { + if (window.getComputedStyle(target).display === 'none') { + return slideDown(target, duration, callback); + } else { + return slideUp(target, duration, callback); + } + } + + function resolveSelector(selector, context) { + if (typeof (selector) !== "string") { + if (context) { + throw 'Context is allowed only when selector is a string.'; + } + if (Array.isArray(selector)) { + return selector; + } + if (isElement(selector)) { + return [selector]; + } + + throw 'Selector can either be a string or an array'; + } + if (!context) { + return document.querySelectorAll(selector); + } + if (typeof (context) == "string") { + return resolveSelector(selector).filter(element => element.matches(selector)); + } + else if (Array.isArray(context)) { + return context.filter(element => element.matches(selector)); + } + else if (isElement(context)) { + return context.querySelectorAll(selector); + } + else { + throw 'Unknown context type: ' + typeof (context) + ". Not a string, array or HTMLElement."; + } + } + + var factory = (target, context) => new domHelper(target, context); + function domHelper(target, context) { + this.elements = Array.from(resolveSelector(target, context)); + this.on = function (event, callback) { + for (let i = 0; i < this.elements.length; i++) { + this.elements[i].addEventListener(event, function (e) { + callback.apply(e.target, arguments); + }); + } + return this; + } + this.parent = function (selector) { + if (!selector) { + return factory( + this.elements.map(e => e.parentElement).filter(e => e) + ); + } + return factory( + this.elements.map(element => { + do { + if (element.parentElement.matches(selector)) { + return element.parentElement; + } + element = element.parentElement; + + } while (element.parentElement); + return null; + }).filter(e => e) + ); + } + this.parents = function (selector) { + var parents = this.elements.map(e => { + var r = []; + while (e.parentElement) { + r.push(e.parentElement); + e = e.parentElement; + } + return r; + }).reduce((acc, curVal) => { + return acc.concat(curVal) + }).filter(e => e); + + if (!selector) { + return factory(parents); + } + return factory(selector, parents); + } + this.find = function (selector) { + return factory(this.elements.map(e => { + return factory(selector, e).elements; + }).reduce((acc, curVal) => { + return acc.concat(curVal) + })); + } + this.show = function () { + this.each(e => e.style.display = "block"); + } + this.hide = function () { + this.each(e => e.style.display = "none"); + } + this.innerWidth = function () { + var element = this.elements[0]; + return parseInt(window.getComputedStyle(element).width); + } + this.toggle = function () { + this.each(e => { + if ((e.offsetParent === null)) // hidden + { + e.style.display = "block"; + } + else { + e.style.display = "none"; + } + }); + } + this.each = function (callback) { + this.elements.forEach(callback); + return this; + } + this.scroll = function () { + this.each(element => element.scroll.apply(element, arguments)); + return this; + } + this.addClass = function (cssClass) { + if (!cssClass) { + return; + } + var classes = cssClass.split(' '); + this.elements.forEach(element => { + classes.forEach(cssClass => { + element.classList.add(cssClass); + }); + }); + return this; + } + this.removeClass = function (cssClass) { + if (!cssClass) { + return; + } + var classes = cssClass.split(' '); + this.elements.forEach(element => { + classes.forEach(cssClass => { + element.classList.remove(cssClass); + }); + }); + return this; + } + this.toggleClass = function (cssClass) { + if (!cssClass) { + return; + } + var classes = cssClass.split(' '); + this.elements.forEach(element => { + classes.forEach(cssClass => { + element.classList.toggle(cssClass); + }); + }); + return this; + } + this.is = function (selector) { + return factory(selector, this.elements).elements.length > 0; + } + this.not = function (exclude) { + if (typeof (exclude) == "string") { + return factory(this.elements.filter(e => !e.matches(exclude))); + } + else if (Array.isArray(exclude)) { + return factory(this.elements.filter(e => exclude.indexOf(e) == -1)); + } + else if (isElement(exclude)) { + return factory(this.elements.filter(e => e != exclude)); + } + else if (exclude instanceof domHelper) { + return factory(this.elements.filter(e => exclude.elements.indexOf(e) == -1)); + } + else { + throw 'Unknown exclude type: ' + typeof (exclude) + ". Not a string, array or HTMLElement or domHelper."; + } + } + this.get = (index) => this.elements[index]; + this.length = this.elements.length; + this.siblings = function (selector) { + var next = this.next(selector); + var prev = this.prev(selector); + return factory(prev.elements.concat(next.elements)); + } + this.next = function (selector) { + var siblings = this.elements.map(e => { + var r = []; + while (e.nextElementSibling) { + r.push(e.nextElementSibling); + e = e.nextElementSibling; + } + return r; + }).reduce((acc, curVal) => { + return acc.concat(curVal) + }).filter(e => e); + + if (!selector) { + return factory(siblings); + } + return factory(selector, siblings); + } + this.prev = function (selector) { + var siblings = this.elements.map(e => { + var r = []; + while (e.previousElementSibling) { + r.push(e.previousElementSibling); + e = e.previousElementSibling; + } + return r; + }).reduce((acc, curVal) => { + return acc.concat(curVal) + }).filter(e => e); + + if (!selector) { + return factory(siblings); + } + return factory(selector, siblings); + } + this.index = function () { + var items = []; + this.prev().each(function (e) { + items.push(e); + }); + items.push(this.get(0)); + this.next().each(function (e) { + items.push(e); + }); + return items.indexOf(this.get(0)); + } + this.slideDown = function (duration, callback) { + return this.each(function (e) { + slideDown(e, duration, callback); + }); + } + this.slideUp = function (duration, callback) { + return this.each(function (e) { + slideUp(e, duration, callback); + }); + } + this.slideToggle = function (duration, callback) { + return this.each(function (e) { + slideToggle(e, duration, callback); + }); + } + this.removeAttr = function (attribute) { + this.each(e => e.removeAttribute(attribute)); + } + this.hasClass = function (cssClass) { + if (!cssClass) { + return; + } + return new RegExp('(\\s|^)' + cssClass + '(\\s|$)').test(this.get(0).className); + } + this.val = function (inputValue) { + if (typeof (inputValue) === 'undefined') { + return this.elements[0].value; + } + return this.each(function (e) { + e.value = inputValue; + }); + } + this.data = function (attribute, inputValue) { + if (typeof (inputValue) === 'undefined') { + return this.elements[0].dataset[attribute]; + } + return this.each(function (e) { + e.dataset[attribute] = inputValue; + }); + } + this.text = function (inputValue) { + if (typeof (inputValue) === 'undefined') { + return this.elements[0].innerText; + } + return this.each(function (e) { + e.innerText = inputValue; + }); + } + this.contains = function (element) { + return this.elements.indexOf(element) >= 0; + } + + this.trigger = function (eventName) { + + if ("createEvent" in document) { + var evt = document.createEvent("HTMLEvents"); + evt.initEvent(eventName, false, true); + return this.each(function (e) { + e.dispatchEvent(evt); + }); + } + else { + return this.each(function (e) { + e.fireEvent("on" + eventName); + }); + + } + } + } + + return factory; +}); \ No newline at end of file diff --git a/html/content/js/Utils/enabled.js b/html/content/js/Utils/enabled.js new file mode 100644 index 0000000..658b2dc --- /dev/null +++ b/html/content/js/Utils/enabled.js @@ -0,0 +1,6 @@ +define(function () { + $.fn.enabled = function (e) { + $(this).prop('disabled', !e); + $(this).trigger('enabled:change', e); + }; +}); \ No newline at end of file diff --git a/html/content/js/Utils/getApis.js b/html/content/js/Utils/getApis.js new file mode 100644 index 0000000..46788a6 --- /dev/null +++ b/html/content/js/Utils/getApis.js @@ -0,0 +1,17 @@ +define(['apiReady', 'whenAll'], function (apiReady, whenAll) { + return function ($elements, name) { + var result = []; + var d = $.Deferred(); + var p = $elements.map(function () { + var $element = $(this); + return apiReady($element, name).then(function (api) { + result.push({ api: api, $element: $element }); + }); + }); + whenAll(p).then(function () { + d.resolve(result); + }); + + return d; + }; +}); \ No newline at end of file diff --git a/html/content/js/Utils/throttle.js b/html/content/js/Utils/throttle.js new file mode 100644 index 0000000..ccfc9a7 --- /dev/null +++ b/html/content/js/Utils/throttle.js @@ -0,0 +1,15 @@ +define(function () { + return function (func, limit) { + var timeout; + var inThrottle; + return function () { + var context = this, args = arguments; + if (!inThrottle) { + func.apply(context, args); + inThrottle = true; + } + setTimeout(() => inThrottle = false, limit); + + }; + }; +}); diff --git a/html/content/js/Utils/whenAll.js b/html/content/js/Utils/whenAll.js new file mode 100644 index 0000000..8d1c3e2 --- /dev/null +++ b/html/content/js/Utils/whenAll.js @@ -0,0 +1,13 @@ +define(function () { + return function (deferreds) { + return $.Deferred(function (def) { + $.when.apply(jQuery, deferreds).then( + function () { + def.resolveWith(this, [Array.prototype.slice.call(arguments)]); + }, + function () { + def.rejectWith(this, [Array.prototype.slice.call(arguments)]); + }); + }); + }; +}); diff --git a/html/content/js/Wishlist/wishlistbutton.js b/html/content/js/Wishlist/wishlistbutton.js new file mode 100644 index 0000000..bde43c1 --- /dev/null +++ b/html/content/js/Wishlist/wishlistbutton.js @@ -0,0 +1,25 @@ +$$plugins.define('wishlistbutton', ['domHelper'], function (element, options, $) { + const $button = $(element).find('.listsClose'); + + $button.on('click', function () { + if (window.innerWidth > 768) { + $button.siblings('.listsBlock').slideToggle(300, function () { + $(element).toggleClass('open'); + $button.siblings('.listsBlock').removeAttr('style'); + }); + } else { + $(element).toggleClass('open'); + } + }); + + document.addEventListener('click', function (event) { + if (!$(element).hasClass('open') || element == event.target || $(event.target).parents().contains(element)) { + return; + } + $button.siblings('.listsBlock').slideUp(300, function () { + $(element).removeClass('open'); + $button.siblings('.listsBlock').removeAttr('style'); + }); + }); + +}); \ No newline at end of file -- cgit v1.2.3