Commit 65c2fc5f63c762c09cc6ce2a7f12850c99ff5357

Authored by Андрей Ларионов
1 parent 0b9fba0955
Exists in master

Работа со скриптами карт. Работа со счетчиком на странице 'Офис'

Showing 12 changed files with 1719 additions and 1515 deletions Side-by-side Diff

app/Classes/RusDate.php
... ... @@ -4,6 +4,8 @@
4 4 namespace App\Classes;
5 5  
6 6  
  7 +use DateTime;
  8 +
7 9 class RusDate
8 10 {
9 11 public static function russian_date($date = null){
... ... @@ -72,4 +74,46 @@ class RusDate
72 74 unset($_COOKIE['favorite_house']);
73 75 //print_r($_COOKIE['arr']);
74 76 }
  77 +
  78 + public static function interval_month($date) {
  79 + $now = new DateTime();
  80 + $date = new DateTime($date); //::createFromFormat("Y-m-d H:i", $date);
  81 + $interval = $now->diff($date);
  82 + $y = $interval->y;
  83 + $d = $interval->d;
  84 + $h = $interval->h;
  85 + $i = $interval->i;
  86 +
  87 + if ($d > 30)
  88 + return true;
  89 + else
  90 + return false;
  91 + }
  92 +
  93 + public static function interval_day($date) {
  94 + $now = new DateTime();
  95 + $date = new DateTime($date); //::createFromFormat("Y-m-d H:i", $date);
  96 + $interval = $now->diff($date);
  97 + $y = $interval->y;
  98 + $d = $interval->d;
  99 + $h = $interval->h;
  100 + $i = $interval->i;
  101 +
  102 + if (($h > 24) || ($d > 0) || ($y > 0))
  103 + return true;
  104 + else
  105 + return false;
  106 + }
  107 +
  108 + public static function ip_addr_client() {
  109 + $client = @$_SERVER['HTTP_CLIENT_IP'];
  110 + $forward = @$_SERVER['HTTP_X_FORWARDED_FOR'];
  111 + $remote = @$_SERVER['REMOTE_ADDR'];
  112 +
  113 + if(filter_var($client, FILTER_VALIDATE_IP)) $ip = $client;
  114 + elseif(filter_var($forward, FILTER_VALIDATE_IP)) $ip = $forward;
  115 + else $ip = $remote;
  116 +
  117 + return $ip;
  118 + }
75 119 }
app/Http/Controllers/MainController.php
... ... @@ -10,11 +10,13 @@ use App\Models\format_area;
10 10 use App\Models\House;
11 11 use App\Models\ModelMailFeedback;
12 12 use App\Models\News;
  13 +use App\Models\Page;
13 14 use App\Models\Partners;
14 15 use App\Models\type_area;
15 16 use Illuminate\Database\Eloquent\Model;
16 17 use Illuminate\Http\Request;
17 18 use App\Classes\RusDate;
  19 +use Illuminate\Support\Facades\DB;
18 20 use Illuminate\Support\Facades\Mail;
19 21 use PhpParser\Node\Stmt\Switch_;
20 22 use Illuminate\Support\Facades\Response;
... ... @@ -732,13 +734,46 @@ class MainController extends Controller
732 734 /*
733 735 * Посмотр конктретного предложение офиса
734 736 */
735   - public function Offer(House $house) {
  737 + public function Offer(House $house, Request $request) {
736 738 $houses = House::with('areas');
737 739 $houses = $houses->where('type_area_id', '=', $house->typearea->id);
738 740 $houses = $houses->where('format_house', '=', $house->format_house);
739 741 $houses = $houses->orderByDesc('created_at')->limit(8)->get();
740 742  
741   - return view('house.post', compact('house', 'houses'));
  743 + //получение адреса страницы
  744 + $url = $request->url();
  745 + // получение ip-адреса клиента
  746 + $ip = RusDate::ip_addr_client();
  747 +
  748 + //получение выборки данных из базы данных по данной странице
  749 + $page_ = Page::query()->where('url', '=', "$url")->
  750 + orderBy('created_at')->limit(1)->get();
  751 +
  752 + //если интервал времени больше суток, то обнуляем счетчик
  753 + if ($page_->count()) {
  754 + $result = RusDate::interval_day($page_[0]->created_at);
  755 + if ($result) {
  756 + DB::table('pages')->where('url', '=', "$url")->delete();
  757 + }
  758 + }
  759 + // проверяем если в базе данных данный ip-адрес
  760 + $count_user = DB::table('pages')->where('ipaddress', '=', "$ip")->
  761 + where('url', '=', "$url")->get();
  762 + // если есть, то обновляем дату просмотра
  763 + if ($count_user->count() > 0) {
  764 + DB::table('pages')->where('ipaddress', '=', "$ip")->
  765 + where('url', '=', "$url")->update(['created_at' => date('Y-m-d H:i')]);
  766 + } else {
  767 + // в противном случае добавляем новый ip В бд
  768 + $page = new Page();
  769 + $page->ipaddress = $ip;
  770 + $page->url = $url;
  771 + $page->save();
  772 + }
  773 +
  774 + // выводим количество пользователей гостей данной страницы
  775 + $count_user = DB::table('pages')->where('url', '=', "$url")->get();
  776 + return view('house.post', compact('house', 'houses', 'count_user'));
742 777 }
743 778  
744 779 /*
... ... @@ -0,0 +1,11 @@
  1 +<?php
  2 +
  3 +namespace App\Models;
  4 +
  5 +use Illuminate\Database\Eloquent\Factories\HasFactory;
  6 +use Illuminate\Database\Eloquent\Model;
  7 +
  8 +class Page extends Model
  9 +{
  10 + use HasFactory;
  11 +}
database/migrations/2023_03_20_110427_create_pages_table.php
... ... @@ -0,0 +1,33 @@
  1 +<?php
  2 +
  3 +use Illuminate\Database\Migrations\Migration;
  4 +use Illuminate\Database\Schema\Blueprint;
  5 +use Illuminate\Support\Facades\Schema;
  6 +
  7 +return new class extends Migration
  8 +{
  9 + /**
  10 + * Run the migrations.
  11 + *
  12 + * @return void
  13 + */
  14 + public function up()
  15 + {
  16 + Schema::create('pages', function (Blueprint $table) {
  17 + $table->id();
  18 + $table->string('ipaddress', 100);
  19 + $table->string('url', 255);
  20 + $table->timestamps();
  21 + });
  22 + }
  23 +
  24 + /**
  25 + * Reverse the migrations.
  26 + *
  27 + * @return void
  28 + */
  29 + public function down()
  30 + {
  31 + Schema::dropIfExists('pages');
  32 + }
  33 +};
