{"id":66694,"date":"2025-08-26T16:32:28","date_gmt":"2025-08-26T14:32:28","guid":{"rendered":"https:\/\/www.diamantrad.com\/?page_id=66694"},"modified":"2026-05-01T21:28:20","modified_gmt":"2026-05-01T19:28:20","slug":"bikes-without-motor","status":"publish","type":"page","link":"https:\/\/www.diamantrad.com\/fi-FI\/bikes\/bikes-without-motor\/","title":{"rendered":"Bikes without motor"},"content":{"rendered":"\n<div class=\"wp-block-cover\"><img data-dominant-color=\"797190\" data-has-transparency=\"false\" loading=\"lazy\" decoding=\"async\" width=\"2250\" height=\"1500\" sizes=\"auto, (max-width: 2250px) 100vw, 2250px\" class=\"wp-block-cover__image-background wp-image-56764 size-full not-transparent\" alt=\"Di 25 mahon lifestyle 67i3404 300dpihero\" src=\"https:\/\/www.diamantrad.com\/wp-content\/uploads\/2025\/03\/DI_25_Mahon_Lifestyle_67I3404_300dpihero-png.avif\" style=\"--dominant-color: #797190; object-position:71% 60%\" data-object-fit=\"cover\" data-object-position=\"71% 60%\" srcset=\"https:\/\/www.diamantrad.com\/wp-content\/uploads\/2025\/03\/DI_25_Mahon_Lifestyle_67I3404_300dpihero-png.avif 2250w, https:\/\/www.diamantrad.com\/wp-content\/uploads\/2025\/03\/DI_25_Mahon_Lifestyle_67I3404_300dpihero-300x200.avif 300w, https:\/\/www.diamantrad.com\/wp-content\/uploads\/2025\/03\/DI_25_Mahon_Lifestyle_67I3404_300dpihero-1024x683.avif 1024w, https:\/\/www.diamantrad.com\/wp-content\/uploads\/2025\/03\/DI_25_Mahon_Lifestyle_67I3404_300dpihero-768x512.avif 768w, https:\/\/www.diamantrad.com\/wp-content\/uploads\/2025\/03\/DI_25_Mahon_Lifestyle_67I3404_300dpihero-1536x1024.avif 1536w, https:\/\/www.diamantrad.com\/wp-content\/uploads\/2025\/03\/DI_25_Mahon_Lifestyle_67I3404_300dpihero-2048x1365.avif 2048w\" \/><span aria-hidden=\"true\" class=\"wp-block-cover__background has-color-primary-hover-background-color has-background-dim-30 has-background-dim\"><\/span><div class=\"wp-block-cover__inner-container is-layout-constrained wp-block-cover-is-layout-constrained\">\n<p class=\"has-text-align-center has-large-font-size wp-block-paragraph\"><\/p>\n\n\n<div class=\"lazyblock-cover-text-mp6W1 wp-block-lazyblock-cover-text\"><div class=\"covertext container py-5 d-flex flex-column colorscheme-light justify-content-center\" id=\"covertext-mp6W1\">\r\n  <div class=\"lazyblock-inner-blocks\">\n\n<h2 class=\"wp-block-heading\">Moottorittomat polkupy\u00f6r\u00e4t<\/h2>\n\n\n\n<h4 class=\"wp-block-heading\">Puhdasta py\u00f6r\u00e4ilyn vapautta<\/h4>\n\n<\/div>\r\n<\/div><\/div><\/div><\/div>\n\n\n<div class=\"lazyblock-nncontainer-Z2vC7Ps wp-block-lazyblock-nncontainer\"><div class=\"section-standard\">\r\n  <div class=\"container\" id=\"container-Z2vC7Ps\">\r\n    <div class=\"row\">\r\n      <div class=\"col py-4 py-lg-5\">\r\n        <div class=\"lazyblock-inner-blocks\">\n\n<p class=\"wp-block-paragraph\">Moottoriton polkupy\u00f6r\u00e4 edustaa edelleen t\u00e4ydellist\u00e4 tasapainoa yksinkertaisuuden, toimivuuden ja keveyden v\u00e4lill\u00e4. S\u00e4hk\u00f6t\u00f6n polkupy\u00f6r\u00e4 taittaa ty\u00f6matkat terveytt\u00e4 tukien, tekee vapaa-ajastasi aktiivisempaa ja vie sinut suuriin seikkailuihin \u2013 ilman akkuja. Arvostamme edelleen suuresti moottorittoman ajon puhtautta. T\u00e4st\u00e4 kaikki liike alkaa. Keskitymme huolellisesti suunniteltuihin yksityiskohtiin, kest\u00e4v\u00e4\u00e4n tekniikkaan ja ajattomaan muotoiluun, jotta voimme tarjota sinulle vertaansa vailla olevan ajokokemuksen. Hypp\u00e4\u00e4 kyytiin \u2013 paras aika ajelulle on heti!<\/p>\n\n<\/div>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n<\/div><\/div>\n\n<div class=\"lazyblock-nncontainer-ZVJqeu wp-block-lazyblock-nncontainer\"><div class=\"section-light\">\r\n  <div class=\"container\" id=\"container-ZVJqeu\">\r\n    <div class=\"row\">\r\n      <div class=\"col py-4 py-lg-5\">\r\n        <div class=\"lazyblock-inner-blocks\">\n<div class=\"lazyblock-bikefinder-1wAG4F wp-block-lazyblock-bikefinder\"><template id=\"bikeTeaserComponent\">\n  <template x-if=\"frames.value.length\">\n    <div class=\"bikefinder-tile px-3 py-4 px-lg-4\" :class=\"classNames\">\n      <div class=\"bikefinder-tile-content\">\n        <a :href=\"selectedFrame.value.link\" class=\"bikefinder-title\">\n          <h4\n            class=\"mb-0\"\n            x-text=\"extractOrCleanString(selectedFrame.value.title)\"\n          ><\/h4>\n        <\/a>\n\n        <p\n          class=\"bikefinder-subtitle\"\n          x-text=\"$store.utils.t($store.utils.translations.frames[extractOrCleanString(selectedFrame.value.title, true)])\"\n        ><\/p>\n\n        <div\n          class=\"bikefinder-preis\"\n          x-text=\"getAbText() + ' ' + formatNumber(selectedFrame.value.price_lowest) + ' ' + getCurrency()\"\n        ><\/div>\n        <a :href=\"selectedFrame.value.link\" class=\"color-images mx-auto mb-4\"\n          ><picture>\n            <img\n              :src=\"selectedFrameImage.value\"\n              class=\"img-fluid lazy\"\n              :alt=\"selectedFrame.value.title\"\n              loading=\"lazy\"\n              decoding=\"async\"\n          \/><\/picture>\n        <\/a>\n\n        <div class=\"bikefinder-controls mb-3\">\n          <div class=\"color-switch\">\n            <template x-for=\"(color, index) in selectedFrame.value.colors\">\n              <span\n                @click=\"onColorClicked(color)\"\n                :class=\"'bike-color-' + color.value.toLowerCase()\"\n                data-bs-toggle=\"tooltip\"\n                data-bs-placement=\"top\"\n                :title=\"color.description\"\n              ><\/span>\n            <\/template>\n          <\/div>\n\n          <div class=\"bikefinder-rahmen\">\n            <ul\n              class=\"nav nav-tabs\"\n              x-data=\"{ frames: framesAsGenerations ? selectedFrameByGeneration: frames }\"\n            >\n              <template x-for=\"(frame, index) in frames.value\">\n                <li\n                  class=\"nav-item\"\n                  @click=\"onFrameClicked(frame)\"\n                  x-show=\"!framesAsGenerations || isFrameInFiltered(frame)\"\n                >\n                  <span\n                    class=\"nav-link\"\n                    :class=\"selectedFrame.value.id == frame.id ? 'active' : ''\"\n                    x-data=\"{ meta: getFrameMeta(frame.frameType) }\"\n                  >\n                    <img\n                      width=\"48\"\n                      height=\"28\"\n                      :src=\"meta.icon\"\n                      data-bs-toggle=\"tooltip\"\n                      data-bs-placement=\"top\"\n                      :title=\"$store.utils.t(meta.text)\"\n                    \/>\n                  <\/span>\n                <\/li>\n              <\/template>\n            <\/ul>\n          <\/div>\n        <\/div>\n      <\/div>\n      <template x-if=\"framesAsGenerations\">\n        <div\n          x-data=\"generation({ generations: frames, generationClickedCallback: generationsCallback })\"\n        ><\/div>\n      <\/template>\n    <\/div>\n  <\/template>\n<\/template>\n\n<script>\n  document.addEventListener(\"alpine:init\", () => {\n    Alpine.data(\n      \"biketeaser\",\n      ({ frames, framesAsGenerations, framesMeta }) => ({\n        template: null,\n        frames: { value: frames },\n        selectedFrameByGeneration: { value: [] },\n        selectedFrame: { value: null },\n        framesMeta: framesMeta,\n        selectedFrameImage: {},\n        framesAsGenerations: framesAsGenerations,\n        generationsCallback: null,\n        classNames: \"\",\n        currentVariant: null,\n        init() {\n          this.setup();\n          this.$nextTick(() => {\n            this.mounted();\n            this.render();\n          });\n        },\n        setup() {\n          this.template = document.getElementById(\"bikeTeaserComponent\");\n        },\n        mounted() {\n          if (this.frames.value.length) {\n            if (this.framesAsGenerations) {\n              this.selectedFrameByGeneration.value = this.frames.value[0];\n              this.selectedFrame.value = this.frames.value[0];\n            } else {\n              this.selectedFrame.value = this.frames.value;\n            }\n\n            const frame = this.getFrameWithSpecialRecommendedRetailpriceOrFirst(\n              this.selectedFrame.value,\n            );\n\n            let variant = this.getVariantsWithKey(\n              frame,\n              \"special_recommended_retailprice\",\n            )[0];\n            if (!variant) {\n              variant = frame.variants[0];\n            }\n\n            const uniqueFrameImageOfVariant = this.getImageByVariant(\n              frame,\n              variant,\n            );\n\n            this.selectedFrame.value = frame;\n            this.selectedFrameImage = uniqueFrameImageOfVariant;\n\n            this.setClassNames(variant.id);\n          }\n          this.generationsCallback = this.onGenerationClicked.bind(this);\n        },\n        render() {\n          this.$el.innerHTML = this.template.innerHTML;\n        },\n        getFrameWithSpecialRecommendedRetailpriceOrFirst(frames) {\n          const frame = frames.find((frame) => {\n            return frame.hasOwnProperty(\"special_recommended_retailprice\");\n          });\n          return frame ? frame : frames[0];\n        },\n        getVariantsWithKey(frame, key, value = \"\") {\n          if (\n            frame.commonValues.hasOwnProperty(key) &&\n            (value ? frame.commonValues[key] === value : true)\n          ) {\n            return frame.variants;\n          }\n\n          return frame.variants.filter((variant) => {\n            return variant[key] && (value ? variant[key] === value : true);\n          });\n        },\n        getRecomendedRetailpriceVariantColor(color) {\n          const variantsWithPrice = this.getVariantsWithKey(\n            this.selectedFrame.value,\n            \"special_recommended_retailprice\",\n          );\n\n          const variantsWithColor = this.getVariantsWithKey(\n            this.selectedFrame.value,\n            \"primary_basecolour\",\n            color.value,\n          );\n\n          const variantsWithPriceIds = variantsWithPrice.map(\n            (variant) => variant.id,\n          );\n          let variant = variantsWithPrice.find((variant) =>\n            variantsWithPriceIds.includes(variant.id),\n          );\n          if (!variant) {\n            variant = variantsWithColor.length ? variantsWithColor[0] : this.selectedFrame.value.variants[0];\n          }\n\n          return {\n            value: variant ? variant.value : \"\",\n            variantId: variant ? variant.id : null,\n          };\n        },\n        getImageByVariant(frame, variant) {\n          if (!variant) {\n            return { value: frame?.commonValues?.pos_image, variantId: null };\n          }\n          const uniqueImage = frame.images?.find((image) => {\n            return image.value == variant[image.propertyPath];\n          });\n\n          return uniqueImage\n            ? { value: uniqueImage.value, variantId: variant.id }\n            : { value: frame.commonValues?.pos_image, variantId: variant.id };\n        },\n        isFrameInFiltered(frame) {\n          return frame.hasVariants.length ? true : false;\n        },\n        extractTextInBrackets(text) {\n          const match = text.match(\/\\((.*?)\\)\/);\n          return match ? match[1] : \"\";\n        },\n        onFrameClicked(frame) {\n          this.selectedFrame.value = frame;\n          const variantsWithPrice = this.getVariantsWithKey(\n            frame,\n            \"special_recommended_retailprice\",\n          );\n\n          const variantsWithPosImage = this.getVariantsWithKey(\n            frame,\n            \"pos_image\",\n          );\n\n          const variantsWithPriceIds = variantsWithPrice.map(\n            (variant) => variant.id,\n          );\n\n          let variant = variantsWithPrice.find((variant) =>\n            variantsWithPriceIds.includes(variant.id),\n          );\n          if (!variant) {\n            variant = frame.variants[0];\n          }\n\n          const uniqueFrameImageOfVariant = this.getImageByVariant(\n            frame,\n            variant,\n          );\n\n          this.selectedFrameImage = uniqueFrameImageOfVariant;\n          this.setClassNames(variant.id);\n        },\n        onGenerationClicked(generation) {\n          this.selectedFrameByGeneration.value = generation;\n          this.selectedFrame.value = this.selectedFrameByGeneration.value[0];\n          this.selectedFrameImage = this.selectedFrame.value.images[0];\n          this.setClassNames(this.selectedFrame.value.images[0].variantId);\n        },\n        onColorClicked(color) {\n          this.selectedFrameImage = this.selectedFrame.value.images.find(\n            (image) => image.variantId == color.variantId,\n          );\n\n          const colorRetailprice =\n            this.getRecomendedRetailpriceVariantColor(color);\n          this.setClassNames(colorRetailprice.variantId);\n        },\n        setClassNames(variantId) {\n          \/\/ Maybe many?\n          this.currentVariant = this.selectedFrame.value.variants.find(\n            (variant) => {\n              return variant.id === variantId;\n            },\n          );\n\n          let names = [];\n          if (this.isReducedPrice(this.currentVariant) && this.isLastChance()) {\n            names.push(\"sale\");\n            names.push(\"lastchance\");\n          } else if (this.isReducedPrice(this.currentVariant)) {\n            names.push(\"sale\");\n          } else if (this.isLastChance()) {\n            names.push(\"lastchance\");\n          }\n\n          if (this.isNewBike()) {\n            names.push(\"newbike\");\n          }\n          this.classNames = names.join(\" \");\n        },\n        getPriceText(price) {\n          return \"Ab \" + price + \" EUR\";\n        },\n        formatNumber(number) {\n          if (this.$store.utils.country === \"CH\") {\n            return number.toString().replace(\/\\B(?=(\\d{3})+(?!\\d))\/g, \"'\");\n          } else {\n            return number.toString().replace(\/\\B(?=(\\d{3})+(?!\\d))\/g, \".\");\n          }\n        },\n        getAbText() {\n          const lang = this.$store.utils.lang;\n          return (\n            {\n              da: \"Fra\",\n              de: \"Ab\",\n              en: \"From\",\n              fi: \"Alkaen\",\n              fr: \"\u00c0 partir de\",\n              nl: \"Vanaf\",\n              pl: \"Od\",\n              sv: \"Fr\u00e5n\",\n            }[lang] || \"Ab\"\n          );\n        },\n        getCurrency() {\n          const currencyMap = {\n            CH: \"CHF\",\n            DK: \"DKK\",\n            GB: \"GBP\",\n            PL: \"PLN\",\n            SE: \"SEK\",\n          };\n          const country = this.$store.utils.country;\n          return currencyMap[country] || \"EUR\";\n        },\n        isReducedPrice(variant) {\n          if (variant && variant.special_recommended_retailprice) {\n            return variant.special_recommended_retailprice;\n          }\n          return this.selectedFrame.value.specialRecommendedRetailprice.find(\n            (price) => price.variantId === this.selectedFrame.value.id,\n          );\n        },\n        isLastChance() {\n          return this.selectedFrame.value.lastchance;\n        },\n        isNewBike() {\n          return this.selectedFrame.value.newbike;\n        },\n        removeAllBrackets(str) {\n          const regex = \/\\[.*?\\]|\\(.*?\\)|\\{.*?\\}\/g;\n          return str.replace(regex, \"\");\n        },\n        extractBrackets(str) {\n          const regex = \/\\[.*?\\]|\\(.*?\\)|\\{.*?\\}\/g;\n\n          const matches = str.match(regex);\n\n          return matches || [];\n        },\n        getFrameMeta(frameType) {\n          return this.framesMeta[frameType] || { icon: '', text: '' };\n        },\n        extractOrCleanString(str, returnExtracted = false) {\n          if (!str) return \"\";\n          const pattern =\n            \/\\(?\\b(HCH|MIT|Lowstep|SCH|TIE|Midstep|Stepover|HER|TRA)\\b\\)?\/i;\n          const match = str.match(pattern);\n\n          if (match) {\n            return returnExtracted\n              ? match[1]\n              : str.replace(pattern, \"\").replace(\/\\s+\/g, \" \").trim();\n          }\n\n          return returnExtracted ? null : str.trim();\n        },\n      }),\n    );\n  });\n<\/script>\n<template id=\"generationComponent\">\n  <template x-if=\"getGenerationWithoutActive().length\">\n    <div class=\"dropdown bikefinder-generations\">\n      <button\n        class=\"btn btn-secondary dropdown-toggle p-0\"\n        type=\"button\"\n        :id=\"'dropdownGenerations-' + selectedGeneration.id\"\n        data-bs-toggle=\"dropdown\"\n        aria-expanded=\"false\"\n        x-text=\"$store.utils.t($store.utils.translations.futureGenerations)\"\n      ><\/button>\n      <div\n        class=\"dropdown-menu dropdown-generations\"\n        :aria-labelledby=\"'dropdownGenerations-' + selectedGeneration.id\"\n      >\n        <template\n          x-for=\"(generation, index) in getGenerationWithoutActive(generations)\"\n          :key=\"'genNav-' + index\"\n        >\n          <a\n            @click=\"onGenerationClicked(generation)\"\n            class=\"dropdown-item\"\n            x-text=\"mainTitle(generation)\"\n          ><\/a>\n        <\/template>\n      <\/div>\n    <\/div>\n  <\/template>\n<\/template>\n\n<script>\n  document.addEventListener(\"alpine:init\", () => {\n    Alpine.data(\"generation\", ({ generations, generationClickedCallback }) => ({\n      template: null,\n      generations: generations,\n      selectedGeneration: {},\n      selectedFrameImage: {},\n      generationClickedCallback: generationClickedCallback,\n      init() {\n        this.setup();\n        this.$nextTick(() => {\n          this.mounted();\n          this.render();\n        });\n      },\n      setup() {\n        this.template = document.getElementById(\"generationComponent\");\n      },\n      mounted() {\n        if (this.generations.value.length) {\n          this.selectedGeneration = this.generations.value[0];\n        }\n      },\n      render() {\n        this.$el.innerHTML = this.template.innerHTML;\n      },\n      onGenerationClicked(generation) {\n        this.generationClickedCallback(generation);\n        this.selectedGeneration = generation;\n      },\n      active(generation, selectedGeneration) {\n        return generation === selectedGeneration;\n      },\n      getGenerationWithoutActive() {\n        return this.generations.value.filter(\n          (generation) => generation != this.selectedGeneration\n        );\n      },\n      getGenerationFrame(generation) {\n        return generation[0];\n      },\n      extractOrCleanString(str, returnExtracted = false) {\n        const pattern =\n          \/\\(?\\b(HCH|MIT|Lowstep|SCH|TIE|Midstep|Stepover|HER|TRA)\\b\\)?\/i;\n        const match = str.match(pattern);\n\n        if (match) {\n          return returnExtracted\n            ? match[1]\n            : str.replace(pattern, \"\").replace(\/\\s+\/g, \" \").trim();\n        }\n\n        return returnExtracted ? null : str.trim();\n      },\n      mainTitle(generation) {\n        let frame = this.getGenerationFrame(generation);\n        let title = this.extractOrCleanString(frame.title);\n        return (\n          title +\n          \" \" +\n          this.addModelYear(frame) +\n          \" - \" +\n          this.formatNumber(frame.price_lowest) +\n          \" \" +\n          this.getCurrency()\n        );\n      },\n      addModelYear(frame) {\n        if (frame.modelYear) {\n          return \" (\" + frame.modelYear + \")\";\n        }\n        return \"\";\n      },\n      getCurrency() {\n        const currencyMap = {\n          CH: \"CHF\",\n          DK: \"DKK\",\n          GB: \"GBP\",\n          PL: \"PLN\",\n          SE: \"SEK\",\n        };\n        const country = this.$store.utils.country;\n        return currencyMap[country] || \"EUR\";\n      },\n      formatNumber(number) {\n        if (this.$store.utils.country === \"CH\") {\n          return number.toString().replace(\/\\B(?=(\\d{3})+(?!\\d))\/g, \"'\");\n        } else {\n          return number.toString().replace(\/\\B(?=(\\d{3})+(?!\\d))\/g, \".\");\n        }\n      },\n    }));\n  });\n<\/script>\n<template id=\"filterComponent\">\n  <template x-if=\"showReset\">\n    <div id=\"filter\" class=\"bikefinder-filter mb-5 text-start\">\n      <h5 class=\"text-uppercase h-underline mt-4 position-relative\">\n        Filter\n        <div x-show=\"isFilterActive()\" class=\"small reset-filters\">\n          <span\n            @click=\"resetFilter()\"\n            class=\"btn-reset-filters\"\n            x-text=\"$store.utils.t($store.utils.translations.resetFilter)\"\n          >\n          <\/span>\n        <\/div>\n      <\/h5>\n      <div class=\"bike-counter mb-4\">\n        <span id=\"bikeCount\" x-text=\"countOfFilteredBikes.value\"><\/span> Bikes\n      <\/div>\n\n      <!-- Biketyp -->\n      <h5\n        class=\"mb-1\"\n        x-text=\"$store.utils.t($store.utils.translations.bikeType)\"\n      ><\/h5>\n      <div\n        class=\"small text-grey mb-2\"\n        x-text=\"$store.utils.t($store.utils.translations.bikeTypeSpeed)\"\n      ><\/div>\n      <div\n        x-key=\"'typeLineSelection'\"\n        x-data=\"lineselection(\n        { \n            items: typeFilterProps.sort((a, b) => a.context.order < b.context.order ? -1 : 1),\n            onSelected: onTypeSelected,\n            selectedFilter: selectedFilter,\n        }\n      )\"\n      ><\/div>\n\n      <template x-if=\"!hideBattery\">\n        <div class=\"bikefinder-filter-akku\">\n          <!-- Akku -->\n          <h5\n            class=\"mb-1\"\n            x-text=\"$store.utils.t($store.utils.translations.battery)\"\n          ><\/h5>\n          <div\n            class=\"small text-grey mb-2\"\n            x-text=\"$store.utils.t($store.utils.translations.unitBattery)\"\n          ><\/div>\n          <div\n            x-show=\"showReset\"\n            x-data=\"sliderselection(\n            { \n                items: batteryFilterProps,\n                onSelected: onBatterySelected,\n                id: 'battery-slider',\n                domLoaded: domLoaded,\n                options: { step: 100, values: true }\n            }\n          )\"\n          ><\/div>\n        <\/div>\n      <\/template>\n\n      <!-- Rahmenformen -->\n      <h5 x-text=\"$store.utils.t($store.utils.translations.frameShape)\"><\/h5>\n\n      <div\n        x-key=\"'frameLineSelection'\"\n        x-data=\"lineselection(\n        { \n            items: frameFilterProps.sort((a, b) => a.context.order < b.context.order ? -1 : 1),\n            onSelected: onFrameSelected,\n            selectedFilter: selectedFilter,\n        }\n      )\"\n      ><\/div>\n\n      <!-- Schaltung -->\n      <h5 x-text=\"$store.utils.t($store.utils.translations.gear)\"><\/h5>\n      <div\n        x-data=\"choiceselection(\n        { \n            items: gearFilterProps,\n            onSelected: onGearSelected \n        }\n      )\"\n      ><\/div>\n\n      <!-- Preis -->\n      <h5\n        class=\"mb-1\"\n        x-text=\"$store.utils.t($store.utils.translations.price)\"\n      ><\/h5>\n      <div\n        class=\"small text-grey mb-2\"\n        x-text=\"$store.utils.t($store.utils.translations.inCurrency)\"\n      ><\/div>\n      <div\n        x-data=\"sliderselection(\n        { \n            items: priceFilterProps,\n            onSelected: onPriceSelected,\n            id: 'price-slider',\n            domLoaded: domLoaded,\n            options: { step: 100 }\n        }\n      )\"\n      ><\/div>\n\n      <!-- Sale -->\n      <h5 x-text=\"$store.utils.t($store.utils.translations.sale)\"><\/h5>\n      <div\n        x-data=\"multiplechoiceselection(\n        { \n            items: saleFilterProps.concat(lastchanceFilterProps),\n            onSelected: onSaleOrLastchanceSelected \n        }\n      )\"\n      ><\/div>\n\n      <!-- Farben -->\n      <h5 x-text=\"$store.utils.t($store.utils.translations.color)\"><\/h5>\n      <div\n        x-data=\"colorselection(\n        { \n            items: colorFilterProps,\n            onSelected: onColorSelected \n        }\n      )\"\n      ><\/div>\n\n      <!-- Gewicht -->\n      <h5\n        class=\"mb-1\"\n        x-text=\"$store.utils.t($store.utils.translations.weight)\"\n      ><\/h5>\n      <div\n        class=\"small text-grey mb-2\"\n        x-text=\"$store.utils.t($store.utils.translations.unitWeight)\"\n      ><\/div>\n      <div\n        class=\"rslider\"\n        x-data=\"sliderselection(\n        { \n            items: weightFilterProps,\n            onSelected: onWeightSelected,\n            id: 'weight-slider',\n            domLoaded: domLoaded\n        }\n      )\"\n      ><\/div>\n    <\/div>\n  <\/template>\n<\/template>\n\n<script>\n  document.addEventListener(\"alpine:init\", () => {\n    Alpine.data(\n      \"filter\",\n      ({\n        frameFilterProps,\n        typeFilterProps,\n        batteryFilterProps,\n        gearFilterProps,\n        priceFilterProps,\n        weightFilterProps,\n        saleFilterProps,\n        lastchanceFilterProps,\n        newbikeFilterProps,\n        countOfFilteredBikes,\n        maxBikesCount,\n        colorFilterProps,\n        selectedFilter,\n        controller,\n      }) => ({\n        template: null,\n        frameFilterProps: frameFilterProps,\n        typeFilterProps: typeFilterProps,\n        batteryFilterProps: batteryFilterProps,\n        gearFilterProps: gearFilterProps,\n        priceFilterProps: priceFilterProps,\n        weightFilterProps: weightFilterProps,\n        saleFilterProps: saleFilterProps,\n        lastchanceFilterProps: lastchanceFilterProps,\n        newbikeFilterProps: newbikeFilterProps,\n        countOfFilteredBikes: countOfFilteredBikes,\n        maxBikesCount: maxBikesCount,\n        colorFilterProps: colorFilterProps,\n        selectedFrame: {},\n        selectedFrameImage: {},\n        controller: controller,\n        showReset: true,\n        domLoaded: false,\n        domUpdate: { value: 0 },\n        selectedFilter: selectedFilter,\n        hideBattery: false,\n        init() {\n          this.setup();\n          this.$nextTick(() => {\n            this.mounted();\n            this.render();\n          });\n        },\n        setup() {\n          this.template = document.getElementById(\"filterComponent\");\n        },\n        mounted() {},\n        render() {\n          this.$el.innerHTML = this.template.innerHTML;\n\n          document.addEventListener(\"DOMContentLoaded\", () => {\n            this.domLoaded = true;\n          });\n\n          this.$watch(\"selectedFilter\", (filter) => {\n            this.hideFilterSections(filter);\n          });\n        },\n        hideFilterSections(filter) {\n          \/\/ Hide Battery\n          const hideBattery = [\"Bike\", \"S-Pedelec\"].includes(\n            filter.properties?.category?.value\n          );\n\n          if (hideBattery) {\n            this.hideBattery = true;\n          } else {\n            this.hideBattery = false;\n          }\n        },\n        onFrameSelected(property, initial = true) {\n          this.controller.filterAddPropertyAction(property, initial);\n        },\n        onTypeSelected(property, initial = true) {\n          this.controller.filterAddPropertyAction(property, initial);\n        },\n        onBatterySelected(property, initial = true) {\n          this.controller.filterAddPropertyAction(property, initial);\n        },\n        onGearSelected(property, initial = true) {\n          this.controller.filterAddPropertyAction(property, initial);\n        },\n        onPriceSelected(property, initial = true) {\n          this.controller.filterAddPropertyAction(property, initial);\n        },\n        onWeightSelected(property, initial = true) {\n          this.controller.filterAddPropertyAction(property, initial);\n        },\n        onSaleOrLastchanceSelected(property, checked, initial = true) {\n          if (checked) {\n            this.controller.filterAddPropertyAction(property, initial);\n          } else {\n            this.controller.filterRemovePropertyAction(property);\n          }\n        },\n        onColorSelected(property, initial = true) {\n          this.controller.filterAddPropertyAction(property, true);\n        },\n        resetFilter() {\n          this.showReset = false;\n          this.controller.resetFilterAction();\n\n          this.$nextTick(() => {\n            this.showReset = true;\n          });\n        },\n        isFilterActive() {\n          return !this.selectedFilter.initial;\n        },\n      })\n    );\n  });\n<\/script>\n<template id=\"lineSelectionComponent\">\n  <div class=\"bikefilter-biketyp mb-4\">\n    <ul class=\"nav nav-tabs\">\n      <template x-for=\"(item, index) in items\">\n        <li class=\"nav-item\">\n          <button\n            @click=\"selected(item, index)\"\n            class=\"nav-link\"\n            :class=\"activeItem &#038;&#038; activeItem.value === item.value ? 'active' : ''\"\n            style=\"outline: none\"\n            data-bs-toggle=\"tooltip\"\n            data-placement=\"top\"\n            :title=\"$store.utils.t(item.context.text)\"\n          >\n            <template x-if=\"item.context &#038;&#038; item.context.type == 'icon'\">\n              <img :src=\"item.context.icon\" style=\"width: 48px; height: 28px\" \/>\n            <\/template>\n            <template x-if=\"item.context &#038;&#038; item.context.type == 'text'\">\n              <span\n                style=\"width: 48px; height: 28px\"\n                x-text=\"$store.utils.t(item.context.text)\"\n              ><\/span>\n            <\/template>\n          <\/button>\n        <\/li>\n      <\/template>\n    <\/ul>\n  <\/div>\n<\/template>\n\n<script>\n  document.addEventListener(\"alpine:init\", () => {\n    Alpine.data(\"lineselection\", ({ items, onSelected, selectedFilter }) => ({\n      template: null,\n      items: items,\n      onSelected: onSelected,\n      selectedFilter: selectedFilter,\n      selectedIndex: -1,\n      activeItem: null,\n      init() {\n        this.setup();\n        this.$nextTick(() => {\n          this.mounted();\n          this.render();\n        });\n      },\n      setup() {\n        this.template = document.getElementById(\"lineSelectionComponent\");\n      },\n      mounted() {\n        this.$watch(\"selectedFilter\", (filter) => {\n          this.activeItem = this.findActiveItem(filter);\n        });\n      },\n      render() {\n        this.$el.innerHTML = this.template.innerHTML;\n      },\n      selected(item, index) {\n        this.selectedIndex = index;\n        this.onSelected(item, false);\n      },\n      findActiveItem(filter) {\n        if (this.items[0].propertyPath in filter.properties) {\n          let found = this.items.find((item) => {\n            return (\n              item.value === filter.properties[this.items[0].propertyPath].value\n            );\n          });\n          return found;\n        }\n      },\n    }));\n  });\n<\/script>\n<template id=\"sliderSelectionComponent\">\n\t<div :style=\"!sliderReady ? 'opacity: 0' : ''\" class=\"bikefilter-rahmenform mb-4 mt-3\">\n\t\t<div :style=\"!domLoaded &#038;&#038; !slider ? 'opacity: 0' : ''\">\n\t\t\t<input :id=\"id\" type=\"range\" \/>\n\t\t<\/div>\n\t<\/div>\n<\/template>\n\n<script>\n\tdocument.addEventListener(\"alpine:init\", () => {\n\t\tAlpine.data(\"sliderselection\", ({ items, onSelected, id, domLoaded, options = {} }) => ({\n\t\t\ttemplate: null,\n\t\t\titems: [],\n\t\t\tonSelected: onSelected,\n\t\t\tid: id,\n\t\t\tvalue: 0,\n\t\t\tslider: items,\n\t\t\tdomLoaded: domLoaded,\n\t\t\tmin: 0,\n\t\t\tmax: 0,\n\t\t\toptions: options,\n\t\t\tinitial: true,\n\t\t\tsliderReady: false,\n\t\t\tinit() {\n\t\t\t\tthis.setup();\n\t\t\t\tthis.$nextTick(() => {\n\t\t\t\t\tthis.mounted();\n\t\t\t\t\tthis.render();\n\t\t\t\t});\n\t\t\t},\n\t\t\tsetup() {\n\t\t\t\tthis.template = document.getElementById(\"sliderSelectionComponent\");\n\t\t\t},\n\t\t\tmounted() {\n\t\t\t\tif (items.length) {\n\t\t\t\t\tthis.items = items.sort((a, b) => (Number(a.value) > Number(b.value) ? 1 : -1));\n\n\t\t\t\t\tthis.min = this.items[0].value;\n\t\t\t\t\tthis.max = this.items.slice(-1)[0].value;\n\t\t\t\t\tthis.value = this.max;\n\t\t\t\t}\n\t\t\t},\n\t\t\trender() {\n\t\t\t\tthis.$el.innerHTML = this.template.innerHTML;\n\n\t\t\t\tdocument.addEventListener(\"DOMContentLoaded\", () => {\n\t\t\t\t\tthis.domLoaded = true;\n\t\t\t\t\tthis.initRangeSlider();\n\t\t\t\t});\n\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\tif (!this.domLoaded) {\n\t\t\t\t\t\tthis.initRangeSlider();\n\t\t\t\t\t}\n\t\t\t\t}, 400);\n\t\t\t},\n\t\t\tinitRangeSlider() {\n\t\t\t\tthis.domLoaded = true;\n\t\t\t\tthis.initial = true;\n\n\t\t\t\tconst min = this.roundMin(parseInt(this.min, 10));\n\t\t\t\tconst max = this.roundMax(parseInt(this.max, 10));\n\n\t\t\t\tthis.slider = new rSlider({\n\t\t\t\t\ttarget: \"#\" + this.id,\n\t\t\t\t\tvalues: options.values && options.values == true ? this.getAllValues() : { min: min, max: max },\n\t\t\t\t\trange: true,\n\t\t\t\t\ttooltip: true,\n\t\t\t\t\tstep: options.step ? options.step : 1,\n\t\t\t\t\tscale: false,\n\t\t\t\t\tlabels: true,\n\t\t\t\t\tvariableWidth: true,\n\t\t\t\t\tunit: \"kg\",\n\t\t\t\t\tset: [this.min, this.max],\n\t\t\t\t\tonChange: (vals) => {\n\t\t\t\t\t\t\/\/ vals is not correct so get from index\n\t\t\t\t\t\tlet min = Number(this.slider.conf.values[this.slider.values.start]);\n\t\t\t\t\t\tlet max = Number(this.slider.conf.values[this.slider.values.end]);\n\t\t\t\t\t\tthis.setValue(min, max);\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tconst sliderElements = document.querySelectorAll(\".rs-container\");\n\t\t\t\tsliderElements.forEach((element) => {\n\t\t\t\t\telement.addEventListener(\"mousedown\", () => {\n\t\t\t\t\t\tthis.initial = false;\n\t\t\t\t\t});\n\t\t\t\t\telement.addEventListener(\"touchstart\", () => {\n\t\t\t\t\t\tthis.initial = false;\n\t\t\t\t\t});\n\t\t\t\t});\n\n\t\t\t\tthis.sliderReady = true;\n\t\t\t},\n\t\t\tsetValue(min, max) {\n\t\t\t\tlet range = this.items.filter((item) => {\n\t\t\t\t\tconst itemValue = Number(item.value);\n\t\t\t\t\treturn itemValue >= min && itemValue <= max;\n\t\t\t\t});\n\t\t\t\tif (range.length === 0) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlet maxOfRange = range.reduce((max, obj) => {\n\t\t\t\t\treturn Number(obj.value) > Number(max.value) ? obj : max;\n\t\t\t\t}, range[0]);\n\n\t\t\t\tlet minOfRange = range.reduce((min, obj) => {\n\t\t\t\t\treturn Number(obj.value) < Number(min.value) ? obj : min;\n\t\t\t\t}, range[0]);\n\n\t\t\t\tif (maxOfRange) {\n\t\t\t\t\tif (minOfRange) {\n\t\t\t\t\t\tmaxOfRange[\"min\"] = minOfRange.value;\n\t\t\t\t\t\tthis.select(maxOfRange, false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tselect(property) {\n\t\t\t\tthis.onSelected(property, this.initial);\n\t\t\t},\n\t\t\tgetAllValues() {\n\t\t\t\treturn this.items.map((item) => Number(item.value));\n\t\t\t},\n\t\t\troundMin(num) {\n\t\t\t\tif (num > 100) {\n\t\t\t\t\treturn Math.floor(num \/ 50) * 50;\n\t\t\t\t} else {\n\t\t\t\t\treturn Math.floor(num \/ 10) * 10;\n\t\t\t\t}\n\t\t\t},\n\t\t\troundMax(num) {\n\t\t\t\tif (num > 100) {\n\t\t\t\t\treturn Math.ceil(num \/ 50) * 50;\n\t\t\t\t} else {\n\t\t\t\t\treturn Math.ceil(num \/ 10) * 10;\n\t\t\t\t}\n\t\t\t},\n\t\t}));\n\t});\n<\/script>\n<template id=\"choiceSelectionComponent\">\n  <div class=\"bikefilter-schaltung mb-4\">\n    <fieldset>\n      <template x-for=\"(item, index) in items\">\n        <div class=\"form-check\">\n          <input\n            type=\"radio\"\n            :id=\"item.propertyPath + index\"\n            name=\"drone\"\n            value=\"huey\"\n            x-on:input.change=\"onSelected(item, false)\"\n            class=\"form-check-input\"\n          \/>\n          <label\n            :for=\"item.propertyPath + index\"\n            x-text=\"$store.utils.t(item.context.text)\"\n            class=\"form-check-label\"\n          ><\/label>\n        <\/div>\n      <\/template>\n    <\/fieldset>\n  <\/div>\n<\/template>\n\n<script>\n  document.addEventListener(\"alpine:init\", () => {\n    Alpine.data(\"choiceselection\", ({ items, onSelected }) => ({\n      template: null,\n      items: items,\n      onSelected: onSelected,\n      value: 0,\n      init() {\n        this.setup();\n        this.$nextTick(() => {\n          this.mounted();\n          this.render();\n        });\n      },\n      setup() {\n        this.template = document.getElementById(\"choiceSelectionComponent\");\n      },\n      mounted() {},\n      render() {\n        this.$el.innerHTML = this.template.innerHTML;\n      },\n    }));\n  });\n<\/script>\n<template id=\"multipleChoiceSelectionComponent\">\n  <div class=\"bikefilter-schaltung mb-4\">\n    <fieldset>\n      <template x-for=\"(item, index) in items\">\n        <div class=\"form-check\">\n          <input\n            type=\"checkbox\"\n            :id=\"item.propertyPath + index\"\n            name=\"drone\"\n            value=\"huey\"\n            x-on:input.change=\"onSelected(item, $event.target.checked, false)\"\n            class=\"form-check-input\"\n          \/>\n          <label\n            :for=\"item.propertyPath + index\"\n            x-text=\"$store.utils.t(item.context.text)\"\n            class=\"form-check-label\"\n          ><\/label>\n        <\/div>\n      <\/template>\n    <\/fieldset>\n  <\/div>\n<\/template>\n\n<script>\n  document.addEventListener(\"alpine:init\", () => {\n    Alpine.data(\"multiplechoiceselection\", ({ items, onSelected }) => ({\n      template: null,\n      items: items,\n      onSelected: onSelected,\n      value: 0,\n      init() {\n        this.setup();\n        this.$nextTick(() => {\n          this.mounted();\n          this.render();\n        });\n      },\n      setup() {\n        this.template = document.getElementById(\n          \"multipleChoiceSelectionComponent\"\n        );\n      },\n      mounted() {},\n      render() {\n        this.$el.innerHTML = this.template.innerHTML;\n      },\n    }));\n  });\n<\/script>\n<template id=\"colorSelectionComponent\">\n  <div class=\"color-options mb-4\">\n    <ul class=\"ps-0\">\n      <template x-for=\"(item, index) in items\">\n        <li>\n          <button\n            @click=\"selected(item, index, false)\"\n            :class=\"'btn filter-color-' + item.value.toLowerCase() + ' ' + ((selectedIndex == index) ? 'active' : '')\"\n            data-bs-toggle=\"tooltip\"\n            data-placement=\"top\"\n            :title=\"item.description\"\n          ><\/button>\n        <\/li>\n      <\/template>\n    <\/ul>\n  <\/div>\n<\/template>\n\n<script>\n  document.addEventListener(\"alpine:init\", () => {\n    Alpine.data(\"colorselection\", ({ items, onSelected }) => ({\n      template: null,\n      items: items,\n      onSelected: onSelected,\n      selectedIndex: -1,\n      init() {\n        this.setup();\n        this.$nextTick(() => {\n          this.mounted();\n          this.render();\n        });\n      },\n      setup() {\n        this.template = document.getElementById(\"colorSelectionComponent\");\n      },\n      mounted() {},\n      render() {\n        this.$el.innerHTML = this.template.innerHTML;\n      },\n      selected(item, index, initial) {\n        this.selectedIndex = index;\n        this.onSelected(item, initial);\n      },\n    }));\n  });\n<\/script>\n<template id=\"bikefinderComponent\">\n  <div class=\"bikefinder\">\n    <div class=\"row\">\n      <div class=\"col-md-4 col-lg-3 bikefinder-filter-col\">\n        <button\n          class=\"btn btn-primary filter-toggle d-md-none mb-3\"\n          data-show=\"Filter anzeigen\"\n          data-hide=\"Filter ausblenden\"\n          x-text=\"$store.utils.t($store.utils.translations.showFilter)\"\n        ><\/button>\n\n        <div\n          class=\"bikefinder-filter-content\"\n          x-data=\"filter({ \n            frameFilterProps: uniquePropFrames(), \n            typeFilterProps: model.uniquePropTypes(), \n            batteryFilterProps: uniquePropBatteries(), \n            gearFilterProps: uniquePropGears(),\n            priceFilterProps: uniquePropPrices(),\n            weightFilterProps: uniquePropWeights(),\n            saleFilterProps: model.uniquePropSales(),\n            lastchanceFilterProps: model.uniquePropLastchance(),\n            newbikeFilterProps: model.uniquePropNewbike(),\n            countOfFilteredBikes: state.countOfFilteredBikes,\n            maxBikesCount: state.maxBikesCount,\n            colorFilterProps: uniquePropColors(),\n            selectedFilter: state.selectedFilter,\n            controller: controller\n          })\"\n        ><\/div>\n      <\/div>\n      <div class=\"col-md-8 col-lg-9\">\n        <div class=\"dropdown text-end dropdown-underline\">\n          <button\n            class=\"btn btn-secondary dropdown-toggle p-0 pb-1 me-3 me-md-4\"\n            type=\"button\"\n            id=\"dropdownSort\"\n            data-bs-toggle=\"dropdown\"\n            aria-haspopup=\"true\"\n            aria-expanded=\"false\"\n            x-text=\"$store.utils.t($store.utils.translations.sortBy)\"\n          ><\/button>\n\n          <div\n            class=\"dropdown-menu dropdown-menu-end dropdown-sort\"\n            aria-labelledby=\"dropdownSort\"\n          >\n            <a\n              @click=\"controller.sortNewestAlphaAction()\"\n              class=\"dropdown-item\"\n              :class=\"state.currentSort == 'newestalpha' ? 'active' : ''\"\n              id=\"sort-newest-alpha\"\n              x-text=\"$store.utils.t($store.utils.translations.sortByNewestAlpha)\"\n            ><\/a>\n            <a\n              @click=\"controller.sortAlphaAction()\"\n              class=\"dropdown-item\"\n              :class=\"state.currentSort == 'alpha' ? 'active' : ''\"\n              id=\"sort-alpha\"\n              x-text=\"$store.utils.t($store.utils.translations.sortByAlpha)\"\n            ><\/a>\n            <a\n              @click=\"controller.sortPriceAction()\"\n              class=\"dropdown-item\"\n              :class=\"state.currentSort == 'priceASC' ? 'active' : 'peter'\"\n              id=\"sort-price-asc\"\n              x-text=\"$store.utils.t($store.utils.translations.sortByAsc)\"\n            ><\/a>\n            <a\n              @click=\"controller.sortPriceAction(true)\"\n              class=\"dropdown-item\"\n              :class=\"state.currentSort == 'priceDESC' ? 'active' : 'peter'\"\n              id=\"sort-price-desc\"\n              x-text=\"$store.utils.t($store.utils.translations.sortByDesc)\"\n            ><\/a>\n          <\/div>\n        <\/div>\n        <template x-if=\"showBikeSort &#038;&#038; initialReady\">\n          <div\n            x-data=\"{ bikesByGenerations: state.filteredBikesGroupedByGenerations  }\"\n            class=\"bikefinder-bikes p-md-3 p-lg-4\"\n          >\n            <div\n              x-show=\"state.countOfFilteredBikes.value == 0\"\n              class=\"no-results\"\n            >\n              <span\n                x-text=\"$store.utils.t($store.utils.translations.noResults)\"\n              ><\/span>\n            <\/div>\n            <div class=\"bikefinder-grid\">\n              <template x-for=\"(value, index) in bikesByGenerations\">\n                <div\n                  x-data=\"biketeaser({ frames: bikesByGenerations[index], framesAsGenerations: true, framesMeta: model.frameFilterModel })\"\n                ><\/div>\n              <\/template>\n            <\/div>\n          <\/div>\n        <\/template>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/template>\n\n<script>\n  document.addEventListener(\"alpine:init\", () => {\n    Alpine.data(\n      \"bikefinder\",\n      ({\n        blockId,\n        model,\n        store,\n        state,\n        utils,\n        controller,\n        filterParamsResolver,\n      }) => ({\n        blockId: blockId,\n        model: model,\n        store: store,\n        state: state,\n        utils: utils,\n        controller: controller,\n        filterParamsResolver: filterParamsResolver,\n        showBikeSort: false,\n        initialReady: false,\n        init() {\n          this.setup();\n          this.$nextTick(() => {\n            this.mounted();\n            this.render();\n          });\n        },\n        setup() {\n          this.template = document.getElementById(\"bikefinderComponent\");\n        },\n        mounted() {\n          this.model.variantsToUniqueFramesAndFlattedVariants(\n            this.model.modules[this.blockId].bikes,\n            this.blockId\n          );\n\n          this.$watch(\"state.selectedFilter\", (value) => {\n            this.filter(value);\n          });\n\n          this.$watch(\"state.filteredBikesGroupedByGenerations\", (grouped) => {\n            this.reactiveBikeSort();\n          });\n\n          setTimeout(() => {\n            const { filters } = this.filterParamsResolver.defaultParams;\n            Object.keys(filters).forEach((key) => {\n              this.filterByParams(key, filters[key], true);\n            });\n            \/\/ wait to set all default filter properties - for resetFilterAction() -- to prevent watch updates on state.selectedFilter\n            this.state.defaultFilterProperties = JSON.stringify(\n              this.state.selectedFilter.properties\n            );\n\n            this.initialReady = true;\n          }, 1500);\n        },\n        render() {\n          this.$el.innerHTML = this.template.innerHTML;\n        },\n        filterByParams(key, value, initial) {\n          const propsMap = {\n            category: {\n              getProps: () => this.model.uniquePropTypes(),\n              keyName: \"value\",\n            },\n            primary_basecolour: {\n              getProps: () => this.uniquePropColors(),\n              keyName: \"value\",\n            },\n            frame_type: {\n              getProps: () => this.uniquePropFrames(),\n              keyName: \"value\",\n            },\n            \"bike_specifics.gear.gear_type\": {\n              getProps: () => this.uniquePropGears(),\n              keyName: \"value\",\n            },\n            \"ebike_specifics.battery_range.battery_capacity\": {\n              getProps: () => this.uniquePropBatteries(),\n              keyName: \"value\",\n            },\n          };\n\n          if (key in propsMap) {\n            const mapResult = propsMap[key];\n            const prop = mapResult\n              .getProps()\n              .find((prop) => prop[mapResult[\"keyName\"]] == value);\n            if (prop) {\n              this.controller.filterAddPropertyAction(prop, initial);\n            }\n\n            this.state.selectedFilter.initial = false;\n          }\n        },\n\n        filter(value) {\n          this.model.groupedFrames[this.blockId].forEach((group) => {\n            group.forEach((frame) => {\n              frame.filter(value);\n            });\n          });\n\n          this.state.filteredBikes =\n            this.controller.getOnlyBikesWhereFramesWithVariants(\n              this.model.groupedFrames[this.blockId]\n            );\n\n          const generations = this.controller.getGenerations(\n            this.state.filteredBikes\n          );\n          this.state.filteredBikesGroupedByGenerations = generations;\n\n          const countOfBikes = this.controller.calculateCountOfFilteredBikes(\n            this.blockId\n          );\n          if (this.state.selectedFilter.initial) {\n            this.state.maxBikesCount.value = countOfBikes;\n          }\n\n          this.state.countOfFilteredBikes.value = countOfBikes;\n          this.controller.sortNewestAlphaAction();\n\n          \/\/ Sync Filter\n          this.filterParamsResolver.setFiltersParams(\n            this.filterParamsResolver.getActiveFilters()\n          );\n        },\n        reactiveBikeSort() {\n          this.showBikeSort = false;\n          this.$nextTick(() => {\n            this.showBikeSort = true;\n          });\n        },\n        uniquePropFrames() {\n          return this.model.uniquePropFrames(this.blockId);\n        },\n        uniquePropBatteries() {\n          return this.model.uniquePropBatteries(this.blockId);\n        },\n        uniquePropGears() {\n          return this.model.uniquePropGears(this.blockId);\n        },\n        uniquePropPrices() {\n          return this.model.uniquePropPrices(this.blockId);\n        },\n        uniquePropWeights() {\n          return this.model.uniquePropWeights(this.blockId);\n        },\n        uniquePropColors() {\n          return this.model.uniquePropColors(this.blockId);\n        },\n      })\n    );\n  });\n<\/script>\n\n<style type=\"text\/css\">\n  body.modal-open {\n    padding-right: 0px !important;\n    overflow: auto !important;\n  }\n\n  body {\n    padding-right: 0 !important;\n  }\n\n  body.modal-open header,\n  body.modal-open .content {\n    padding-right: 0 !important;\n  }\n<\/style>\n<div x-data='bikefinder({ \n    blockId: \"1wAG4F\", \n    model: $store.bikefinderModel, \n    store: $store.bikefinderStore, \n    state: $store.bikefinderState, \n    utils: $store.utils, \n    controller: $store.bikefinderController,\n    filterParamsResolver: $store.bikefinderFilterParamsResolver })'>\n<\/div>\n<\/div>\n<\/div>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n<\/div><\/div>\n\n<div class=\"lazyblock-nncontainer-Z1I6h79 wp-block-lazyblock-nncontainer\"><div class=\"section-standard\">\r\n  <div class=\"container\" id=\"container-Z1I6h79\">\r\n    <div class=\"row\">\r\n      <div class=\"col py-4 py-lg-5\">\r\n        <div class=\"lazyblock-inner-blocks\">\n\n<h3 class=\"wp-block-heading\">Miksi valita moottoriton polkupy\u00f6r\u00e4 Diamantilta?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Moottorittomat polkupy\u00f6r\u00e4mme on suunniteltu heille, jotka arvostavat omalla voimalla polkemisen puhdasta iloa. T\u00e4ss\u00e4 on malliemme erottuvat ominaisuudet:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>1x Voimansiirto, 11 tai 12 vaihdetta<\/strong>: Tarvitset vain yhden k\u00e4den \u2013 t\u00e4m\u00e4n helpommaksi ei py\u00f6r\u00e4ily muutu! Yksiketjuiset eturattaamme ovat kevyempi\u00e4 ja kuluvat hitaammin. Yl\u00e4m\u00e4ess\u00e4 j\u00e4rjestelm\u00e4si toimii yht\u00e4 hyvin kuin perinteiset 30-vaihteiset j\u00e4rjestelm\u00e4t.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Kiinnikevaihtoehtoja on runsaasti:<\/strong> Rungoissamme on lukuisia kiinnityspisteit\u00e4 lis\u00e4varusteille, kuten pullotelineille, laukuille tai lukoille. Unelmoinnin voi jo aloittaa, koska olet ehdottomasti valmis seikkailuun.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Vankka takatavarateline<\/strong>: Diamantin Clever Rack on vakaa ja monipuolinen tavarateline, jossa on kiinnityshihna ja joka on yhteensopiva sivulaukkujen ja lis\u00e4kuorman kanssa. Se tukee MIK Side- tai Ortlieb 3.1 -j\u00e4rjestelmi\u00e4 ongelmattoman integroinnin takaamiseksi.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>136 kg:n painokapasiteetti<\/strong>: Raskaille kuormille, pidemmille kuljettajille tai lasten istuimille suunnitellut polkupy\u00f6r\u00e4mme kest\u00e4v\u00e4t helposti jopa 136 kg:n kokonaispainon.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Kest\u00e4v\u00e4 runko<\/strong>: Arvostamme helppoa huollettavuutta, standardoituja osien liit\u00e4nt\u00f6j\u00e4 ja \u00e4\u00e4rimm\u00e4ist\u00e4 kest\u00e4vyytt\u00e4. Rungot kest\u00e4v\u00e4t koko elini\u00e4n \u2013 takuulla!<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Parannetut turvallisuusominaisuudet:<\/strong> Takavalot pys\u00e4k\u00f6intivalolla, heijastavat rengasnauhat ja dynamo-virtal\u00e4hteiset valaistusj\u00e4rjestelm\u00e4t pit\u00e4v\u00e4t sinut n\u00e4kyviss\u00e4 ja turvassa.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Monipuoliset voimansiirtovaihtoehdot<\/strong>: Valitse huoltovapaa hihnaveto napavaihteistolla tai klassinen ketjuvetoinen vaihtaja.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Kenelle moottoriton Diamant-polkupy\u00f6r\u00e4\u00e4 sopii parhaiten?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Polkupy\u00f6r\u00e4mme sopivat parhaiten seuraaville ihmisille:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Ty\u00f6matkapy\u00f6r\u00e4ilij\u00e4t<\/strong>: Moderni voimansiirto, huoltovapaa rakenne ja tyylik\u00e4s ulkon\u00e4k\u00f6 tekev\u00e4t py\u00f6rist\u00e4mme t\u00e4ydellisi\u00e4 p\u00e4ivitt\u00e4iseen k\u00e4ytt\u00f6\u00f6n.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Seikkailijat<\/strong>: Kattavien kiinnikevaihtoehtojen ja kest\u00e4v\u00e4n rungon ansiosta ne sopivat erinomaisesti py\u00f6r\u00e4retkille ja pidemp\u00e4\u00e4nkin py\u00f6r\u00e4matkailuun.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Perinteiden vaalijat<\/strong>: Kenenk\u00e4\u00e4n ei tulisi olla pakko ajaa s\u00e4hk\u00f6py\u00f6r\u00e4ll\u00e4. Jos et halua tehd\u00e4 niin, \u00e4l\u00e4 tee niin. P\u00e4\u00e4set silti hy\u00f6tym\u00e4\u00e4n innovaatioista. Jatkamme my\u00f6s moottorittomien polkupy\u00f6rien teknologian kehitt\u00e4mist\u00e4.<\/li>\n<\/ul>\n\n<\/div>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n<\/div><\/div>\n\n\n<div class=\"wp-block-cover is-light\"><img data-dominant-color=\"8a7782\" data-has-transparency=\"false\" loading=\"lazy\" decoding=\"async\" width=\"2560\" height=\"1707\" sizes=\"auto, (max-width: 2560px) 100vw, 2560px\" class=\"wp-block-cover__image-background wp-image-56751 size-full not-transparent\" alt=\"Di 25 mahon lifestyle\" src=\"https:\/\/www.diamantrad.com\/wp-content\/uploads\/2025\/03\/DI_25_Mahon_Lifestyle_67I2798_300-scaled.avif\" style=\"--dominant-color: #8a7782; object-position:43% 50%\" data-object-fit=\"cover\" data-object-position=\"43% 50%\" srcset=\"https:\/\/www.diamantrad.com\/wp-content\/uploads\/2025\/03\/DI_25_Mahon_Lifestyle_67I2798_300-scaled.avif 2560w, https:\/\/www.diamantrad.com\/wp-content\/uploads\/2025\/03\/DI_25_Mahon_Lifestyle_67I2798_300-300x200.avif 300w, https:\/\/www.diamantrad.com\/wp-content\/uploads\/2025\/03\/DI_25_Mahon_Lifestyle_67I2798_300-1024x683.avif 1024w, https:\/\/www.diamantrad.com\/wp-content\/uploads\/2025\/03\/DI_25_Mahon_Lifestyle_67I2798_300-768x512.avif 768w, https:\/\/www.diamantrad.com\/wp-content\/uploads\/2025\/03\/DI_25_Mahon_Lifestyle_67I2798_300-1536x1024.avif 1536w, https:\/\/www.diamantrad.com\/wp-content\/uploads\/2025\/03\/DI_25_Mahon_Lifestyle_67I2798_300-2048x1365.avif 2048w\" \/><span aria-hidden=\"true\" class=\"wp-block-cover__background has-background-dim-0 has-background-dim\"><\/span><div class=\"wp-block-cover__inner-container is-layout-constrained wp-block-cover-is-layout-constrained\">\n<p class=\"has-text-align-center has-large-font-size wp-block-paragraph\"><\/p>\n<\/div><\/div>\n\n\n<div class=\"lazyblock-nncontainer-Z2bvcjN wp-block-lazyblock-nncontainer\"><div class=\"section-standard\">\r\n  <div class=\"container\" id=\"container-Z2bvcjN\">\r\n    <div class=\"row\">\r\n      <div class=\"col py-4 py-lg-5\">\r\n        <div class=\"lazyblock-inner-blocks\">\n\n<h3 class=\"wp-block-heading\">Voinko j\u00e4lkiasentaa polkupy\u00f6r\u00e4\u00e4ni moottorin?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Huomaamme klassisten polkupy\u00f6rien s\u00e4hk\u00f6py\u00f6riksi muuntamisen kiinnostavan ihmisi\u00e4 kasvavissa m\u00e4\u00e4rin. Py\u00f6r\u00e4n pedelec-ajoneuvoksi (enint\u00e4\u00e4n 25 km\/h kulkevaksi) muuntaminen itsess\u00e4\u00e4n on laillisesti sallittua. Positiivista, positiivista. Prosessiin liittyy kuitenkin muutamia mutkia.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Muuntamisen suorittajana omaksut valmistajan roolin. Siksi tuotevastuu on sinun vastuullasi. Sinun on maksettava muutamista testeist\u00e4, joilla varmistetaan, ett\u00e4 muunnettu py\u00f6r\u00e4si t\u00e4ytt\u00e4\u00e4 CE-sertifiointistandardit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Jos haluat sivuuttaa tuon osan prosessista, koska kukaan ei kuitenkaan koskaan hallitse polkupy\u00f6r\u00e4\u00e4: Jos kolmas henkil\u00f6 k\u00e4rsii vahinkoa sinun valmistamastasi viallisesta polkupy\u00f6r\u00e4st\u00e4, olet itse vastuussa. Ja vastuuvakuutuksesi ei v\u00e4ltt\u00e4m\u00e4tt\u00e4 korvaa vahinkoa n\u00e4iss\u00e4 olosuhteissa.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Huomaa my\u00f6s, ett\u00e4 moottorittomien polkupy\u00f6rien runkoja ei ole suunniteltu kest\u00e4m\u00e4\u00e4n moottori-osasarjojen aiheuttamaa lis\u00e4voimaa. J\u00e4lkiasennus voi toimia. Emme vain koskaan lupaa toimivuutta \u2013 emmek\u00e4 voi taata py\u00f6r\u00e4n turvallisuutta tai yll\u00e4pit\u00e4\u00e4 takuuta, jos ongelmia ilmenee.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Laatu suojelee ymp\u00e4rist\u00f6\u00e4 \u2013 osta py\u00f6r\u00e4 koko el\u00e4m\u00e4n ajaksi<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Py\u00f6r\u00e4mme on suunniteltu pitk\u00e4ik\u00e4isiksi, huoltovapaiksi ja resurssitehokkaiksi. Kehit\u00e4mme jatkuvasti materiaaleja ja tuotantoprosesseja asettaaksemme korkeampia ymp\u00e4rist\u00f6vaatimuksia \u2013 ja vaadimme samaa sitoutumista my\u00f6s toimittajiltamme.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Yksi n\u00e4kyv\u00e4 tulos on uudelleenk\u00e4ytett\u00e4v\u00e4t, t\u00e4ysin kierr\u00e4tett\u00e4v\u00e4t pahviset py\u00f6r\u00e4pakkauksemme. Vaikutusvaltaisin vipuvoima on kuitenkin itse py\u00f6r\u00e4: mit\u00e4 pidemp\u00e4\u00e4n ajat sill\u00e4, sit\u00e4 pienemm\u00e4ksi sinun ja meid\u00e4n ymp\u00e4rist\u00f6jalanj\u00e4lkemme muuttuu. Siksi polkupy\u00f6r\u00e4mme on valmistettu kest\u00e4m\u00e4\u00e4n ja olemaan yhteensopivia varaosien kanssa monien vuosien ajan.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Diamant-polkupy\u00f6r\u00e4ll\u00e4 valitset laadun ja tuotteen, joka on suunniteltu tuomaan iloa viel\u00e4 vuosienkin k\u00e4yt\u00f6n j\u00e4lkeen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Miksi ette vain kutsu sit\u00e4 py\u00f6r\u00e4ksi?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Maasta riippuen jotkut kutsuvat moottoritonta py\u00f6r\u00e4\u00e4 biobike-py\u00f6r\u00e4ksi, toiset klassiseksi py\u00f6r\u00e4ksi, toiset taas akustiseksi py\u00f6r\u00e4ksi, ja monet yksinkertaisesti polkupy\u00f6r\u00e4ksi. Ja he ovat oikeassa \u2013 kun ihmiset kuulevat sanan &#8220;py\u00f6r\u00e4&#8221;, he kuvittelevat yleens\u00e4 mieless\u00e4\u00e4n py\u00f6r\u00e4n, jossa ei ole moottoria. Silti s\u00e4hk\u00f6py\u00f6r\u00e4kin on polkupy\u00f6r\u00e4. Moottorilla.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Mutta eik\u00f6 ole hieman omituista m\u00e4\u00e4ritell\u00e4 polkupy\u00f6r\u00e4n alkuper\u00e4inen k\u00e4site sen perusteella, mit\u00e4 py\u00f6r\u00e4st\u00e4 puuttuu? Eik\u00f6 meid\u00e4n pit\u00e4isi keskitty\u00e4 siihen, mit\u00e4 se todella on? Siksi on syntynyt k\u00e4sitteit\u00e4 kuten biobike, jotka korostavat polkupy\u00f6rien sataprosenttisesti ihmisvoimalla toimivaa luonnetta. S\u00e4hk\u00f6avusteinen polkupy\u00f6r\u00e4kin toimii kuitenkin ihmisen voimin.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Yleens\u00e4 kutsumme kaikkia polkupy\u00f6ri\u00e4 vain polkupy\u00f6riksi \u2013 riippumatta siit\u00e4, onko niiss\u00e4 moottori vai ei. Mutta t\u00e4m\u00e4n sivun on annettava sinulle intuitiivinen k\u00e4sitys siit\u00e4, mit\u00e4 polkupy\u00f6ri\u00e4 t\u00e4\u00e4lt\u00e4 l\u00f6ytyy. Vaikka &#8220;moottoriton polkupy\u00f6r\u00e4&#8221; ei ole sanayhdistelm\u00e4n\u00e4 ihanteellinen, se on ainakin helposti ymm\u00e4rrett\u00e4v\u00e4. Kutsu sit\u00e4 miksi haluat \u2013 me autamme sinua l\u00f6yt\u00e4m\u00e4\u00e4n t\u00e4ydellisen moottorittoman, lihasvoimalla toimivan akustisen bio-py\u00f6r\u00e4n.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong><strong>Juuri sinulle sopiva py\u00f6r\u00e4 \u2013 L\u00f6yd\u00e4 liikkumisen ilo uudelleen<\/strong><\/strong><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Moottoriton polkupy\u00f6r\u00e4 on puhdasta iloa ja liikkumisen vapautta. Huolellisesti suunnitellut ja korkealaatuisista materiaaleista valmistetut polkupy\u00f6r\u00e4mme rikastuttavat arkeasi ja valmistavat sinut kaikkiin seikkailuihin.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">L\u00f6yd\u00e4 t\u00e4n\u00e4\u00e4n itsellesi sopiva py\u00f6r\u00e4 ja koe hiljainen, moottoriton ajokokemus Diamant-polkupy\u00f6r\u00e4ll\u00e4!<\/p>\n\n<\/div>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n<\/div><\/div>\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":3,"featured_media":66696,"parent":65913,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_acf_changed":false,"slim_seo":{"title":"Moottorittomat polkupy\u00f6r\u00e4t Diamantilta","description":"Tutustu moottorittomiin py\u00f6riin vaellukseen, urheiluun ja kaupunkiin \u2013 sek\u00e4 miehille ett\u00e4 naisille."},"footnotes":""},"class_list":["post-66694","page","type-page","status-publish","has-post-thumbnail","hentry"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.diamantrad.com\/fi-FI\/wp-json\/wp\/v2\/pages\/66694","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.diamantrad.com\/fi-FI\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.diamantrad.com\/fi-FI\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.diamantrad.com\/fi-FI\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/www.diamantrad.com\/fi-FI\/wp-json\/wp\/v2\/comments?post=66694"}],"version-history":[{"count":4,"href":"https:\/\/www.diamantrad.com\/fi-FI\/wp-json\/wp\/v2\/pages\/66694\/revisions"}],"predecessor-version":[{"id":75858,"href":"https:\/\/www.diamantrad.com\/fi-FI\/wp-json\/wp\/v2\/pages\/66694\/revisions\/75858"}],"up":[{"embeddable":true,"href":"https:\/\/www.diamantrad.com\/fi-FI\/wp-json\/wp\/v2\/pages\/65913"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.diamantrad.com\/fi-FI\/wp-json\/wp\/v2\/media\/66696"}],"wp:attachment":[{"href":"https:\/\/www.diamantrad.com\/fi-FI\/wp-json\/wp\/v2\/media?parent=66694"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}