public/js/main_main.js
Changes suppressed. Click to show
... ... @@ -0,0 +1,1508 @@
  1 +// управляющий класс App с методом init(), в котором собраны все используемые методы с комментариями о том, что конкретно делает каждый метод
  2 +
  3 +class App {
  4 +
  5 + constructor() {
  6 + this.patternPhone = /^(\+7|7|8)?[\s\-]?\(?[489][0-9]{2}\)?[\s\-]?[0-9]{3}[\s\-]?[0-9]{2}[\s\-]?[0-9]{2}$/; // рег. выражение для поля 'телефон';
  7 + this.patternEmail = /^[a-zA-Z0-9._%+-\.]+@[a-z0-9.-]+\.[a-z]{2,}$/i; // рег. выражение для поля 'электронная почта';
  8 + }
  9 +
  10 + init() {
  11 +
  12 + console.log('init');
  13 +
  14 + this.stickyHeader(); // липкий хедер;
  15 + this.controlBurgerMenu(); // бургер-меню;
  16 + this.smoothScroll(); // плавный скролл к якорю (smooth scroll);
  17 + this.scrollUp(); // кнопка наверх;
  18 + this.addToFavorites(); // добавить в избранное (звёздочка);
  19 + this.initTypicalSlider(); // типовые слайдеры;
  20 + this.initPartnerslSlider(); // слайдер с партнёрами;
  21 + this.controlFilters(); // фильтры на главном экране;
  22 + this.controlPopups(); // открытие/закрытие поп-апов;
  23 + this.controlContactUsPopup(); // открытие/закрытие поп-апа 'обратный звонок';
  24 +
  25 + this.sendForm('.js_popup_feedback_form', '[data-popup="success"]'); // отправка формы в поп-апе обратной связи;
  26 + this.sendForm('.js_popup_viewing_form', '[data-popup="success"]'); // отправка формы в поп-апе 'записаться на просмотр';
  27 + this.sendForm('.js_footer_feedback_form', '[data-popup="success"]'); // отправка формы в футере;
  28 + this.sendForm('.js_contacts_form', '.js_contacts_success'); // отправка формы на странице контакты;
  29 + this.sendForm('.js_popup_sending_form_', '[data-popup="success"]');
  30 + //this.sendOffer(); //отправка предложения по e-mail;
  31 +
  32 + //this.setGeneralMap(); // карта на странице карт;
  33 + //this.setComplexMap('complex-map', [55.726591050908745, 37.57244549999999], 'ЖК Садовые кварталы'); // карта на странице 'ЖК';
  34 + //this.setComplexMap('offer-map', [55.70851106903402, 37.65864349999999], 'Аренда торгового помещения 321,6 м2'); // карта на странице 'Предложение';
  35 + this.setCatalogSorts(); // сортировка на странице 'каталог';
  36 + this.initIntroSlider(); // слайдер на странице жк и на странице предложения;
  37 + this.setTabs('.js_offer_side_tab', '.js_offer_side_item'); // табы с планами объекат и этажа на странице предложения;
  38 + this.setTabs('.js_offer_side_popup_tab', '.js_offer_side_popup_item'); // табы с планами объекат и этажа в поп-апе на странице предложения;
  39 + this.sontrolOfferSidePopup(); // логика открытия нужного таба при открытии поп-апа с планами объекат и этажа на странице предложения;
  40 + this.setCustomGallery(); // галлерея;
  41 + this.setCookies() // куки;
  42 + this.setFooterSpoilers() // аккордеон в футере;
  43 +
  44 + }
  45 +
  46 + // фиксация <body>
  47 + fixBodyPosition() {
  48 +
  49 + setTimeout(function () {
  50 + // ставим необходимую задержку, чтобы не было «конфликта» в случае, если функция фиксации вызывается сразу после расфиксации (расфиксация отменяет действия расфиксации из-за одновременного действия)
  51 +
  52 + if (!document.body.hasAttribute('data-body-scroll-fix')) {
  53 +
  54 + // получаем позицию прокрутки
  55 + let scrollPosition = window.pageYOffset || document.documentElement.scrollTop;
  56 +
  57 + // ставим нужные стили
  58 + document.body.setAttribute('data-body-scroll-fix', scrollPosition); // Cтавим атрибут со значением прокрутки
  59 + document.body.style.overflow = 'hidden';
  60 + document.body.style.position = 'fixed';
  61 + document.body.style.top = '-' + scrollPosition + 'px';
  62 + document.body.style.left = '0';
  63 + document.body.style.width = '100%';
  64 +
  65 + if (window.innerWidth >= 1200) {
  66 + document.body.style.paddingRight = '8px';
  67 + }
  68 + }
  69 +
  70 + }, 15); // можно задержку ещё меньше, но работает хорошо именно с этим значением на всех устройствах и браузерах
  71 +
  72 + }
  73 +
  74 +
  75 + // расфиксация <body>
  76 + unfixBodyPosition() {
  77 +
  78 + if (document.body.hasAttribute('data-body-scroll-fix')) {
  79 +
  80 + // получаем позицию прокрутки из атрибута
  81 + let scrollPosition = document.body.getAttribute('data-body-scroll-fix');
  82 +
  83 + // удаляем атрибут
  84 + document.body.removeAttribute('data-body-scroll-fix');
  85 +
  86 + // удаляем ненужные стили
  87 + document.body.style.overflow = '';
  88 + document.body.style.position = '';
  89 + document.body.style.top = '';
  90 + document.body.style.left = '';
  91 + document.body.style.width = '';
  92 + document.body.style.paddingRight = '';
  93 +
  94 + // прокручиваем страницу на полученное из атрибута значение
  95 + window.scroll(0, scrollPosition);
  96 +
  97 + }
  98 +
  99 + }
  100 +
  101 +
  102 + // бургер-меню
  103 + controlBurgerMenu() {
  104 +
  105 + const headerBurger = document.querySelector('.js_header_burger');
  106 +
  107 + if (headerBurger) {
  108 +
  109 + const menu = document.querySelector('.js_menu');
  110 + const menuClose = menu.querySelector('.js_menu_close');
  111 +
  112 + headerBurger.addEventListener('click', () => {
  113 + menu.classList.add('active');
  114 + this.fixBodyPosition();
  115 + });
  116 +
  117 + menu.addEventListener('click', (e) => {
  118 +
  119 + if (e.target == menu) {
  120 + menu.classList.remove('active');
  121 + this.unfixBodyPosition();
  122 + }
  123 +
  124 + });
  125 +
  126 + menuClose.addEventListener('click', () => {
  127 + menu.classList.remove('active');
  128 + this.unfixBodyPosition();
  129 + });
  130 +
  131 + }
  132 +
  133 + }
  134 +
  135 +
  136 + // липкий хедер
  137 + stickyHeader() {
  138 +
  139 + const header = document.querySelector('.js_header');
  140 +
  141 + if (header) {
  142 +
  143 + window.addEventListener('scroll', () => {
  144 +
  145 + if (window.scrollY > 200) {
  146 + header.classList.add('fixed');
  147 + } else {
  148 + header.classList.remove('fixed');
  149 + }
  150 +
  151 + });
  152 +
  153 + };
  154 +
  155 + }
  156 +
  157 +
  158 + // плавный скролл к якорю (smooth scroll)
  159 + smoothScroll() {
  160 +
  161 + const smoothLinks = document.querySelectorAll('.js_smooth_link');
  162 +
  163 + if (smoothLinks.length) {
  164 +
  165 + smoothLinks.forEach(link => {
  166 +
  167 + link.addEventListener('click', function (e) {
  168 +
  169 + e.preventDefault();
  170 +
  171 + let href = this.getAttribute('href').substring(1);
  172 +
  173 + const scrollTarget = document.getElementById(href);
  174 +
  175 + // const topOffset = document.querySelector('.header').offsetHeight;
  176 + const topOffset = 0; // если не нужен отступ сверху
  177 + const elementPosition = scrollTarget.getBoundingClientRect().top;
  178 + const offsetPosition = elementPosition - topOffset;
  179 +
  180 + window.scrollBy({
  181 + top: offsetPosition,
  182 + behavior: 'smooth'
  183 + });
  184 +
  185 + });
  186 +
  187 + });
  188 +
  189 + }
  190 +
  191 + }
  192 +
  193 +
  194 + // кнопка наверх
  195 + scrollUp() {
  196 +
  197 + const toTopBtn = document.querySelector('.js_btn_up');
  198 +
  199 + if (toTopBtn) {
  200 +
  201 + toTopBtn.addEventListener('click', function () {
  202 +
  203 + window.scrollTo({
  204 + top: 0,
  205 + behavior: 'smooth'
  206 + });
  207 +
  208 + });
  209 +
  210 + }
  211 +
  212 + }
  213 +
  214 +
  215 + // добавить в избранное (звёздочка)
  216 + addToFavorites() {
  217 +
  218 + const cardFavorites = document.querySelectorAll('.js_card_favorites');
  219 +
  220 + if (cardFavorites.length) {
  221 +
  222 + cardFavorites.forEach(item => {
  223 +
  224 + item.addEventListener('click', (e) => {
  225 + e.preventDefault();
  226 + item.classList.toggle('active');
  227 + });
  228 +
  229 + });
  230 +
  231 + }
  232 +
  233 + }
  234 +
  235 +
  236 + // типовые слайдеры
  237 + initTypicalSlider() {
  238 +
  239 + const slidersWraps = document.querySelectorAll('.slider__wrap');
  240 +
  241 + if (slidersWraps.length) {
  242 +
  243 + slidersWraps.forEach(wrap => {
  244 +
  245 + const slider = wrap.querySelector('.swiper');
  246 + const prev = wrap.querySelector('.swiper-button-prev');
  247 + const next = wrap.querySelector('.swiper-button-next');
  248 + const pagination = wrap.querySelector('.swiper-pagination');
  249 +
  250 + let swiper1 = new Swiper(slider, {
  251 + navigation: {
  252 + nextEl: next,
  253 + prevEl: prev,
  254 + },
  255 + pagination: {
  256 + el: pagination,
  257 + clickable: true,
  258 + },
  259 + slidesPerView: 1,
  260 + spaceBetween: 20,
  261 + observer: true,
  262 + observeParents: true,
  263 + observeSlideChildren: true,
  264 + breakpoints: {
  265 + 480: {
  266 + slidesPerView: 1.5,
  267 + },
  268 + 640: {
  269 + slidesPerView: 2,
  270 + },
  271 + 780: {
  272 + slidesPerView: 2.5,
  273 + },
  274 + 920: {
  275 + slidesPerView: 3,
  276 + },
  277 + 1024: {
  278 + slidesPerView: 3.4
  279 + },
  280 + 1200: {
  281 + slidesPerView: 4,
  282 + }
  283 + }
  284 + });
  285 +
  286 + });
  287 +
  288 + }
  289 +
  290 + }
  291 +
  292 +
  293 + // метод, делающий число удобночитаемым (добавляет пробел справа через каждые 3 цифры)
  294 + prettify(num) {
  295 + const withoutSpace = num.replace(/[^\d]/g, ''); //убирает все символы;
  296 + return withoutSpace.replace(/(?!^)(?=(?:\d{3})+(?:\.|$))/gm, ' '); //ставит пробелы;
  297 + }
  298 +
  299 +
  300 + // фильтры на главном экране
  301 + controlFilters() {
  302 +
  303 + const heroFilters = document.querySelectorAll('.js_hero_filter');
  304 + const heroSearchBtns = document.querySelectorAll('.js_hero_search_btn');
  305 +
  306 + if (heroFilters.length) {
  307 +
  308 + heroFilters.forEach(filter => {
  309 +
  310 + const heroFilterInput = filter.querySelector('.js_hero_filter_input');
  311 + const heroFilterCurrent = filter.querySelector('.js_hero_filter_current');
  312 + const heroFilterItems = filter.querySelectorAll('.hero-filter__item');
  313 + const heroFilterFields = filter.querySelectorAll('.js_hero_filter_field');
  314 + const heroFilterFrom = filter.querySelector('.js_hero_filter_from');
  315 + const heroFilterTo = filter.querySelector('.js_hero_filter_to');
  316 + const heroFilterReset = filter.querySelector('.js_hero_filter_reset');
  317 +
  318 + heroFilterCurrent.addEventListener('click', () => {
  319 +
  320 + if (filter.classList.contains('active')) {
  321 +
  322 + filter.classList.remove('active');
  323 +
  324 + heroSearchBtns.forEach(btn => {
  325 + btn.disabled = false;
  326 + });
  327 +
  328 + } else {
  329 +
  330 + heroFilters.forEach(filter => {
  331 + filter.classList.remove('active');
  332 + });
  333 +
  334 + filter.classList.add('active');
  335 +
  336 + heroSearchBtns.forEach(btn => {
  337 + btn.disabled = true;
  338 + });
  339 +
  340 + }
  341 +
  342 + });
  343 +
  344 + if (heroFilterItems.length) {
  345 +
  346 + heroFilterItems.forEach(item => {
  347 +
  348 + item.addEventListener('click', () => {
  349 +
  350 + heroFilterCurrent.textContent = item.textContent;
  351 + heroFilterInput.value = item.dataset.val;
  352 + filter.classList.remove('active');
  353 +
  354 + heroSearchBtns.forEach(btn => {
  355 + btn.disabled = false;
  356 + });
  357 +
  358 + });
  359 +
  360 + });
  361 +
  362 + }
  363 +
  364 + if (heroFilterFields.length) {
  365 +
  366 + const heroFilterMin = heroFilterFrom.dataset.min;
  367 + const heroFilterMax = heroFilterTo.dataset.max;
  368 +
  369 + let heroFilterFromVal;
  370 + let heroFilterToVal;
  371 +
  372 + heroFilterFields.forEach(field => {
  373 +
  374 + field.addEventListener('input', () => {
  375 +
  376 + field.value = this.prettify(field.value);
  377 +
  378 + heroFilterReset.classList.remove('active');
  379 +
  380 + heroFilterFields.forEach(field => {
  381 +
  382 + if (field.value != "") {
  383 + heroFilterReset.classList.add('active');
  384 + }
  385 +
  386 + });
  387 +
  388 + });
  389 +
  390 + });
  391 +
  392 + heroFilterFrom.addEventListener('change', () => {
  393 +
  394 + heroFilterFromVal = +heroFilterFrom.value.replace(/\s/g, '');
  395 + heroFilterToVal = +heroFilterTo.value.replace(/\s/g, '');
  396 +
  397 + if (heroFilterToVal != '' && heroFilterFromVal > heroFilterToVal) {
  398 +
  399 + heroFilterFrom.value = heroFilterTo.value;
  400 +
  401 + } else if (heroFilterFromVal < +heroFilterMin) {
  402 +
  403 + heroFilterFrom.value = this.prettify(heroFilterMin);
  404 +
  405 + } else if (heroFilterFromVal > +heroFilterMax) {
  406 +
  407 + heroFilterFrom.value = this.prettify(heroFilterMax);
  408 +
  409 + }
  410 +
  411 + });
  412 +
  413 + heroFilterTo.addEventListener('change', () => {
  414 +
  415 + heroFilterFromVal = +heroFilterFrom.value.replace(/\s/g, '');
  416 + heroFilterToVal = +heroFilterTo.value.replace(/\s/g, '');
  417 +
  418 + if (heroFilterFromVal != '' && heroFilterToVal < heroFilterFromVal) {
  419 +
  420 + heroFilterTo.value = heroFilterFrom.value;
  421 +
  422 + } else if (heroFilterToVal < +heroFilterMin) {
  423 +
  424 + heroFilterTo.value = this.prettify(heroFilterMax);
  425 +
  426 + } else if (heroFilterToVal > +heroFilterMax) {
  427 +
  428 + heroFilterTo.value = this.prettify(heroFilterMax);
  429 +
  430 + }
  431 +
  432 + });
  433 +
  434 + heroFilterReset.addEventListener('click', () => {
  435 +
  436 + heroFilterFields.forEach(field => {
  437 + field.value = '';
  438 + });
  439 +
  440 + heroFilterReset.classList.remove('active');
  441 +
  442 + });
  443 + }
  444 +
  445 + });
  446 +
  447 + document.addEventListener('click', (e) => {
  448 +
  449 + if (!e.target.closest('.js_hero_filter_dropdown') && !e.target.closest('.js_hero_filter_current')) {
  450 +
  451 + heroFilters.forEach(filter => {
  452 + filter.classList.remove('active');
  453 + });
  454 +
  455 + heroSearchBtns.forEach(btn => {
  456 + btn.disabled = false;
  457 + });
  458 +
  459 + }
  460 +
  461 + });
  462 +
  463 + }
  464 +
  465 + }
  466 +
  467 +
  468 + // открытие/закрытие типовых поп-апов
  469 + controlPopups() {
  470 +
  471 + const popupShowBtns = document.querySelectorAll('[data-btn]');
  472 + const popups = document.querySelectorAll('[data-popup]');
  473 +
  474 + if (popupShowBtns.length) {
  475 +
  476 + popupShowBtns.forEach(btn => {
  477 +
  478 + btn.addEventListener('click', (e) => {
  479 +
  480 + e.preventDefault();
  481 +
  482 + popups.forEach(popup => {
  483 +
  484 + popup.classList.remove('active'); // если какойто поп-ап открыт, то закрываем его;
  485 + this.unfixBodyPosition();
  486 +
  487 + if (btn.dataset.btn == popup.dataset.popup) {
  488 + popup.classList.add('active');
  489 + this.fixBodyPosition();
  490 + }
  491 +
  492 + });
  493 +
  494 +
  495 + });
  496 +
  497 + });
  498 +
  499 + popups.forEach(popup => {
  500 +
  501 + const popupCloseBtns = popup.querySelectorAll('.js_popup_close');
  502 +
  503 + popupCloseBtns.forEach(btn => {
  504 +
  505 + btn.addEventListener('click', (e) => {
  506 + e.preventDefault();
  507 + popup.classList.remove('active');
  508 + this.unfixBodyPosition();
  509 + });
  510 +
  511 + });
  512 +
  513 + popup.addEventListener('click', (e) => {
  514 +
  515 + if (e.target == popup) {
  516 +
  517 + popup.classList.remove('active');
  518 + this.unfixBodyPosition();
  519 + }
  520 +
  521 + });
  522 +
  523 + });
  524 +
  525 + }
  526 + }
  527 +
  528 +
  529 + // открытие/закрытие поп-апа 'обратный звонок'
  530 + controlContactUsPopup() {
  531 +
  532 + const contactUsBtn = document.querySelector('.js_btn_contact_us');
  533 + const contactUsPopup = document.querySelector('.js_contact_us');
  534 +
  535 + if (contactUsPopup) {
  536 +
  537 + const contactUsPopupCloseBtns = contactUsPopup.querySelectorAll('.js_contact_us_close');
  538 +
  539 + contactUsBtn.addEventListener('click', (e) => {
  540 +
  541 + e.preventDefault();
  542 +
  543 + if (contactUsPopup.classList.contains('active')) {
  544 + contactUsPopup.classList.remove('active');
  545 + } else {
  546 + contactUsPopup.classList.add('active');
  547 + }
  548 +
  549 + });
  550 +
  551 + contactUsPopupCloseBtns.forEach(btn => {
  552 + btn.addEventListener('click', () => {
  553 + contactUsPopup.classList.remove('active');
  554 + });
  555 + });
  556 +
  557 +
  558 + document.addEventListener('click', (e) => {
  559 +
  560 + if (!e.target.closest('.js_contact_us') && !e.target.closest('.js_btn_contact_us')) {
  561 + contactUsPopup.classList.remove('active');
  562 + }
  563 +
  564 + });
  565 +
  566 + }
  567 +
  568 + }
  569 +
  570 +
  571 + // валидатор форм
  572 + validateForm(input) {
  573 +
  574 + // функция добавления ошибки
  575 + const createError = (text) => {
  576 +
  577 + input.classList.add('error');
  578 + input.classList.remove('no-error');
  579 +
  580 + if (input.closest('label').querySelector('span.error')) {
  581 + input.closest('label').querySelector('span.error').remove();
  582 + input.closest('label').insertAdjacentHTML('beforeend', `<span class="error">${text}</span>`);
  583 + } else {
  584 + input.closest('label').insertAdjacentHTML('beforeend', `<span class="error">${text}</span>`);
  585 + }
  586 +
  587 + }
  588 +
  589 + // функция удаления ошибки
  590 + const removeError = () => {
  591 +
  592 + input.classList.remove('error');
  593 + input.classList.add('no-error');
  594 +
  595 + if (input.closest('label').querySelector('span.error')) {
  596 + input.closest('label').querySelector('span.error').remove();
  597 + }
  598 +
  599 + }
  600 +
  601 + // проверяем на правильность заполнения поля 'Телефон'
  602 + if (input.classList.contains('js_input_phone') && input.value == "") {
  603 + createError('Заполните, пожалуйста, поле');
  604 + } else if (input.classList.contains('js_input_phone') && input.value.search(this.patternPhone) == 0) {
  605 + removeError();
  606 + } else if (input.classList.contains('js_input_phone')) {
  607 + createError('Укажите, пожалуйста, корректный телефон');
  608 + }
  609 +
  610 + // проверяем правильность заполнения поля 'Электронная почта'
  611 + if (input.classList.contains('js_input_email') && input.value == "") {
  612 + createError('Заполните, пожалуйста, поле');
  613 + } else if (input.classList.contains('js_input_email') && input.value.search(this.patternEmail) == 0) {
  614 + removeError();
  615 + } else if (input.classList.contains('js_input_email')) {
  616 + createError('Укажите, пожалуйста, корректный e-mail');
  617 + }
  618 +
  619 + }
  620 +
  621 +
  622 + // отправка форм
  623 + sendForm(formEl, success) {
  624 +
  625 + const form = document.querySelector(formEl);
  626 +
  627 + if (form) {
  628 +
  629 + form.addEventListener('submit', async (e) => {
  630 +
  631 + e.preventDefault();
  632 +
  633 + const formInputs = form.querySelectorAll('input');
  634 + const formBtn = form.querySelector('.js_form_btn');
  635 +
  636 + formInputs.forEach(input => { // перебираем все инпуты в форме;
  637 +
  638 + this.validateForm(input);
  639 +
  640 + input.addEventListener('input', () => {
  641 + this.validateForm(input);
  642 + });
  643 +
  644 + });
  645 +
  646 + if (!form.querySelector('.error')) { //проверяем, чтоб все инпуты прошли валидацию (чтоб не было в форме ни одного элемента с класссом error);
  647 +
  648 + // сюда пишем команды, которые должны сработать после успешной валидации;
  649 +
  650 + console.log('validate');
  651 + formBtn.classList.add('btn-animate');
  652 + formBtn.disabled = true;
  653 +
  654 + const formData = new FormData(form);
  655 +
  656 + console.log(...formData);
  657 +
  658 + const response = await fetch(e.target.action, {
  659 + method: e.target.method,
  660 + body: formData
  661 + });
  662 +
  663 + if (response.ok) {
  664 +
  665 + setTimeout(() => { // имитация отправки, когда отправка будет настроена, нужно достать всё из setTimeout() и удалить его;
  666 +
  667 + console.log('Отправлено');
  668 + formBtn.classList.remove('btn-animate');
  669 + formBtn.disabled = false;
  670 + if (document.querySelector('[data-popup="feedback"]')) {
  671 + document.querySelector('[data-popup="feedback"]').classList.remove('active');
  672 + }
  673 + if (document.querySelector('[data-popup="viewing"]')) {
  674 + document.querySelector('[data-popup="viewing"]').classList.remove('active');
  675 + }
  676 + document.querySelector(success).classList.add('active');
  677 + this.fixBodyPosition();
  678 + form.reset();
  679 +
  680 + formInputs.forEach(input => {
  681 + input.classList.remove('no-error');
  682 + });
  683 +
  684 + }, 2000)
  685 +
  686 + } else {
  687 + formBtn.classList.remove('btn-animate');
  688 + formBtn.disabled = false;
  689 + alert('Ошибка');
  690 + }
  691 +
  692 + } else {
  693 + console.log('no-validate');
  694 + form.querySelector('.error').focus(); //фокус к полю с ошибкой;
  695 + }
  696 +
  697 + });
  698 +
  699 + }
  700 +
  701 + }
  702 +
  703 + //отправка предложения по e-mail
  704 + sendOffer() {
  705 +
  706 + const form = document.querySelector('.js_popup_sending_form');
  707 +
  708 + if (form) {
  709 +
  710 + form.addEventListener('submit', async (e) => {
  711 +
  712 + e.preventDefault();
  713 +
  714 + const formInputs = form.querySelectorAll('input');
  715 + const formBtn = form.querySelector('.js_form_btn');
  716 +
  717 + formInputs.forEach(input => { // перебираем все инпуты в форме;
  718 +
  719 + this.validateForm(input);
  720 +
  721 + input.addEventListener('input', () => {
  722 + this.validateForm(input);
  723 + });
  724 +
  725 + });
  726 +
  727 + if (!form.querySelector('.error')) { //проверяем, чтоб все инпуты прошли валидацию (чтоб не было в форме ни одного элемента с класссом error);
  728 +
  729 + // сюда пишем команды, которые должны сработать после успешной валидации;
  730 +
  731 + console.log('validate');
  732 + formBtn.classList.add('btn-animate');
  733 + formBtn.disabled = true;
  734 +
  735 + const formData = new FormData(form);
  736 +
  737 + console.log(...formData);
  738 +
  739 + const response = await fetch(e.target.action, {
  740 + method: e.target.method,
  741 + body: formData
  742 + });
  743 +
  744 + if (response.ok) {
  745 +
  746 + setTimeout(() => { // имитация отправки, когда отправка будет настроена, нужно достать всё из setTimeout() и удалить его;
  747 +
  748 + console.log('Отправлено');
  749 + formBtn.classList.remove('btn-animate');
  750 + formBtn.disabled = false;
  751 + if (document.querySelector('[data-popup="sending"]')) {
  752 + document.querySelector('[data-popup="sending"]').classList.remove('active');
  753 + }
  754 + this.fixBodyPosition();
  755 + form.reset();
  756 +
  757 + formInputs.forEach(input => {
  758 + input.classList.remove('no-error');
  759 + });
  760 +
  761 + }, 2000)
  762 +
  763 + } else {
  764 + formBtn.classList.remove('btn-animate');
  765 + formBtn.disabled = false;
  766 + alert('Ошибка');
  767 + }
  768 +
  769 + } else {
  770 + console.log('no-validate');
  771 + form.querySelector('.error').focus(); //фокус к полю с ошибкой;
  772 + }
  773 +
  774 + });
  775 +
  776 + }
  777 +
  778 + }
  779 +
  780 +
  781 + // карта на странице 'ЖК'
  782 +/* setComplexMap(id, coords, caption) {
  783 +
  784 + if (document.querySelector('#' + id)) {
  785 +
  786 + // Дождёмся загрузки API и готовности DOM.
  787 + ymaps.ready(init);
  788 +
  789 + function init() {
  790 + const map = new ymaps.Map(id, {
  791 + // При инициализации карты обязательно нужно указать её центр и коэффициент масштабирования.
  792 + center: coords,
  793 + zoom: 16,
  794 + controls: []
  795 + });
  796 +
  797 + // Создаём макет содержимого.
  798 + const MyIconContentLayout = ymaps.templateLayoutFactory.createClass(
  799 + '<div style="color: #FFFFFF; font-weight: bold;">$[properties.iconContent]</div>'
  800 + );
  801 +
  802 + // Создание макета содержимого хинта.
  803 + // Макет создается через фабрику макетов с помощью текстового шаблона.
  804 + const HintLayout = ymaps.templateLayoutFactory.createClass("<div class='my-hint'>" +
  805 + "{{ properties.object }}" + "</div>", {
  806 + // Определяем метод getShape, который
  807 + // будет возвращать размеры макета хинта.
  808 + // Это необходимо для того, чтобы хинт автоматически
  809 + // сдвигал позицию при выходе за пределы карты.
  810 + getShape: function () {
  811 + let el = this.getElement(),
  812 + result = null;
  813 + if (el) {
  814 + var firstChild = el.firstChild;
  815 + result = new ymaps.shape.Rectangle(
  816 + new ymaps.geometry.pixel.Rectangle([
  817 + [0, 0],
  818 + [firstChild.offsetWidth, firstChild.offsetHeight]
  819 + ])
  820 + );
  821 + }
  822 + return result;
  823 + }
  824 + }
  825 + );
  826 +
  827 + // метка
  828 + const placemark = new ymaps.Placemark(coords, {
  829 + // hintContent: caption,
  830 + // balloonContent: caption,
  831 + iconContent: '1',
  832 + // address: caption,
  833 + object: caption
  834 + }, {
  835 + iconLayout: 'default#imageWithContent',
  836 + iconImageHref: 'images/mark-complex.svg',
  837 + iconImageSize: [52, 67],
  838 + iconImageOffset: [-26, -67],
  839 + iconContentOffset: [0, 17],
  840 + iconContentLayout: MyIconContentLayout,
  841 + hintLayout: HintLayout
  842 + });
  843 +
  844 + map.geoObjects.add(placemark);
  845 +
  846 + }
  847 +
  848 + }
  849 +
  850 + }
  851 +*/
  852 +
  853 + // фильтры и сортировка на странице 'каталог'
  854 + setCatalogSorts() {
  855 +
  856 + const sortGroups = document.querySelectorAll('.js_sort_group');
  857 +
  858 + if (sortGroups.length) {
  859 +
  860 + sortGroups.forEach(group => {
  861 +
  862 + const sortGroupInput = group.querySelector('.js_sort_group_input');
  863 + const sortGroupCurrent = group.querySelector('.js_sort_group_current');
  864 + const sortGroupList = group.querySelector('.js_sort_group_list');
  865 + const sortGroupItems = group.querySelectorAll('.js_sort_group_item');
  866 +
  867 + const sendRequest = () => {
  868 +
  869 + const spinner = document.querySelector('.spinner'); // спиннер;
  870 +
  871 + spinner.classList.add('active');
  872 + document.body.classList.add('overlay');
  873 + /*this.fixBodyPosition();
  874 +
  875 + fetch('test.json')
  876 + .then(response => response.json())
  877 + .then(data => {
  878 +
  879 + console.log()
  880 +
  881 + setTimeout(() => { //имитация ответа сервера
  882 +
  883 + spinner.classList.remove('active');
  884 + document.body.classList.remove('overlay');
  885 + this.unfixBodyPosition();
  886 +
  887 + }, 3000);
  888 +
  889 + })
  890 + .catch(err => {
  891 + console.log(err);
  892 + });
  893 +
  894 + */
  895 + spinner.classList.remove('active');
  896 + document.body.classList.remove('overlay');
  897 +
  898 +
  899 + };
  900 +
  901 + sortGroupCurrent.addEventListener('click', () => {
  902 +
  903 + if (group.classList.contains('active')) {
  904 +
  905 + group.classList.remove('active');
  906 +
  907 + } else {
  908 +
  909 + sortGroups.forEach(group => {
  910 + group.classList.remove('active');
  911 + });
  912 +
  913 + group.classList.add('active');
  914 +
  915 + }
  916 +
  917 + });
  918 +
  919 + sortGroupItems.forEach(item => {
  920 +
  921 + item.addEventListener('click', () => {
  922 +
  923 + sortGroupItems.forEach(item => {
  924 + item.classList.remove('active');
  925 + });
  926 +
  927 + item.classList.add('active');
  928 + sortGroupCurrent.textContent = item.textContent;
  929 + sortGroupInput.value = item.dataset.val;
  930 + group.classList.remove('active');
  931 +
  932 + sendRequest();
  933 +
  934 + });
  935 +
  936 + });
  937 +
  938 + });
  939 +
  940 + document.addEventListener('click', (e) => {
  941 +
  942 + if (!e.target.closest('.js_sort_group_list') && !e.target.closest('.js_sort_group_current')) {
  943 +
  944 + sortGroups.forEach(group => {
  945 + group.classList.remove('active');
  946 + });
  947 +
  948 + }
  949 +
  950 + });
  951 +
  952 + }
  953 +
  954 + }
  955 +
  956 +
  957 + // слайдер на странице жк и на странице предложения
  958 + initIntroSlider() {
  959 +
  960 + let swiper3 = new Swiper('.intro__swiper', {
  961 + navigation: {
  962 + nextEl: '.swiper-button-next',
  963 + prevEl: '.swiper-button-prev',
  964 + },
  965 + pagination: {
  966 + el: '.swiper-pagination',
  967 + clickable: true,
  968 + },
  969 + slidesPerView: 1.1,
  970 + spaceBetween: 20,
  971 + breakpoints: {
  972 + 480: {
  973 + slidesPerView: 1.5,
  974 + },
  975 + 640: {
  976 + slidesPerView: 1.75,
  977 + },
  978 + 780: {
  979 + slidesPerView: 2.15,
  980 + },
  981 + 1024: {
  982 + slidesPerView: 3.5,
  983 + },
  984 + 1200: {
  985 + slidesPerView: 1,
  986 + }
  987 + }
  988 + });
  989 +
  990 + }
  991 +
  992 +
  993 + // табы на странице предложения
  994 + setTabs(tabs, items) {
  995 +
  996 + const offerSideTabs = document.querySelectorAll(tabs);
  997 + const offerSideItems = document.querySelectorAll(items);
  998 +
  999 + if (offerSideTabs) {
  1000 +
  1001 + offerSideTabs.forEach(tab => {
  1002 +
  1003 + tab.addEventListener('click', () => {
  1004 +
  1005 + offerSideTabs.forEach(tab => {
  1006 + tab.classList.remove('active');
  1007 + });
  1008 +
  1009 + tab.classList.add('active');
  1010 +
  1011 + offerSideItems.forEach(item => {
  1012 +
  1013 + item.classList.remove('active', 'fade');
  1014 +
  1015 + if (tab.dataset.tab == item.dataset.item) {
  1016 + item.classList.add('active', 'fade');
  1017 + }
  1018 +
  1019 + });
  1020 +
  1021 + });
  1022 +
  1023 + });
  1024 +
  1025 + }
  1026 +
  1027 + }
  1028 +
  1029 +
  1030 + // логика открытия нужного таба при открытии поп-апа с планами объекат и этажа на странице предложения
  1031 + sontrolOfferSidePopup() {
  1032 +
  1033 + const offerSideItems = document.querySelectorAll('.js_offer_side_item');
  1034 + const offerSidePopupTabs = document.querySelectorAll('.js_offer_side_popup_tab');
  1035 + const offerSidePopupItems = document.querySelectorAll('.js_offer_side_popup_item');
  1036 +
  1037 + if (offerSideItems) {
  1038 +
  1039 + offerSideItems.forEach(item => {
  1040 +
  1041 + const offerSideItemBtn = item.querySelector('.js_offer_side_item_btn');
  1042 +
  1043 + offerSideItemBtn.addEventListener('click', (e) => {
  1044 +
  1045 + e.preventDefault();
  1046 +
  1047 + offerSidePopupTabs.forEach(tab => {
  1048 +
  1049 + tab.classList.remove('active');
  1050 +
  1051 + if (item.dataset.item == tab.dataset.tab) {
  1052 + tab.classList.add('active');
  1053 + }
  1054 +
  1055 + });
  1056 +
  1057 + offerSidePopupItems.forEach(el => {
  1058 +
  1059 + el.classList.remove('active', 'fade');
  1060 +
  1061 + if (item.dataset.item == el.dataset.item) {
  1062 + el.classList.add('active', 'fade');
  1063 + }
  1064 +
  1065 + });
  1066 +
  1067 + });
  1068 +
  1069 + });
  1070 +
  1071 + }
  1072 +
  1073 + }
  1074 +
  1075 +
  1076 + // галлерея
  1077 + setCustomGallery() {
  1078 +
  1079 + let swiper4 = new Swiper(".img-viewer__thumbs-swiper", {
  1080 + slidesPerView: 3,
  1081 + spaceBetween: 8,
  1082 + // freeMode: true,
  1083 + observer: true,
  1084 + observeParents: true,
  1085 + observeSlideChildren: true,
  1086 + breakpoints: {
  1087 + 640: {
  1088 + spaceBetween: 10,
  1089 + },
  1090 + },
  1091 + });
  1092 +
  1093 + let swiper5 = new Swiper(".img-viewer__slider .swiper", {
  1094 + navigation: {
  1095 + nextEl: ".img-viewer__slider .swiper-button-next",
  1096 + prevEl: ".img-viewer__slider .swiper-button-prev",
  1097 + },
  1098 + slidesPerView: 1,
  1099 + spaceBetween: 20,
  1100 + thumbs: {
  1101 + swiper: swiper4
  1102 + },
  1103 + observer: true,
  1104 + observeParents: true,
  1105 + observeSlideChildren: true,
  1106 + });
  1107 +
  1108 + if (document.querySelector('.js_intro_item_btn')) {
  1109 +
  1110 + const imgViewer = document.querySelector('.js_img_viewer');
  1111 + const imgViewerCloses = imgViewer.querySelectorAll('.js_img_viewer_close');
  1112 + const imgViewerCaption = imgViewer.querySelector('.js_img_viewer_caption');
  1113 +
  1114 + const imgViewerSliderSwiper = imgViewer.querySelector('.js_img_viewer_slider_swiper');
  1115 + const imgViewerSliderSwiperWrap = imgViewerSliderSwiper.querySelector('.js_img_viewer_slider_swiper .swiper-wrapper');
  1116 +
  1117 + const imgViewerThumbsSwiper = imgViewer.querySelector('.js_img_viewer_thumbs_swiper');
  1118 + const imgViewerThumbsSwiperWrap = imgViewerThumbsSwiper.querySelector('.js_img_viewer_thumbs_swiper .swiper-wrapper');
  1119 +
  1120 + const introItemBtns = document.querySelectorAll('.js_intro_item_btn');
  1121 +
  1122 + introItemBtns.forEach((btn, i) => {
  1123 +
  1124 + btn.addEventListener('click', (e) => {
  1125 +
  1126 + e.preventDefault();
  1127 +
  1128 + imgViewer.classList.add('active');
  1129 + this.fixBodyPosition();
  1130 +
  1131 + imgViewerSliderSwiperWrap.innerHTML = '';
  1132 + imgViewerThumbsSwiperWrap.innerHTML = '';
  1133 + imgViewerCaption.textContent = '';
  1134 +
  1135 +
  1136 + introItemBtns.forEach(btn => {
  1137 +
  1138 + imgViewerSliderSwiperWrap.insertAdjacentHTML('beforeend', `
  1139 + <div class="swiper-slide">
  1140 + <img src="${btn.getAttribute('href')}" alt="">
  1141 + </div>`
  1142 + );
  1143 +
  1144 + imgViewerThumbsSwiperWrap.insertAdjacentHTML('beforeend', `
  1145 + <div class="swiper-slide">
  1146 + <img src="${btn.getAttribute('href')}" alt="">
  1147 + </div>`
  1148 + );
  1149 +
  1150 + });
  1151 +
  1152 + swiper4.update();
  1153 + swiper5.update();
  1154 + swiper5.slideTo(i);
  1155 + imgViewerCaption.textContent = btn.dataset.caption;
  1156 +
  1157 + });
  1158 +
  1159 + });
  1160 +
  1161 + swiper5.on('slideChange', function () {
  1162 + imgViewerCaption.textContent = introItemBtns[swiper5.realIndex].dataset.caption;
  1163 + });
  1164 +
  1165 + imgViewerCloses.forEach(close => {
  1166 +
  1167 + close.addEventListener('click', () => {
  1168 +
  1169 + imgViewer.classList.remove('active');
  1170 + this.unfixBodyPosition();
  1171 +
  1172 + });
  1173 +
  1174 + });
  1175 +
  1176 + }
  1177 +
  1178 + }
  1179 +
  1180 +
  1181 + // куки
  1182 + setCookies() {
  1183 +
  1184 + const cookies = document.querySelector('.js_cookies');
  1185 + const cookiesBtn = document.querySelector('.js_cookies_confirm');
  1186 + const cookiesTrigger = document.querySelector('.js_btn_cookies');
  1187 +
  1188 + if (cookiesTrigger) {
  1189 +
  1190 + cookiesTrigger.addEventListener('click', () => {
  1191 + cookies.classList.add('active');
  1192 + });
  1193 +
  1194 + };
  1195 +
  1196 + if (cookies) {
  1197 +
  1198 + cookiesBtn.addEventListener('click', () => {
  1199 + cookies.classList.remove('active');
  1200 + });
  1201 +
  1202 + };
  1203 +
  1204 + }
  1205 +
  1206 +
  1207 + // карта на странице карт;
  1208 + /*
  1209 + //setGeneralMap() {
  1210 +
  1211 + if (document.querySelector('#general-map')) {
  1212 +
  1213 + ymaps.ready(init); // Дождёмся загрузки API и готовности DOM;
  1214 +
  1215 + function init() {
  1216 +
  1217 + const myMap = new ymaps.Map('general-map', { // Создание экземпляра карты и его привязка к контейнеру с заданным id;
  1218 + center: [55.752933963675126, 37.52233749962665], // При инициализации карты обязательно нужно указать её центр и коэффициент масштабирования;
  1219 + zoom: 10,
  1220 + controls: [] // Скрываем элементы управления на карте;
  1221 + });
  1222 +
  1223 + // Создаём макет содержимого.
  1224 + const MyIconContentLayout = ymaps.templateLayoutFactory.createClass(
  1225 + '<div style="color: #FFFFFF; font-weight: bold;">$[properties.iconContent]</div>'
  1226 + );
  1227 +
  1228 + let collection = new ymaps.GeoObjectCollection(null, { // Создаём коллекцию, в которую будемпомещать метки (что-то типа массива);
  1229 + // preset: 'islands#yellowIcon'
  1230 + });
  1231 +
  1232 + let collectionCoords = [ // Создаём массив с координатами (координаты должны располагаться в том же порядке, что и адреса в списке на сайте);
  1233 + [55.867783219108354, 37.392867499999916],
  1234 + [55.728043075486504, 37.73937949999994],
  1235 + [55.72624100423305, 37.476078499999964],
  1236 + [55.80751105044832, 37.449622999999974],
  1237 + [55.601783098948836, 37.36700499999998],
  1238 + [55.86086502152225, 37.540348999999964],
  1239 + [55.784961528728715, 37.56188599999996],
  1240 + [55.63910010399773, 37.319407999999996],
  1241 + [55.55819256767507, 37.55711549999994],
  1242 + [55.79829252928473, 37.52063549999999],
  1243 + ];
  1244 +
  1245 + for (let i = 0, l = collectionCoords.length; i < l; i++) { // C помощью цикла добавляем все метки в коллекцию;
  1246 + collection.add(new ymaps.Placemark(collectionCoords[i]));
  1247 + collection.get(i).properties.set('iconContent', `${i + 1}`); // Добавляем каждой метке порядковый номер, записываем его в свойство 'iconContent';
  1248 + }
  1249 +
  1250 + myMap.geoObjects.add(collection); // Добавляем коллекцию с метками на карту;
  1251 +
  1252 + collection.options.set('iconLayout', 'default#imageWithContent'); // Необходимо указать данный тип макета;
  1253 + collection.options.set('iconImageHref', 'images/mark-complex.svg'); // Своё изображение иконки метки;
  1254 + collection.options.set('iconImageSize', [52, 67]); // Размеры метки;
  1255 + collection.options.set('iconImageOffset', [-26, -67]); // Смещение левого верхнего угла иконки относительно её "ножки" (точки привязки);
  1256 + collection.options.set('iconContentOffset', [0, 17]);
  1257 + collection.options.set('iconContentLayout', MyIconContentLayout); // Смещение левого верхнего угла иконки относительно её "ножки" (точки привязки);
  1258 +
  1259 + const pageMapBar = document.querySelector('.js_page_map_bar');
  1260 + const pageMapBarBtn = pageMapBar.querySelector('.js_page_map_bar_btn');
  1261 + const pageMapBarList = pageMapBar.querySelector('.js_page_map_bar_list');
  1262 + const pageMapBarCards = pageMapBar.querySelectorAll('.card-news');
  1263 +
  1264 + const showCard = (i) => {
  1265 +
  1266 + pageMapBarCards.forEach((card, k) => {
  1267 +
  1268 + card.classList.remove('active');
  1269 +
  1270 + if (i == k) {
  1271 + card.classList.add('active');
  1272 + }
  1273 +
  1274 + });
  1275 +
  1276 + };
  1277 +
  1278 + const hidecard = () => {
  1279 +
  1280 + pageMapBarCards.forEach(card => {
  1281 + card.classList.remove('active');
  1282 + });
  1283 +
  1284 + }
  1285 +
  1286 + let pageMapBarItems;
  1287 +
  1288 + pageMapBarBtn.addEventListener('click', () => {
  1289 + pageMapBar.classList.toggle('active');
  1290 + });
  1291 +
  1292 + pageMapBarList.addEventListener('click', (e) => {
  1293 +
  1294 + if (e.target.closest('.page-map-bar__item')) {
  1295 +
  1296 + pageMapBarItems = pageMapBarList.querySelectorAll('.page-map-bar__item');
  1297 +
  1298 + pageMapBarItems.forEach((item, i) => {
  1299 +
  1300 + if (e.target == item && e.target.classList.contains('active')) {
  1301 +
  1302 + item.classList.remove('active');
  1303 +
  1304 + hidecard();
  1305 +
  1306 + } else if (e.target == item) {
  1307 +
  1308 + pageMapBarItems.forEach(item => {
  1309 + item.classList.remove('active');
  1310 + });
  1311 +
  1312 + item.classList.add('active');
  1313 +
  1314 + let offsetCoords = collection.get(i).geometry.getCoordinates();
  1315 +
  1316 + offsetCoords = [
  1317 + offsetCoords[0] - 0.0025,
  1318 + offsetCoords[1]
  1319 + ];
  1320 +
  1321 + myMap.setZoom(16);
  1322 + // myMap.setCenter(collection.get(i).geometry.getCoordinates());
  1323 + myMap.setCenter(offsetCoords);
  1324 +
  1325 + showCard(i);
  1326 +
  1327 + }
  1328 +
  1329 + });
  1330 + }
  1331 +
  1332 + });
  1333 +
  1334 + collection.events.add('click', function (e) {
  1335 +
  1336 + for (let i = 0, l = collection.getLength(); i < l; i++) {
  1337 +
  1338 + if (e.get('target') == collection.get(i)) {
  1339 +
  1340 + pageMapBarItems = pageMapBarList.querySelectorAll('.page-map-bar__item');
  1341 +
  1342 + pageMapBarItems.forEach((item) => {
  1343 + pageMapBar.classList.add('active');
  1344 + item.classList.remove('active');
  1345 + });
  1346 +
  1347 + pageMapBarItems[i].classList.add('active');
  1348 +
  1349 + showCard(i);
  1350 +
  1351 + }
  1352 +
  1353 + }
  1354 +
  1355 + });
  1356 +
  1357 + }
  1358 +
  1359 + }
  1360 +
  1361 + };*/
  1362 +
  1363 +
  1364 + // аккордеон в футере
  1365 + setFooterSpoilers() {
  1366 +
  1367 + const items = document.querySelectorAll('.js_footer_col');
  1368 +
  1369 + items.forEach(item => {
  1370 +
  1371 + const itemTitle = item.querySelector('.js_footer_caption');
  1372 + const itemContent = item.querySelector('.js_footer_block');
  1373 +
  1374 + const blockToggle = (block, duration) => {
  1375 +
  1376 + if (window.getComputedStyle(block).display == "none" && !block.classList.contains('smooth')) {
  1377 +
  1378 + block.style.display = "block";
  1379 +
  1380 + const blockHeight = block.offsetHeight;
  1381 +
  1382 + block.style.height = 0;
  1383 + block.style.overflow = "hidden";
  1384 + block.style.transition = `height ${duration}ms ease`;
  1385 + block.classList.add('smooth');
  1386 + block.offsetHeight;
  1387 + block.style.height = `${blockHeight}px`;
  1388 +
  1389 + setTimeout(() => {
  1390 +
  1391 + block.classList.remove('smooth');
  1392 + block.style.height = '';
  1393 + block.style.transition = '';
  1394 + block.style.overflow = '';
  1395 +
  1396 + }, duration);
  1397 +
  1398 + } else if (!block.classList.contains('smooth')) {
  1399 +
  1400 + block.style.height = `${block.offsetHeight}px`;
  1401 + block.offsetHeight;
  1402 + block.style.height = 0;
  1403 + block.style.overflow = "hidden";
  1404 + block.style.transition = `height ${duration}ms ease`;
  1405 + block.classList.add('smooth');
  1406 +
  1407 + setTimeout(() => {
  1408 +
  1409 + block.classList.remove('smooth');
  1410 + block.style.display = "none";
  1411 + block.style.height = '';
  1412 + block.style.transition = '';
  1413 + block.style.overflow = '';
  1414 +
  1415 + }, duration);
  1416 +
  1417 + }
  1418 +
  1419 + };
  1420 +
  1421 + itemTitle.addEventListener('click', (e) => {
  1422 + itemTitle.classList.toggle('active');
  1423 + blockToggle(itemContent, 300);
  1424 + });
  1425 +
  1426 + });
  1427 +
  1428 + }
  1429 +
  1430 +
  1431 + // слайдер с партнёрами;
  1432 + initPartnerslSlider() {
  1433 +
  1434 + const slider = document.querySelector('.partners__swiper');
  1435 +
  1436 + if (slider) {
  1437 +
  1438 + let swiper6;
  1439 +
  1440 + const initSlider = () => {
  1441 +
  1442 + swiper6 = new Swiper(slider, {
  1443 + // scrollbar: {
  1444 + // el: '.swiper-scrollbar',
  1445 + // draggable: true,
  1446 + // },
  1447 + slidesPerView: 0.275,
  1448 + loop: true,
  1449 + spaceBetween: 20,
  1450 + freeMode: true,
  1451 + allowTouchMove: true,
  1452 + breakpoints: {
  1453 + 480: {
  1454 + slidesPerView: 0.4,
  1455 + },
  1456 + 640: {
  1457 + slidesPerView: 0.65,
  1458 + },
  1459 + 780: {
  1460 + slidesPerView: 0.65,
  1461 + },
  1462 + 1024: {
  1463 + slidesPerView: 0.8,
  1464 + },
  1465 + 1200: {
  1466 + slidesPerView: 1,
  1467 + loop: false,
  1468 + allowTouchMove: false,
  1469 + }
  1470 + }
  1471 + });
  1472 +
  1473 + };
  1474 +
  1475 + initSlider();
  1476 +
  1477 + const updateSlider = () => {
  1478 + swiper6.destroy();
  1479 + initSlider();
  1480 + }
  1481 +
  1482 + window.addEventListener('resize', () => {
  1483 +
  1484 + if (window.innerWidth <= 1200 && slider.dataset.mobile == 'false') {
  1485 + slider.dataset.mobile = 'true';
  1486 + updateSlider();
  1487 + }
  1488 +
  1489 + if (window.innerWidth > 1200 && slider.dataset.mobile == 'true') {
  1490 + slider.dataset.mobile = 'false';
  1491 + updateSlider();
  1492 + }
  1493 +
  1494 + });
  1495 +
  1496 + }
  1497 +
  1498 + }
  1499 +
  1500 +}
  1501 +
  1502 +
  1503 +document.addEventListener('DOMContentLoaded', () => {
  1504 +
  1505 + const app = new App();
  1506 + app.init();
  1507 +
  1508 +});
public/js/main_new.js
Changes suppressed. Click to show
... ... @@ -1,1508 +0,0 @@
1   -// управляющий класс App с методом init(), в котором собраны все используемые методы с комментариями о том, что конкретно делает каждый метод
2   -
3   -class App {
4   -
5   - constructor() {
6   - this.patternPhone = /^(\+7|7|8)?[\s\-]?\(?[489][0-9]{2}\)?[\s\-]?[0-9]{3}[\s\-]?[0-9]{2}[\s\-]?[0-9]{2}$/; // рег. выражение для поля 'телефон';
7   - this.patternEmail = /^[a-zA-Z0-9._%+-\.]+@[a-z0-9.-]+\.[a-z]{2,}$/i; // рег. выражение для поля 'электронная почта';
8   - }
9   -
10   - init() {
11   -
12   - console.log('init');
13   -
14   - this.stickyHeader(); // липкий хедер;
15   - this.controlBurgerMenu(); // бургер-меню;
16   - this.smoothScroll(); // плавный скролл к якорю (smooth scroll);
17   - this.scrollUp(); // кнопка наверх;
18   - this.addToFavorites(); // добавить в избранное (звёздочка);
19   - this.initTypicalSlider(); // типовые слайдеры;
20   - this.initPartnerslSlider(); // слайдер с партнёрами;
21   - this.controlFilters(); // фильтры на главном экране;
22   - this.controlPopups(); // открытие/закрытие поп-апов;
23   - this.controlContactUsPopup(); // открытие/закрытие поп-апа 'обратный звонок';
24   -
25   - this.sendForm('.js_popup_feedback_form', '[data-popup="success"]'); // отправка формы в поп-апе обратной связи;
26   - this.sendForm('.js_popup_viewing_form', '[data-popup="success"]'); // отправка формы в поп-апе 'записаться на просмотр';
27   - this.sendForm('.js_footer_feedback_form', '[data-popup="success"]'); // отправка формы в футере;
28   - this.sendForm('.js_contacts_form', '.js_contacts_success'); // отправка формы на странице контакты;
29   - this.sendForm('.js_popup_sending_form_', '[data-popup="success"]');
30   - //this.sendOffer(); //отправка предложения по e-mail;
31   -
32   - //this.setGeneralMap(); // карта на странице карт;
33   - this.setComplexMap('complex-map', [55.726591050908745, 37.57244549999999], 'ЖК Садовые кварталы'); // карта на странице 'ЖК';
34   - this.setComplexMap('offer-map', [55.70851106903402, 37.65864349999999], 'Аренда торгового помещения 321,6 м2'); // карта на странице 'Предложение';
35   - this.setCatalogSorts(); // сортировка на странице 'каталог';
36   - this.initIntroSlider(); // слайдер на странице жк и на странице предложения;
37   - this.setTabs('.js_offer_side_tab', '.js_offer_side_item'); // табы с планами объекат и этажа на странице предложения;
38   - this.setTabs('.js_offer_side_popup_tab', '.js_offer_side_popup_item'); // табы с планами объекат и этажа в поп-апе на странице предложения;
39   - this.sontrolOfferSidePopup(); // логика открытия нужного таба при открытии поп-апа с планами объекат и этажа на странице предложения;
40   - this.setCustomGallery(); // галлерея;
41   - this.setCookies() // куки;
42   - this.setFooterSpoilers() // аккордеон в футере;
43   -
44   - }
45   -
46   - // фиксация <body>
47   - fixBodyPosition() {
48   -
49   - setTimeout(function () {
50   - // ставим необходимую задержку, чтобы не было «конфликта» в случае, если функция фиксации вызывается сразу после расфиксации (расфиксация отменяет действия расфиксации из-за одновременного действия)
51   -
52   - if (!document.body.hasAttribute('data-body-scroll-fix')) {
53   -
54   - // получаем позицию прокрутки
55   - let scrollPosition = window.pageYOffset || document.documentElement.scrollTop;
56   -
57   - // ставим нужные стили
58   - document.body.setAttribute('data-body-scroll-fix', scrollPosition); // Cтавим атрибут со значением прокрутки
59   - document.body.style.overflow = 'hidden';
60   - document.body.style.position = 'fixed';
61   - document.body.style.top = '-' + scrollPosition + 'px';
62   - document.body.style.left = '0';
63   - document.body.style.width = '100%';
64   -
65   - if (window.innerWidth >= 1200) {
66   - document.body.style.paddingRight = '8px';
67   - }
68   - }
69   -
70   - }, 15); // можно задержку ещё меньше, но работает хорошо именно с этим значением на всех устройствах и браузерах
71   -
72   - }
73   -
74   -
75   - // расфиксация <body>
76   - unfixBodyPosition() {
77   -
78   - if (document.body.hasAttribute('data-body-scroll-fix')) {
79   -
80   - // получаем позицию прокрутки из атрибута
81   - let scrollPosition = document.body.getAttribute('data-body-scroll-fix');
82   -
83   - // удаляем атрибут
84   - document.body.removeAttribute('data-body-scroll-fix');
85   -
86   - // удаляем ненужные стили
87   - document.body.style.overflow = '';
88   - document.body.style.position = '';
89   - document.body.style.top = '';
90   - document.body.style.left = '';
91   - document.body.style.width = '';
92   - document.body.style.paddingRight = '';
93   -
94   - // прокручиваем страницу на полученное из атрибута значение
95   - window.scroll(0, scrollPosition);
96   -
97   - }
98   -
99   - }
100   -
101   -
102   - // бургер-меню
103   - controlBurgerMenu() {
104   -
105   - const headerBurger = document.querySelector('.js_header_burger');
106   -
107   - if (headerBurger) {
108   -
109   - const menu = document.querySelector('.js_menu');
110   - const menuClose = menu.querySelector('.js_menu_close');
111   -
112   - headerBurger.addEventListener('click', () => {
113   - menu.classList.add('active');
114   - this.fixBodyPosition();
115   - });
116   -
117   - menu.addEventListener('click', (e) => {
118   -
119   - if (e.target == menu) {
120   - menu.classList.remove('active');
121   - this.unfixBodyPosition();
122   - }
123   -
124   - });
125   -
126   - menuClose.addEventListener('click', () => {
127   - menu.classList.remove('active');
128   - this.unfixBodyPosition();
129   - });
130   -
131   - }
132   -
133   - }
134   -
135   -
136   - // липкий хедер
137   - stickyHeader() {
138   -
139   - const header = document.querySelector('.js_header');
140   -
141   - if (header) {
142   -
143   - window.addEventListener('scroll', () => {
144   -
145   - if (window.scrollY > 200) {
146   - header.classList.add('fixed');
147   - } else {
148   - header.classList.remove('fixed');
149   - }
150   -
151   - });
152   -
153   - };
154   -
155   - }
156   -
157   -
158   - // плавный скролл к якорю (smooth scroll)
159   - smoothScroll() {
160   -
161   - const smoothLinks = document.querySelectorAll('.js_smooth_link');
162   -
163   - if (smoothLinks.length) {
164   -
165   - smoothLinks.forEach(link => {
166   -
167   - link.addEventListener('click', function (e) {
168   -
169   - e.preventDefault();
170   -
171   - let href = this.getAttribute('href').substring(1);
172   -
173   - const scrollTarget = document.getElementById(href);
174   -
175   - // const topOffset = document.querySelector('.header').offsetHeight;
176   - const topOffset = 0; // если не нужен отступ сверху
177   - const elementPosition = scrollTarget.getBoundingClientRect().top;
178   - const offsetPosition = elementPosition - topOffset;
179   -
180   - window.scrollBy({
181   - top: offsetPosition,
182   - behavior: 'smooth'
183   - });
184   -
185   - });
186   -
187   - });
188   -
189   - }
190   -
191   - }
192   -
193   -
194   - // кнопка наверх
195   - scrollUp() {
196   -
197   - const toTopBtn = document.querySelector('.js_btn_up');
198   -
199   - if (toTopBtn) {
200   -
201   - toTopBtn.addEventListener('click', function () {
202   -
203   - window.scrollTo({
204   - top: 0,
205   - behavior: 'smooth'
206   - });
207   -
208   - });
209   -
210   - }
211   -
212   - }
213   -
214   -
215   - // добавить в избранное (звёздочка)
216   - addToFavorites() {
217   -
218   - const cardFavorites = document.querySelectorAll('.js_card_favorites');
219   -
220   - if (cardFavorites.length) {
221   -
222   - cardFavorites.forEach(item => {
223   -
224   - item.addEventListener('click', (e) => {
225   - e.preventDefault();
226   - item.classList.toggle('active');
227   - });
228   -
229   - });
230   -
231   - }
232   -
233   - }
234   -
235   -
236   - // типовые слайдеры
237   - initTypicalSlider() {
238   -
239   - const slidersWraps = document.querySelectorAll('.slider__wrap');
240   -
241   - if (slidersWraps.length) {
242   -
243   - slidersWraps.forEach(wrap => {
244   -
245   - const slider = wrap.querySelector('.swiper');
246   - const prev = wrap.querySelector('.swiper-button-prev');
247   - const next = wrap.querySelector('.swiper-button-next');
248   - const pagination = wrap.querySelector('.swiper-pagination');
249   -
250   - let swiper1 = new Swiper(slider, {
251   - navigation: {
252   - nextEl: next,
253   - prevEl: prev,
254   - },
255   - pagination: {
256   - el: pagination,
257   - clickable: true,
258   - },
259   - slidesPerView: 1,
260   - spaceBetween: 20,
261   - observer: true,
262   - observeParents: true,
263   - observeSlideChildren: true,
264   - breakpoints: {
265   - 480: {
266   - slidesPerView: 1.5,
267   - },
268   - 640: {
269   - slidesPerView: 2,
270   - },
271   - 780: {
272   - slidesPerView: 2.5,
273   - },
274   - 920: {
275   - slidesPerView: 3,
276   - },
277   - 1024: {
278   - slidesPerView: 3.4
279   - },
280   - 1200: {
281   - slidesPerView: 4,
282   - }
283   - }
284   - });
285   -
286   - });
287   -
288   - }
289   -
290   - }
291   -
292   -
293   - // метод, делающий число удобночитаемым (добавляет пробел справа через каждые 3 цифры)
294   - prettify(num) {
295   - const withoutSpace = num.replace(/[^\d]/g, ''); //убирает все символы;
296   - return withoutSpace.replace(/(?!^)(?=(?:\d{3})+(?:\.|$))/gm, ' '); //ставит пробелы;
297   - }
298   -
299   -
300   - // фильтры на главном экране
301   - controlFilters() {
302   -
303   - const heroFilters = document.querySelectorAll('.js_hero_filter');
304   - const heroSearchBtns = document.querySelectorAll('.js_hero_search_btn');
305   -
306   - if (heroFilters.length) {
307   -
308   - heroFilters.forEach(filter => {
309   -
310   - const heroFilterInput = filter.querySelector('.js_hero_filter_input');
311   - const heroFilterCurrent = filter.querySelector('.js_hero_filter_current');
312   - const heroFilterItems = filter.querySelectorAll('.hero-filter__item');
313   - const heroFilterFields = filter.querySelectorAll('.js_hero_filter_field');
314   - const heroFilterFrom = filter.querySelector('.js_hero_filter_from');
315   - const heroFilterTo = filter.querySelector('.js_hero_filter_to');
316   - const heroFilterReset = filter.querySelector('.js_hero_filter_reset');
317   -
318   - heroFilterCurrent.addEventListener('click', () => {
319   -
320   - if (filter.classList.contains('active')) {
321   -
322   - filter.classList.remove('active');
323   -
324   - heroSearchBtns.forEach(btn => {
325   - btn.disabled = false;
326   - });
327   -
328   - } else {
329   -
330   - heroFilters.forEach(filter => {
331   - filter.classList.remove('active');
332   - });
333   -
334   - filter.classList.add('active');
335   -
336   - heroSearchBtns.forEach(btn => {
337   - btn.disabled = true;
338   - });
339   -
340   - }
341   -
342   - });
343   -
344   - if (heroFilterItems.length) {
345   -
346   - heroFilterItems.forEach(item => {
347   -
348   - item.addEventListener('click', () => {
349   -
350   - heroFilterCurrent.textContent = item.textContent;
351   - heroFilterInput.value = item.dataset.val;
352   - filter.classList.remove('active');
353   -
354   - heroSearchBtns.forEach(btn => {
355   - btn.disabled = false;
356   - });
357   -
358   - });
359   -
360   - });
361   -
362   - }
363   -
364   - if (heroFilterFields.length) {
365   -
366   - const heroFilterMin = heroFilterFrom.dataset.min;
367   - const heroFilterMax = heroFilterTo.dataset.max;
368   -
369   - let heroFilterFromVal;
370   - let heroFilterToVal;
371   -
372   - heroFilterFields.forEach(field => {
373   -
374   - field.addEventListener('input', () => {
375   -
376   - field.value = this.prettify(field.value);
377   -
378   - heroFilterReset.classList.remove('active');
379   -
380   - heroFilterFields.forEach(field => {
381   -
382   - if (field.value != "") {
383   - heroFilterReset.classList.add('active');
384   - }
385   -
386   - });
387   -
388   - });
389   -
390   - });
391   -
392   - heroFilterFrom.addEventListener('change', () => {
393   -
394   - heroFilterFromVal = +heroFilterFrom.value.replace(/\s/g, '');
395   - heroFilterToVal = +heroFilterTo.value.replace(/\s/g, '');
396   -
397   - if (heroFilterToVal != '' && heroFilterFromVal > heroFilterToVal) {
398   -
399   - heroFilterFrom.value = heroFilterTo.value;
400   -
401   - } else if (heroFilterFromVal < +heroFilterMin) {
402   -
403   - heroFilterFrom.value = this.prettify(heroFilterMin);
404   -
405   - } else if (heroFilterFromVal > +heroFilterMax) {
406   -
407   - heroFilterFrom.value = this.prettify(heroFilterMax);
408   -
409   - }
410   -
411   - });
412   -
413   - heroFilterTo.addEventListener('change', () => {
414   -
415   - heroFilterFromVal = +heroFilterFrom.value.replace(/\s/g, '');
416   - heroFilterToVal = +heroFilterTo.value.replace(/\s/g, '');
417   -
418   - if (heroFilterFromVal != '' && heroFilterToVal < heroFilterFromVal) {
419   -
420   - heroFilterTo.value = heroFilterFrom.value;
421   -
422   - } else if (heroFilterToVal < +heroFilterMin) {
423   -
424   - heroFilterTo.value = this.prettify(heroFilterMax);
425   -
426   - } else if (heroFilterToVal > +heroFilterMax) {
427   -
428   - heroFilterTo.value = this.prettify(heroFilterMax);
429   -
430   - }
431   -
432   - });
433   -
434   - heroFilterReset.addEventListener('click', () => {
435   -
436   - heroFilterFields.forEach(field => {
437   - field.value = '';
438   - });
439   -
440   - heroFilterReset.classList.remove('active');
441   -
442   - });
443   - }
444   -
445   - });
446   -
447   - document.addEventListener('click', (e) => {
448   -
449   - if (!e.target.closest('.js_hero_filter_dropdown') && !e.target.closest('.js_hero_filter_current')) {
450   -
451   - heroFilters.forEach(filter => {
452   - filter.classList.remove('active');
453   - });
454   -
455   - heroSearchBtns.forEach(btn => {
456   - btn.disabled = false;
457   - });
458   -
459   - }
460   -
461   - });
462   -
463   - }
464   -
465   - }
466   -
467   -
468   - // открытие/закрытие типовых поп-апов
469   - controlPopups() {
470   -
471   - const popupShowBtns = document.querySelectorAll('[data-btn]');
472   - const popups = document.querySelectorAll('[data-popup]');
473   -
474   - if (popupShowBtns.length) {
475   -
476   - popupShowBtns.forEach(btn => {
477   -
478   - btn.addEventListener('click', (e) => {
479   -
480   - e.preventDefault();
481   -
482   - popups.forEach(popup => {
483   -
484   - popup.classList.remove('active'); // если какойто поп-ап открыт, то закрываем его;
485   - this.unfixBodyPosition();
486   -
487   - if (btn.dataset.btn == popup.dataset.popup) {
488   - popup.classList.add('active');
489   - this.fixBodyPosition();
490   - }
491   -
492   - });
493   -
494   -
495   - });
496   -
497   - });
498   -
499   - popups.forEach(popup => {
500   -
501   - const popupCloseBtns = popup.querySelectorAll('.js_popup_close');
502   -
503   - popupCloseBtns.forEach(btn => {
504   -
505   - btn.addEventListener('click', (e) => {
506   - e.preventDefault();
507   - popup.classList.remove('active');
508   - this.unfixBodyPosition();
509   - });
510   -
511   - });
512   -
513   - popup.addEventListener('click', (e) => {
514   -
515   - if (e.target == popup) {
516   -
517   - popup.classList.remove('active');
518   - this.unfixBodyPosition();
519   - }
520   -
521   - });
522   -
523   - });
524   -
525   - }
526   - }
527   -
528   -
529   - // открытие/закрытие поп-апа 'обратный звонок'
530   - controlContactUsPopup() {
531   -
532   - const contactUsBtn = document.querySelector('.js_btn_contact_us');
533   - const contactUsPopup = document.querySelector('.js_contact_us');
534   -
535   - if (contactUsPopup) {
536   -
537   - const contactUsPopupCloseBtns = contactUsPopup.querySelectorAll('.js_contact_us_close');
538   -
539   - contactUsBtn.addEventListener('click', (e) => {
540   -
541   - e.preventDefault();
542   -
543   - if (contactUsPopup.classList.contains('active')) {
544   - contactUsPopup.classList.remove('active');
545   - } else {
546   - contactUsPopup.classList.add('active');
547   - }
548   -
549   - });
550   -
551   - contactUsPopupCloseBtns.forEach(btn => {
552   - btn.addEventListener('click', () => {
553   - contactUsPopup.classList.remove('active');
554   - });
555   - });
556   -
557   -
558   - document.addEventListener('click', (e) => {
559   -
560   - if (!e.target.closest('.js_contact_us') && !e.target.closest('.js_btn_contact_us')) {
561   - contactUsPopup.classList.remove('active');
562   - }
563   -
564   - });
565   -
566   - }
567   -
568   - }
569   -
570   -
571   - // валидатор форм
572   - validateForm(input) {
573   -
574   - // функция добавления ошибки
575   - const createError = (text) => {
576   -
577   - input.classList.add('error');
578   - input.classList.remove('no-error');
579   -
580   - if (input.closest('label').querySelector('span.error')) {
581   - input.closest('label').querySelector('span.error').remove();
582   - input.closest('label').insertAdjacentHTML('beforeend', `<span class="error">${text}</span>`);
583   - } else {
584   - input.closest('label').insertAdjacentHTML('beforeend', `<span class="error">${text}</span>`);
585   - }
586   -
587   - }
588   -
589   - // функция удаления ошибки
590   - const removeError = () => {
591   -
592   - input.classList.remove('error');
593   - input.classList.add('no-error');
594   -
595   - if (input.closest('label').querySelector('span.error')) {
596   - input.closest('label').querySelector('span.error').remove();
597   - }
598   -
599   - }
600   -
601   - // проверяем на правильность заполнения поля 'Телефон'
602   - if (input.classList.contains('js_input_phone') && input.value == "") {
603   - createError('Заполните, пожалуйста, поле');
604   - } else if (input.classList.contains('js_input_phone') && input.value.search(this.patternPhone) == 0) {
605   - removeError();
606   - } else if (input.classList.contains('js_input_phone')) {
607   - createError('Укажите, пожалуйста, корректный телефон');
608   - }
609   -
610   - // проверяем правильность заполнения поля 'Электронная почта'
611   - if (input.classList.contains('js_input_email') && input.value == "") {
612   - createError('Заполните, пожалуйста, поле');
613   - } else if (input.classList.contains('js_input_email') && input.value.search(this.patternEmail) == 0) {
614   - removeError();
615   - } else if (input.classList.contains('js_input_email')) {
616   - createError('Укажите, пожалуйста, корректный e-mail');
617   - }
618   -
619   - }
620   -
621   -
622   - // отправка форм
623   - sendForm(formEl, success) {
624   -
625   - const form = document.querySelector(formEl);
626   -
627   - if (form) {
628   -
629   - form.addEventListener('submit', async (e) => {
630   -
631   - e.preventDefault();
632   -
633   - const formInputs = form.querySelectorAll('input');
634   - const formBtn = form.querySelector('.js_form_btn');
635   -
636   - formInputs.forEach(input => { // перебираем все инпуты в форме;
637   -
638   - this.validateForm(input);
639   -
640   - input.addEventListener('input', () => {
641   - this.validateForm(input);
642   - });
643   -
644   - });
645   -
646   - if (!form.querySelector('.error')) { //проверяем, чтоб все инпуты прошли валидацию (чтоб не было в форме ни одного элемента с класссом error);
647   -
648   - // сюда пишем команды, которые должны сработать после успешной валидации;
649   -
650   - console.log('validate');
651   - formBtn.classList.add('btn-animate');
652   - formBtn.disabled = true;
653   -
654   - const formData = new FormData(form);
655   -
656   - console.log(...formData);
657   -
658   - const response = await fetch(e.target.action, {
659   - method: e.target.method,
660   - body: formData
661   - });
662   -
663   - if (response.ok) {
664   -
665   - setTimeout(() => { // имитация отправки, когда отправка будет настроена, нужно достать всё из setTimeout() и удалить его;
666   -
667   - console.log('Отправлено');
668   - formBtn.classList.remove('btn-animate');
669   - formBtn.disabled = false;
670   - if (document.querySelector('[data-popup="feedback"]')) {
671   - document.querySelector('[data-popup="feedback"]').classList.remove('active');
672   - }
673   - if (document.querySelector('[data-popup="viewing"]')) {
674   - document.querySelector('[data-popup="viewing"]').classList.remove('active');
675   - }
676   - document.querySelector(success).classList.add('active');
677   - this.fixBodyPosition();
678   - form.reset();
679   -
680   - formInputs.forEach(input => {
681   - input.classList.remove('no-error');
682   - });
683   -
684   - }, 2000)
685   -
686   - } else {
687   - formBtn.classList.remove('btn-animate');
688   - formBtn.disabled = false;
689   - alert('Ошибка');
690   - }
691   -
692   - } else {
693   - console.log('no-validate');
694   - form.querySelector('.error').focus(); //фокус к полю с ошибкой;
695   - }
696   -
697   - });
698   -
699   - }
700   -
701   - }
702   -
703   - //отправка предложения по e-mail
704   - sendOffer() {
705   -
706   - const form = document.querySelector('.js_popup_sending_form');
707   -
708   - if (form) {
709   -
710   - form.addEventListener('submit', async (e) => {
711   -
712   - e.preventDefault();
713   -
714   - const formInputs = form.querySelectorAll('input');
715   - const formBtn = form.querySelector('.js_form_btn');
716   -
717   - formInputs.forEach(input => { // перебираем все инпуты в форме;
718   -
719   - this.validateForm(input);
720   -
721   - input.addEventListener('input', () => {
722   - this.validateForm(input);
723   - });
724   -
725   - });
726   -
727   - if (!form.querySelector('.error')) { //проверяем, чтоб все инпуты прошли валидацию (чтоб не было в форме ни одного элемента с класссом error);
728   -
729   - // сюда пишем команды, которые должны сработать после успешной валидации;
730   -
731   - console.log('validate');
732   - formBtn.classList.add('btn-animate');
733   - formBtn.disabled = true;
734   -
735   - const formData = new FormData(form);
736   -
737   - console.log(...formData);
738   -
739   - const response = await fetch(e.target.action, {
740   - method: e.target.method,
741   - body: formData
742   - });
743   -
744   - if (response.ok) {
745   -
746   - setTimeout(() => { // имитация отправки, когда отправка будет настроена, нужно достать всё из setTimeout() и удалить его;
747   -
748   - console.log('Отправлено');
749   - formBtn.classList.remove('btn-animate');
750   - formBtn.disabled = false;
751   - if (document.querySelector('[data-popup="sending"]')) {
752   - document.querySelector('[data-popup="sending"]').classList.remove('active');
753   - }
754   - this.fixBodyPosition();
755   - form.reset();
756   -
757   - formInputs.forEach(input => {
758   - input.classList.remove('no-error');
759   - });
760   -
761   - }, 2000)
762   -
763   - } else {
764   - formBtn.classList.remove('btn-animate');
765   - formBtn.disabled = false;
766   - alert('Ошибка');
767   - }
768   -
769   - } else {
770   - console.log('no-validate');
771   - form.querySelector('.error').focus(); //фокус к полю с ошибкой;
772   - }
773   -
774   - });
775   -
776   - }
777   -
778   - }
779   -
780   -
781   - // карта на странице 'ЖК'
782   - setComplexMap(id, coords, caption) {
783   -
784   - if (document.querySelector('#' + id)) {
785   -
786   - // Дождёмся загрузки API и готовности DOM.
787   - ymaps.ready(init);
788   -
789   - function init() {
790   - const map = new ymaps.Map(id, {
791   - // При инициализации карты обязательно нужно указать её центр и коэффициент масштабирования.
792   - center: coords,
793   - zoom: 16,
794   - controls: []
795   - });
796   -
797   - // Создаём макет содержимого.
798   - const MyIconContentLayout = ymaps.templateLayoutFactory.createClass(
799   - '<div style="color: #FFFFFF; font-weight: bold;">$[properties.iconContent]</div>'
800   - );
801   -
802   - // Создание макета содержимого хинта.
803   - // Макет создается через фабрику макетов с помощью текстового шаблона.
804   - const HintLayout = ymaps.templateLayoutFactory.createClass("<div class='my-hint'>" +
805   - "{{ properties.object }}" + "</div>", {
806   - // Определяем метод getShape, который
807   - // будет возвращать размеры макета хинта.
808   - // Это необходимо для того, чтобы хинт автоматически
809   - // сдвигал позицию при выходе за пределы карты.
810   - getShape: function () {
811   - let el = this.getElement(),
812   - result = null;
813   - if (el) {
814   - var firstChild = el.firstChild;
815   - result = new ymaps.shape.Rectangle(
816   - new ymaps.geometry.pixel.Rectangle([
817   - [0, 0],
818   - [firstChild.offsetWidth, firstChild.offsetHeight]
819   - ])
820   - );
821   - }
822   - return result;
823   - }
824   - }
825   - );
826   -
827   - // метка
828   - const placemark = new ymaps.Placemark(coords, {
829   - // hintContent: caption,
830   - // balloonContent: caption,
831   - iconContent: '1',
832   - // address: caption,
833   - object: caption
834   - }, {
835   - iconLayout: 'default#imageWithContent',
836   - iconImageHref: 'images/mark-complex.svg',
837   - iconImageSize: [52, 67],
838   - iconImageOffset: [-26, -67],
839   - iconContentOffset: [0, 17],
840   - iconContentLayout: MyIconContentLayout,
841   - hintLayout: HintLayout
842   - });
843   -
844   - map.geoObjects.add(placemark);
845   -
846   - }
847   -
848   - }
849   -
850   - }
851   -
852   -
853   - // фильтры и сортировка на странице 'каталог'
854   - setCatalogSorts() {
855   -
856   - const sortGroups = document.querySelectorAll('.js_sort_group');
857   -
858   - if (sortGroups.length) {
859   -
860   - sortGroups.forEach(group => {
861   -
862   - const sortGroupInput = group.querySelector('.js_sort_group_input');
863   - const sortGroupCurrent = group.querySelector('.js_sort_group_current');
864   - const sortGroupList = group.querySelector('.js_sort_group_list');
865   - const sortGroupItems = group.querySelectorAll('.js_sort_group_item');
866   -
867   - const sendRequest = () => {
868   -
869   - const spinner = document.querySelector('.spinner'); // спиннер;
870   -
871   - spinner.classList.add('active');
872   - document.body.classList.add('overlay');
873   - /*this.fixBodyPosition();
874   -
875   - fetch('test.json')
876   - .then(response => response.json())
877   - .then(data => {
878   -
879   - console.log()
880   -
881   - setTimeout(() => { //имитация ответа сервера
882   -
883   - spinner.classList.remove('active');
884   - document.body.classList.remove('overlay');
885   - this.unfixBodyPosition();
886   -
887   - }, 3000);
888   -
889   - })
890   - .catch(err => {
891   - console.log(err);
892   - });
893   -
894   - */
895   - spinner.classList.remove('active');
896   - document.body.classList.remove('overlay');
897   -
898   -
899   - };
900   -
901   - sortGroupCurrent.addEventListener('click', () => {
902   -
903   - if (group.classList.contains('active')) {
904   -
905   - group.classList.remove('active');
906   -
907   - } else {
908   -
909   - sortGroups.forEach(group => {
910   - group.classList.remove('active');
911   - });
912   -
913   - group.classList.add('active');
914   -
915   - }
916   -
917   - });
918   -
919   - sortGroupItems.forEach(item => {
920   -
921   - item.addEventListener('click', () => {
922   -
923   - sortGroupItems.forEach(item => {
924   - item.classList.remove('active');
925   - });
926   -
927   - item.classList.add('active');
928   - sortGroupCurrent.textContent = item.textContent;
929   - sortGroupInput.value = item.dataset.val;
930   - group.classList.remove('active');
931   -
932   - sendRequest();
933   -
934   - });
935   -
936   - });
937   -
938   - });
939   -
940   - document.addEventListener('click', (e) => {
941   -
942   - if (!e.target.closest('.js_sort_group_list') && !e.target.closest('.js_sort_group_current')) {
943   -
944   - sortGroups.forEach(group => {
945   - group.classList.remove('active');
946   - });
947   -
948   - }
949   -
950   - });
951   -
952   - }
953   -
954   - }
955   -
956   -
957   - // слайдер на странице жк и на странице предложения
958   - initIntroSlider() {
959   -
960   - let swiper3 = new Swiper('.intro__swiper', {
961   - navigation: {
962   - nextEl: '.swiper-button-next',
963   - prevEl: '.swiper-button-prev',
964   - },
965   - pagination: {
966   - el: '.swiper-pagination',
967   - clickable: true,
968   - },
969   - slidesPerView: 1.1,
970   - spaceBetween: 20,
971   - breakpoints: {
972   - 480: {
973   - slidesPerView: 1.5,
974   - },
975   - 640: {
976   - slidesPerView: 1.75,
977   - },
978   - 780: {
979   - slidesPerView: 2.15,
980   - },
981   - 1024: {
982   - slidesPerView: 3.5,
983   - },
984   - 1200: {
985   - slidesPerView: 1,
986   - }
987   - }
988   - });
989   -
990   - }
991   -
992   -
993   - // табы на странице предложения
994   - setTabs(tabs, items) {
995   -
996   - const offerSideTabs = document.querySelectorAll(tabs);
997   - const offerSideItems = document.querySelectorAll(items);
998   -
999   - if (offerSideTabs) {
1000   -
1001   - offerSideTabs.forEach(tab => {
1002   -
1003   - tab.addEventListener('click', () => {
1004   -
1005   - offerSideTabs.forEach(tab => {
1006   - tab.classList.remove('active');
1007   - });
1008   -
1009   - tab.classList.add('active');
1010   -
1011   - offerSideItems.forEach(item => {
1012   -
1013   - item.classList.remove('active', 'fade');
1014   -
1015   - if (tab.dataset.tab == item.dataset.item) {
1016   - item.classList.add('active', 'fade');
1017   - }
1018   -
1019   - });
1020   -
1021   - });
1022   -
1023   - });
1024   -
1025   - }
1026   -
1027   - }
1028   -
1029   -
1030   - // логика открытия нужного таба при открытии поп-апа с планами объекат и этажа на странице предложения
1031   - sontrolOfferSidePopup() {
1032   -
1033   - const offerSideItems = document.querySelectorAll('.js_offer_side_item');
1034   - const offerSidePopupTabs = document.querySelectorAll('.js_offer_side_popup_tab');
1035   - const offerSidePopupItems = document.querySelectorAll('.js_offer_side_popup_item');
1036   -
1037   - if (offerSideItems) {
1038   -
1039   - offerSideItems.forEach(item => {
1040   -
1041   - const offerSideItemBtn = item.querySelector('.js_offer_side_item_btn');
1042   -
1043   - offerSideItemBtn.addEventListener('click', (e) => {
1044   -
1045   - e.preventDefault();
1046   -
1047   - offerSidePopupTabs.forEach(tab => {
1048   -
1049   - tab.classList.remove('active');
1050   -
1051   - if (item.dataset.item == tab.dataset.tab) {
1052   - tab.classList.add('active');
1053   - }
1054   -
1055   - });
1056   -
1057   - offerSidePopupItems.forEach(el => {
1058   -
1059   - el.classList.remove('active', 'fade');
1060   -
1061   - if (item.dataset.item == el.dataset.item) {
1062   - el.classList.add('active', 'fade');
1063   - }
1064   -
1065   - });
1066   -
1067   - });
1068   -
1069   - });
1070   -
1071   - }
1072   -
1073   - }
1074   -
1075   -
1076   - // галлерея
1077   - setCustomGallery() {
1078   -
1079   - let swiper4 = new Swiper(".img-viewer__thumbs-swiper", {
1080   - slidesPerView: 3,
1081   - spaceBetween: 8,
1082   - // freeMode: true,
1083   - observer: true,
1084   - observeParents: true,
1085   - observeSlideChildren: true,
1086   - breakpoints: {
1087   - 640: {
1088   - spaceBetween: 10,
1089   - },
1090   - },
1091   - });
1092   -
1093   - let swiper5 = new Swiper(".img-viewer__slider .swiper", {
1094   - navigation: {
1095   - nextEl: ".img-viewer__slider .swiper-button-next",
1096   - prevEl: ".img-viewer__slider .swiper-button-prev",
1097   - },
1098   - slidesPerView: 1,
1099   - spaceBetween: 20,
1100   - thumbs: {
1101   - swiper: swiper4
1102   - },
1103   - observer: true,
1104   - observeParents: true,
1105   - observeSlideChildren: true,
1106   - });
1107   -
1108   - if (document.querySelector('.js_intro_item_btn')) {
1109   -
1110   - const imgViewer = document.querySelector('.js_img_viewer');
1111   - const imgViewerCloses = imgViewer.querySelectorAll('.js_img_viewer_close');
1112   - const imgViewerCaption = imgViewer.querySelector('.js_img_viewer_caption');
1113   -
1114   - const imgViewerSliderSwiper = imgViewer.querySelector('.js_img_viewer_slider_swiper');
1115   - const imgViewerSliderSwiperWrap = imgViewerSliderSwiper.querySelector('.js_img_viewer_slider_swiper .swiper-wrapper');
1116   -
1117   - const imgViewerThumbsSwiper = imgViewer.querySelector('.js_img_viewer_thumbs_swiper');
1118   - const imgViewerThumbsSwiperWrap = imgViewerThumbsSwiper.querySelector('.js_img_viewer_thumbs_swiper .swiper-wrapper');
1119   -
1120   - const introItemBtns = document.querySelectorAll('.js_intro_item_btn');
1121   -
1122   - introItemBtns.forEach((btn, i) => {
1123   -
1124   - btn.addEventListener('click', (e) => {
1125   -
1126   - e.preventDefault();
1127   -
1128   - imgViewer.classList.add('active');
1129   - this.fixBodyPosition();
1130   -
1131   - imgViewerSliderSwiperWrap.innerHTML = '';
1132   - imgViewerThumbsSwiperWrap.innerHTML = '';
1133   - imgViewerCaption.textContent = '';
1134   -
1135   -
1136   - introItemBtns.forEach(btn => {
1137   -
1138   - imgViewerSliderSwiperWrap.insertAdjacentHTML('beforeend', `
1139   - <div class="swiper-slide">
1140   - <img src="${btn.getAttribute('href')}" alt="">
1141   - </div>`
1142   - );
1143   -
1144   - imgViewerThumbsSwiperWrap.insertAdjacentHTML('beforeend', `
1145   - <div class="swiper-slide">
1146   - <img src="${btn.getAttribute('href')}" alt="">
1147   - </div>`
1148   - );
1149   -
1150   - });
1151   -
1152   - swiper4.update();
1153   - swiper5.update();
1154   - swiper5.slideTo(i);
1155   - imgViewerCaption.textContent = btn.dataset.caption;
1156   -
1157   - });
1158   -
1159   - });
1160   -
1161   - swiper5.on('slideChange', function () {
1162   - imgViewerCaption.textContent = introItemBtns[swiper5.realIndex].dataset.caption;
1163   - });
1164   -
1165   - imgViewerCloses.forEach(close => {
1166   -
1167   - close.addEventListener('click', () => {
1168   -
1169   - imgViewer.classList.remove('active');
1170   - this.unfixBodyPosition();
1171   -
1172   - });
1173   -
1174   - });
1175   -
1176   - }
1177   -
1178   - }
1179   -
1180   -
1181   - // куки
1182   - setCookies() {
1183   -
1184   - const cookies = document.querySelector('.js_cookies');
1185   - const cookiesBtn = document.querySelector('.js_cookies_confirm');
1186   - const cookiesTrigger = document.querySelector('.js_btn_cookies');
1187   -
1188   - if (cookiesTrigger) {
1189   -
1190   - cookiesTrigger.addEventListener('click', () => {
1191   - cookies.classList.add('active');
1192   - });
1193   -
1194   - };
1195   -
1196   - if (cookies) {
1197   -
1198   - cookiesBtn.addEventListener('click', () => {
1199   - cookies.classList.remove('active');
1200   - });
1201   -
1202   - };
1203   -
1204   - }
1205   -
1206   -
1207   - // карта на странице карт;
1208   - /*
1209   - //setGeneralMap() {
1210   -
1211   - if (document.querySelector('#general-map')) {
1212   -
1213   - ymaps.ready(init); // Дождёмся загрузки API и готовности DOM;
1214   -
1215   - function init() {
1216   -
1217   - const myMap = new ymaps.Map('general-map', { // Создание экземпляра карты и его привязка к контейнеру с заданным id;
1218   - center: [55.752933963675126, 37.52233749962665], // При инициализации карты обязательно нужно указать её центр и коэффициент масштабирования;
1219   - zoom: 10,
1220   - controls: [] // Скрываем элементы управления на карте;
1221   - });
1222   -
1223   - // Создаём макет содержимого.
1224   - const MyIconContentLayout = ymaps.templateLayoutFactory.createClass(
1225   - '<div style="color: #FFFFFF; font-weight: bold;">$[properties.iconContent]</div>'
1226   - );
1227   -
1228   - let collection = new ymaps.GeoObjectCollection(null, { // Создаём коллекцию, в которую будемпомещать метки (что-то типа массива);
1229   - // preset: 'islands#yellowIcon'
1230   - });
1231   -
1232   - let collectionCoords = [ // Создаём массив с координатами (координаты должны располагаться в том же порядке, что и адреса в списке на сайте);
1233   - [55.867783219108354, 37.392867499999916],
1234   - [55.728043075486504, 37.73937949999994],
1235   - [55.72624100423305, 37.476078499999964],
1236   - [55.80751105044832, 37.449622999999974],
1237   - [55.601783098948836, 37.36700499999998],
1238   - [55.86086502152225, 37.540348999999964],
1239   - [55.784961528728715, 37.56188599999996],
1240   - [55.63910010399773, 37.319407999999996],
1241   - [55.55819256767507, 37.55711549999994],
1242   - [55.79829252928473, 37.52063549999999],
1243   - ];
1244   -
1245   - for (let i = 0, l = collectionCoords.length; i < l; i++) { // C помощью цикла добавляем все метки в коллекцию;
1246   - collection.add(new ymaps.Placemark(collectionCoords[i]));
1247   - collection.get(i).properties.set('iconContent', `${i + 1}`); // Добавляем каждой метке порядковый номер, записываем его в свойство 'iconContent';
1248   - }
1249   -
1250   - myMap.geoObjects.add(collection); // Добавляем коллекцию с метками на карту;
1251   -
1252   - collection.options.set('iconLayout', 'default#imageWithContent'); // Необходимо указать данный тип макета;
1253   - collection.options.set('iconImageHref', 'images/mark-complex.svg'); // Своё изображение иконки метки;
1254   - collection.options.set('iconImageSize', [52, 67]); // Размеры метки;
1255   - collection.options.set('iconImageOffset', [-26, -67]); // Смещение левого верхнего угла иконки относительно её "ножки" (точки привязки);
1256   - collection.options.set('iconContentOffset', [0, 17]);
1257   - collection.options.set('iconContentLayout', MyIconContentLayout); // Смещение левого верхнего угла иконки относительно её "ножки" (точки привязки);
1258   -
1259   - const pageMapBar = document.querySelector('.js_page_map_bar');
1260   - const pageMapBarBtn = pageMapBar.querySelector('.js_page_map_bar_btn');
1261   - const pageMapBarList = pageMapBar.querySelector('.js_page_map_bar_list');
1262   - const pageMapBarCards = pageMapBar.querySelectorAll('.card-news');
1263   -
1264   - const showCard = (i) => {
1265   -
1266   - pageMapBarCards.forEach((card, k) => {
1267   -
1268   - card.classList.remove('active');
1269   -
1270   - if (i == k) {
1271   - card.classList.add('active');
1272   - }
1273   -
1274   - });
1275   -
1276   - };
1277   -
1278   - const hidecard = () => {
1279   -
1280   - pageMapBarCards.forEach(card => {
1281   - card.classList.remove('active');
1282   - });
1283   -
1284   - }
1285   -
1286   - let pageMapBarItems;
1287   -
1288   - pageMapBarBtn.addEventListener('click', () => {
1289   - pageMapBar.classList.toggle('active');
1290   - });
1291   -
1292   - pageMapBarList.addEventListener('click', (e) => {
1293   -
1294   - if (e.target.closest('.page-map-bar__item')) {
1295   -
1296   - pageMapBarItems = pageMapBarList.querySelectorAll('.page-map-bar__item');
1297   -
1298   - pageMapBarItems.forEach((item, i) => {
1299   -
1300   - if (e.target == item && e.target.classList.contains('active')) {
1301   -
1302   - item.classList.remove('active');
1303   -
1304   - hidecard();
1305   -
1306   - } else if (e.target == item) {
1307   -
1308   - pageMapBarItems.forEach(item => {
1309   - item.classList.remove('active');
1310   - });
1311   -
1312   - item.classList.add('active');
1313   -
1314   - let offsetCoords = collection.get(i).geometry.getCoordinates();
1315   -
1316   - offsetCoords = [
1317   - offsetCoords[0] - 0.0025,
1318   - offsetCoords[1]
1319   - ];
1320   -
1321   - myMap.setZoom(16);
1322   - // myMap.setCenter(collection.get(i).geometry.getCoordinates());
1323   - myMap.setCenter(offsetCoords);
1324   -
1325   - showCard(i);
1326   -
1327   - }
1328   -
1329   - });
1330   - }
1331   -
1332   - });
1333   -
1334   - collection.events.add('click', function (e) {
1335   -
1336   - for (let i = 0, l = collection.getLength(); i < l; i++) {
1337   -
1338   - if (e.get('target') == collection.get(i)) {
1339   -
1340   - pageMapBarItems = pageMapBarList.querySelectorAll('.page-map-bar__item');
1341   -
1342   - pageMapBarItems.forEach((item) => {
1343   - pageMapBar.classList.add('active');
1344   - item.classList.remove('active');
1345   - });
1346   -
1347   - pageMapBarItems[i].classList.add('active');
1348   -
1349   - showCard(i);
1350   -
1351   - }
1352   -
1353   - }
1354   -
1355   - });
1356   -
1357   - }
1358   -
1359   - }
1360   -
1361   - };*/
1362   -
1363   -
1364   - // аккордеон в футере
1365   - setFooterSpoilers() {
1366   -
1367   - const items = document.querySelectorAll('.js_footer_col');
1368   -
1369   - items.forEach(item => {
1370   -
1371   - const itemTitle = item.querySelector('.js_footer_caption');
1372   - const itemContent = item.querySelector('.js_footer_block');
1373   -
1374   - const blockToggle = (block, duration) => {
1375   -
1376   - if (window.getComputedStyle(block).display == "none" && !block.classList.contains('smooth')) {
1377   -
1378   - block.style.display = "block";
1379   -
1380   - const blockHeight = block.offsetHeight;
1381   -
1382   - block.style.height = 0;
1383   - block.style.overflow = "hidden";
1384   - block.style.transition = `height ${duration}ms ease`;
1385   - block.classList.add('smooth');
1386   - block.offsetHeight;
1387   - block.style.height = `${blockHeight}px`;
1388   -
1389   - setTimeout(() => {
1390   -
1391   - block.classList.remove('smooth');
1392   - block.style.height = '';
1393   - block.style.transition = '';
1394   - block.style.overflow = '';
1395   -
1396   - }, duration);
1397   -
1398   - } else if (!block.classList.contains('smooth')) {
1399   -
1400   - block.style.height = `${block.offsetHeight}px`;
1401   - block.offsetHeight;
1402   - block.style.height = 0;
1403   - block.style.overflow = "hidden";
1404   - block.style.transition = `height ${duration}ms ease`;
1405   - block.classList.add('smooth');
1406   -
1407   - setTimeout(() => {
1408   -
1409   - block.classList.remove('smooth');
1410   - block.style.display = "none";
1411   - block.style.height = '';
1412   - block.style.transition = '';
1413   - block.style.overflow = '';
1414   -
1415   - }, duration);
1416   -
1417   - }
1418   -
1419   - };
1420   -
1421   - itemTitle.addEventListener('click', (e) => {
1422   - itemTitle.classList.toggle('active');
1423   - blockToggle(itemContent, 300);
1424   - });
1425   -
1426   - });
1427   -
1428   - }
1429   -
1430   -
1431   - // слайдер с партнёрами;
1432   - initPartnerslSlider() {
1433   -
1434   - const slider = document.querySelector('.partners__swiper');
1435   -
1436   - if (slider) {
1437   -
1438   - let swiper6;
1439   -
1440   - const initSlider = () => {
1441   -
1442   - swiper6 = new Swiper(slider, {
1443   - // scrollbar: {
1444   - // el: '.swiper-scrollbar',
1445   - // draggable: true,
1446   - // },
1447   - slidesPerView: 0.275,
1448   - loop: true,
1449   - spaceBetween: 20,
1450   - freeMode: true,
1451   - allowTouchMove: true,
1452   - breakpoints: {
1453   - 480: {
1454   - slidesPerView: 0.4,
1455   - },
1456   - 640: {
1457   - slidesPerView: 0.65,
1458   - },
1459   - 780: {
1460   - slidesPerView: 0.65,
1461   - },
1462   - 1024: {
1463   - slidesPerView: 0.8,
1464   - },
1465   - 1200: {
1466   - slidesPerView: 1,
1467   - loop: false,
1468   - allowTouchMove: false,
1469   - }
1470   - }
1471   - });
1472   -
1473   - };
1474   -
1475   - initSlider();
1476   -
1477   - const updateSlider = () => {
1478   - swiper6.destroy();
1479   - initSlider();
1480   - }
1481   -
1482   - window.addEventListener('resize', () => {
1483   -
1484   - if (window.innerWidth <= 1200 && slider.dataset.mobile == 'false') {
1485   - slider.dataset.mobile = 'true';
1486   - updateSlider();
1487   - }
1488   -
1489   - if (window.innerWidth > 1200 && slider.dataset.mobile == 'true') {
1490   - slider.dataset.mobile = 'false';
1491   - updateSlider();
1492   - }
1493   -
1494   - });
1495   -
1496   - }
1497   -
1498   - }
1499   -
1500   -}
1501   -
1502   -
1503   -document.addEventListener('DOMContentLoaded', () => {
1504   -
1505   - const app = new App();
1506   - app.init();
1507   -
1508   -});
public/pug/templates/scripts.pug
... ... @@ -2,4 +2,4 @@
2 2  
3 3 script(src="https://api-maps.yandex.ru/2.1/?lang=ru_RU")
4 4 script(src="js/swiper-bundle.min.js")
5   -script(src="js/main_new.js")
  5 +script(src="js/main_main.js")
resources/views/complex.blade.php
1 1 @extends('layout.site', ['title' => $area->name_area])
2 2  
3 3 @section('custom_js')
  4 + @include('js.maps_js')
4 5 <script>
5 6 $(document).on('click', '.js_sort_group_item', function() {
6 7 var val = $(this).data('val');
... ... @@ -98,6 +99,8 @@
98 99  
99 100 }
100 101 );
  102 +
  103 + setComplexMap('complex-map', [{{ $area->coord_x }}, {{ $area->coord_y }}], "{{ $area->name_area }}");
101 104 </script>
102 105 @endsection
103 106  
resources/views/house/post.blade.php
1   -@extends('layout.site', ['title' => 'Избранное RentTorg'])
  1 +@extends('layout.site', ['title' => $house->title])
  2 +
  3 +@section('custom_js')
  4 + @include('js.maps_js')
  5 + <script>
  6 + setComplexMap('offer-map', [{{ $house->coord_x }}, {{ $house->coord_y }}], "{{ $house->title}}");
  7 + </script>
  8 +@endsection
2 9  
3 10 @section('content')
4 11 <section class="intro" style="background-image:url({{ asset('images/intro-bg-2.jpg') }}">
... ... @@ -30,7 +37,7 @@
30 37 </div>
31 38 </div>
32 39 <div class="intro__bottom">
33   - <div class="intro__views">16 человек интересовались этим объектом за последние 24&nbsp;ч</div>
  40 + <div class="intro__views">{{ $count_user->count() }} человек(а) интересовались этим объектом за последние 24&nbsp;ч</div>
34 41 <a class="intro__link-phone btn btn--main" href="tel:+70001234567"><span>Позвонить
35 42 <svg width="22" height="22">
36 43 <use xlink:href="{{ asset('images/sprite.svg#intro-link-phone') }}"></use>
resources/views/js/maps_js.blade.php
... ... @@ -0,0 +1,71 @@
  1 +<script>
  2 + function setComplexMap(id, coords, caption) {
  3 +
  4 + if (document.querySelector('#' + id)) {
  5 +
  6 + // Дождёмся загрузки API и готовности DOM.
  7 + ymaps.ready(init);
  8 +
  9 + function init() {
  10 + const map = new ymaps.Map(id, {
  11 + // При инициализации карты обязательно нужно указать её центр и коэффициент масштабирования.
  12 + center: coords,
  13 + zoom: 16,
  14 + controls: []
  15 + });
  16 +
  17 + // Создаём макет содержимого.
  18 + const MyIconContentLayout = ymaps.templateLayoutFactory.createClass(
  19 + '<div style="color: #FFFFFF; font-weight: bold;">$[properties.iconContent]</div>'
  20 + );
  21 +
  22 + // Создание макета содержимого хинта.
  23 + // Макет создается через фабрику макетов с помощью текстового шаблона.
  24 + const HintLayout = ymaps.templateLayoutFactory.createClass("<div class='my-hint'>" +
  25 + caption + "</div>", {
  26 + // Определяем метод getShape, который
  27 + // будет возвращать размеры макета хинта.
  28 + // Это необходимо для того, чтобы хинт автоматически
  29 + // сдвигал позицию при выходе за пределы карты.
  30 + getShape: function () {
  31 + let el = this.getElement(),
  32 + result = null;
  33 + if (el) {
  34 + var firstChild = el.firstChild;
  35 + result = new ymaps.shape.Rectangle(
  36 + new ymaps.geometry.pixel.Rectangle([
  37 + [0, 0],
  38 + [firstChild.offsetWidth, firstChild.offsetHeight]
  39 + ])
  40 + );
  41 + }
  42 + return result;
  43 + }
  44 + }
  45 + );
  46 +
  47 + // метка
  48 + const placemark = new ymaps.Placemark(coords, {
  49 + // hintContent: caption,
  50 + // balloonContent: caption,
  51 + iconContent: '1',
  52 + // address: caption,
  53 + object: caption
  54 + }, {
  55 + iconLayout: 'default#imageWithContent',
  56 + iconImageHref: "{{ asset('images/mark-complex.svg') }}",
  57 + iconImageSize: [52, 67],
  58 + iconImageOffset: [-26, -67],
  59 + iconContentOffset: [0, 17],
  60 + iconContentLayout: MyIconContentLayout,
  61 + hintLayout: HintLayout
  62 + });
  63 +
  64 + map.geoObjects.add(placemark);
  65 +
  66 + }
  67 +
  68 + }
  69 +
  70 + }
  71 +</script>
resources/views/layout/admin.blade.php
... ... @@ -457,7 +457,7 @@
457 457 </div>
458 458 <script src="https://api-maps.yandex.ru/2.1/?lang=ru_RU"></script>
459 459 <script src="{{ asset('js/swiper-bundle.min.js') }}"></script>
460   -<script src="{{ asset('js/main_new.js') }}"></script>
  460 +<script src="{{ asset('js/main_main.js') }}"></script>
461 461 <script type="text/javascript" src="{{ asset('js/jquery.min.js') }}"></script>
462 462 <script type="text/javascript" src="{{ asset('js/jquery.cookie.js') }}"></script>
463 463 @yield('custom_js')
resources/views/layout/site.blade.php
... ... @@ -413,7 +413,7 @@
413 413 </div>
414 414 <script src="https://api-maps.yandex.ru/2.1/?lang=ru_RU"></script>
415 415 <script src="{{ asset('js/swiper-bundle.min.js') }}"></script>
416   -<script src="{{ asset('js/main_new.js') }}"></script>
  416 +<script src="{{ asset('js/main_main.js') }}"></script>
417 417 <script type="text/javascript" src="{{ asset('js/jquery.min.js') }}"></script>
418 418 <script type="text/javascript" src="{{ asset('js/jquery.cookie.js') }}"></script>
419 419 @yield('custom_js')