DELETE FROM KEYWORDS WHERE ID_DOMAINE=36214965DoneDoneDone
URL:e-matras.ua
Cache-Control: private, max-age=0, no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Connection: keep-alive
Date: Sat, 02 Nov 2024 04:27:09 GMT
Content-Length: 169
Content-Type: text/html
Expires: Thu, 01 Jan 1970 00:00:01 GMT
Location: https://e-matras.ua/
Server: cloudflare
Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=wOeXKuRy4WQKaFvkFOA8EJV98YB0s53cMe33HRl1aqu9lAAbJzOd%2FDOrYpYzA1QBhhexG28PltPDw6Bio333Jo19tp4d8PnPQsbari41eyX0Amp9%2Bt0hn5CgbVZ%2F"}],"group":"cf-nel","max_age":604800}
NEL: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
expect-ct: max-age=86400, enforce
referrer-policy: same-origin
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
CF-RAY: 8dc15ed8ad9d7918-CDG
alt-svc: h3=":443"; ma=86400
Location => https://e-matras.ua/
Status => 308
<html>
<head><title>308 Permanent Redirect</title></head>
(vide) Tentative en https
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, initial-scale=1.0, user-scalable=no, viewport-fit=cover">
<script src="/polyfill.min.js?v1.6.1+4.8.0" fetchpriority="high"></script>
<script>
const debuglog = (...args) => args.length &&
location.search &&
/[\?&]env=dev(?:&.+)?$/.test(location.search) &&
console[args.some((arg) => arg instanceof Error) ? 'warn' : 'log'](...args) ||
void 0
</script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto+Flex:opsz,slnt,wdth,wght,GRAD,XOPQ,XTRA,YOPQ,YTAS,YTDE,YTFI,YTLC,YTUC@8..144,-10..0,25..151,100..1000,-200..150,27..175,323..603,25..135,649..854,-305..-98,560..788,416..570,528..760&family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=swap" type="text/css" fetchpriority="high" media="all">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/combine/npm/notyf/notyf.min.css,npm/css-tooltip,npm/spinkit/spinkit.min.css,npm/photoswipe/dist/photoswipe.min.css" type="text/css" fetchpriority="low" media="screen">
<script src="https://cdn.jsdelivr.net/combine/npm/mobile-detect,npm/@glidejs/glide,npm/micromodal,npm/imask,npm/notyf,npm/translit-ed/dist/translit-ed.js,npm/photoswipe/dist/umd/photoswipe.umd.min.js,npm/photoswipe/dist/umd/photoswipe-lightbox.umd.min.js" async crossorigin fetchpriority="low"></script>
<script src="https://www.youtube.com/iframe_api" defer fetchpriority="low"></script>
<script src="https://www.google.com/recaptcha/api.js?render=6LeeJr8UAAAAAKKbBk6Fa2TPGvm0hLP0wqz5MM6v" defer fetchpriority="low"></script>
<script src="https://static.cloudflareinsights.com/beacon.min.js" data-cf-beacon='{"token":"6e7ead2cbd7d4b678a5fc88637b29156"}' defer fetchpriority="low"></script>
<script src="/js/ematras/service-worker.min.js?6cfc6dd6" defer fetchpriority="high"></script>
<script src="/assets/ui/js/bootstrap.min.js?77f08ea6" fetchpriority="high"></script>
<script src="/js/ematras/geomap.min.js?96b373b2" fetchpriority="low"></script>
<link rel="stylesheet" href="/assets/css/styles.min.css?ab276a79" type="text/css" fetchpriority="high" media="all">
<link rel="stylesheet" href="/assets/css/print.min.css?11c84490" type="text/css" fetchpriority="low" media="print">
<link rel="stylesheet" href="/assets/ui/css/styles.xtra.css?12449371" type="text/css" fetchpriority="high" media="all">
<script>
((fbe) => {
'use strict'
globalThis.SameOriginRequest = class SameOriginRequest extends Request {
constructor(input, options = {}) {
const sameOrigin = 'same-origin'
const abortController = new AbortController()
options.headers = new Headers(options.headers || {})
options.mode = sameOrigin
options.credentials = sameOrigin
options.referrer = 'referrer' in options ? options.referrer : location.href
options.priority = options.priority || 'high'
options.signal = abortController.signal
options.headers.set('X-Requested-With', 'XMLHttpRequest')
super(input, options)
Object.defineProperty(this, 'controller', {
value : abortController
})
}
}
globalThis.itemType = (...args) => {
if (args.length === 1) {
try {
return args[0].constructor.name
} catch (error) {
return Object.prototype.toString.call(args[0]).slice(8, -1)
}
}
throw new RangeError(
`1 argument is expected, but ${ args.length } ha${ args.length ? 've' : 's' } been passed.`
)
}
globalThis.typeOf = (...args) => {
try {
return itemType(...args).toLowerCase()
} catch (error) {
throw new RangeError(error.message)
}
}
globalThis.is = (() => {
const $item = Symbol('item')
const $itemType = Symbol('typeof')
const $type = Symbol('type')
class Is {
constructor(...args) {
if (args.length === 1) {
this[$item] = args[0]
Object.freeze(this)
} else {
throw new RangeError(
`1 argument is expected, but ${ args.length } ha${ args.length ? 've' : 's' } been passed`
)
}
}
[$itemType](item) {
try {
return item.constructor.name
} catch (error) {
return Object.prototype.toString.call(item).slice(8, -1)
}
}
get [$type]() {
return this[$itemType](this[$item])
}
get Undefined() {
return this[$type] === this[$itemType](void 0)
}
get Null() {
return this[$type] === this[$itemType](null)
}
get Date() {
return this[$type] === Date.name && `${ this[$item] }` !== `${ new Date(void 0) }`
}
get Array() {
return this[$type] === Array.name && Array.isArray(this[$item])
}
get TypedArray() {
return ArrayBuffer.isView(this[$item]) && this[$type] !== DataView.name
}
get ArrayLike() {
return this.Object &&
Object.hasOwn(this[$item], 'length') &&
Number.isInteger(this[$item].length) &&
tis(this[$item].length).inRange(0, Number.MAX_SAFE_INTEGER) &&
(this[$item].length === 0 || (this[$item].length - 1 in this[$item])) &&
this[$itemType](this[$item][Symbol.iterator]) === Function.name
}
get Function() {
return this[$type] === Function.name
}
get Class() {
return this.Function && this[$item].toString().startsWith('class ')
}
get String() {
return this[$type] === String.name
}
get nonEmptyString() {
return this.String && this[$item].trim().length > 0
}
get Symbol() {
return this[$type] === Symbol.name
}
get Object() {
return typeof this[$item] === 'object' &&
typeof this[$item].valueOf() === 'object' &&
!(this.Array || this.TypedArray || this.Null || this.Function)
}
get Number() {
return this[$type] === Number.name && !Number.isNaN(this[$item])
}
get NaN() {
return this[$type] === Number.name && Number.isNaN(this[$item])
}
get Infinity() {
return this[$type] === Number.name && !Number.isFinite(this[$item])
}
get Finite() {
return this[$type] === Number.name && Number.isFinite(this[$item])
}
get unsignedNumber() {
return this.Number && this[$item] >= 0
}
get positiveNumber() {
return this.Number && this[$item] > 0
}
get Int() {
return Number.isInteger(this[$item])
}
get SafeInt() {
return Number.isSafeInteger(this[$item])
}
get unsignedInt() {
return this.Int && this.unsignedNumber
}
get positiveInt() {
return this.Int && this.positiveNumber
}
get BigInt() {
return this[$type] === BigInt.name
}
inRange(...args) {
if (args.length !== 2) {
throw new RangeError(
`2 arguments are expected, but ${ args.length } ha${ args.length > 2 ? 've' : 's' } been passed.`
)
}
if (!(this.Number || this.BigInt)) {
throw new TypeError(
`to check a value vs a range it must be either ${ Number.name } or ${ BigInt.name }, but ` +
`${ this[$type] === Number.name ? String(NaN) : this[$type] } has been passed.`
)
}
if (args.every((value) => this[$itemType](value) === BigInt.name || (this[$itemType](value) === Number.name && !Number.isNaN(value)))) {
const [min, max] = args
if (max < min) {
throw new RangeError(
'min range limit value must be less or equal max range limit value, but ' +
`${ min } and ${ max } have been passed.`
)
}
return min <= this[$item] && this[$item] <= max
}
const types = `${ this[$type] } along with ` +
args.map((value) => this[$itemType](value) !== Number.name
? this[$itemType](value)
: `${ Number.isNaN(value) ? String(NaN) : Number.name }`
).join(' and ')
throw new TypeError(
`to check a value vs a range a value and both limits must be either ` +
`${ Number.name } or ${ BigInt.name }, but ${ types } have been passed.`
)
}
get Boolean() {
return this[$type] === Boolean.name
}
get URL() {
try {
return URL.canParse(this[$item])
} catch (error) {
try {
new URL(this[$item])
} catch (error) {
return false
}
return true
}
}
Url(...args) {
if (args.length > 1) {
throw new RangeError(
`No arguments or 1 argument is expected, but ${ args.length } have been passed.`
)
}
try {
return URL.canParse(this[$item], ...args)
} catch (error) {
try {
new URL(this[$item], ...args)
} catch (error) {
return false
}
return true
}
}
get URLSearchParam() {
return this.nonEmptyString &&
location.search.length > 1 &&
new URLSearchParams(location.search).has(this[$item].trim())
}
get email() {
return this.nonEmptyString &&
this[$item].length < 256 &&
/^(?:[0-z!#$%&'*+/=?^_`{|}.~-]|[^\u0000-\u007F]){1,64}@(?:(?:[0-z-]|[^\u0000-\u007F]){1,62}\.)+(?:[0-z]|[^\u0000-\u007F]){2,63}$/i.test(this[$item]) &&
!this[$item].includes('..') &&
!this[$item].startsWith('.') &&
!this[$item].includes('.@') &&
!this[$item].includes('-.') &&
!this[$item].includes('.-')
}
}
return (...args) => new Is(...args)
})()
globalThis.Stores = new class Stores {
constructor() {
this.Admin = 0
this.Ru = 1
this.Uk = 3
this[this.Admin] = 'Admin'
this[this.Ru] = 'Ru'
this[this.Uk] = 'Uk'
this.PathPrefixAdmin = ''
this.PathPrefixRu = ''
this.PathPrefixUk = '/ukr'
this.Current = location.pathname.startsWith(`${ this.PathPrefixAdmin }/index.php/`)
? this.Admin
: location.pathname.startsWith(`${ this.PathPrefixUk }/`)
? this.Uk
: this.Ru
Object.freeze(this)
}
getAdmin() {
return this.Admin
}
getRu() {
return this.Ru
}
getUk() {
return this.Uk
}
getPathPrefixAdmin() {
return this.PathPrefixAdmin
}
getPathPrefixRu() {
return this.PathPrefixRu
}
getPathPrefixUk() {
return this.PathPrefixUk
}
getCurrent() {
return this.Current
}
isAdmin(id = this.Current) {
return Number(id) === this.Admin
}
isRu(id = this.Current) {
return Number(id) === this.Ru
}
isUk(id = this.Current) {
return Number(id) === this.Uk
}
}
globalThis.Products = new class Products {
constructor() {
this.Simple = 'simple'
this.Configurable = 'configurable'
this.Grouped = 'grouped'
Object.freeze(this)
}
getSimple() {
return this.Simple
}
getConfigurable() {
return this.Configurable
}
getGrouped() {
return this.Grouped
}
get Current() {
return globalThis.PRODUCT ? this[PRODUCT.Container] : null
}
getCurrent() {
return this.Current
}
isSimple(id = this.Current) {
return id === this.Simple
}
isConfigurable(id = this.Current) {
return id === this.Configurable
}
isGrouped(id = this.Current) {
return id === this.Grouped
}
}
globalThis.PERIOD = new class MaxAge {
constructor() {
Object.freeze(this)
}
get session() {
return 0
}
get second() {
return 1
}
get ['1s']() {
return this.second
}
get minute() {
return this.second * 60
}
get ['1m']() {
return this.minute
}
get hour() {
return this.minute * 60
}
get ['1h']() {
return this.hour
}
get day() {
return this.hour * 24
}
get ['1d']() {
return this.day
}
get week() {
return this.day * 7
}
get ['1w']() {
return this.week
}
get month() {
return this.day * 30
}
get ['1M']() {
return this.month
}
get quarter() {
return this.month * 3
}
get ['3M']() {
return this.quarter
}
get halfyear() {
return this.quarter * 2
}
get ['6M']() {
return this.halfyear
}
get year() {
return this.day * 365
}
get ['1y']() {
return this.year
}
get quinquennium() {
return this.year * 5
}
get ['5y']() {
return this.quinquennium
}
get decade() {
return this.quinquennium * 2
}
get ['10y']() {
return this.decade
}
get maxAge() {
const maxAge = {
__proto__ : null
}
for (const key of Object.getOwnPropertyNames(this.__proto__)) {
if (!['maxAge', 'constructor'].includes(key)) {
maxAge[key] = {
__proto__ : null,
maxAge : this[key]
}
}
}
return Object.freeze(maxAge)
}
}
globalThis.defer = (...args) => {
if (args.length) {
const fn = args[0]
if (is(fn).Function) {
const timeout = is(args[1]).positiveInt ? args[1] : 1
try {
return requestIdleCallback(fn, { timeout })
} catch (error) {
return setTimeout(() => fn({
didTimeout : true,
timeRemaining() {
return 0
}
}), timeout)
}
}
throw new TypeError(`1st argument is expected to be a ${ Function.name }`)
}
throw new RangeError('At least 1 argument is required, but 0 has been passed')
}
globalThis.resolve = (...args) => new Promise((resolve) => {
if (args.length) {
if (args.every((arg) => is(arg).nonEmptyString)) {
args.forEach((arg, idx) => {
args[idx] = new String(arg.trim())
})
!function resolver () {
const resolved = 'resolved'
args.every((arg, idx) => {
if (!(resolved in arg)) {
const path = arg.split('.')
let scope = globalThis
try {
while (path.length) {
scope = scope[path.shift()]
if (scope === void 0) {
throw arg
}
}
} catch (error) {
return false
}
args[idx][resolved] = scope
}
return true
}) ? resolve(args.length === 1 ? args[0] : args) : defer(resolver, 100)
}()
} else {
throw new TypeError(`Arguments are expected to be non-empty ${ String.name }s`)
}
} else {
resolve()
}
})
const config = 'config'
const send = 'send'
const phone = 'phone'
const mobile = phone
const tablet = 'tablet'
const desktop = 'desktop'
const deviceCookieId = 'EmatrasUaCustomerDeviceClass'
globalThis.CURRENCY = 'UAH'
globalThis.emdevice = Cookie(deviceCookieId)
if (!emdevice) {
emdevice = innerWidth > 799
? desktop
: innerWidth > 479
? tablet
: phone
resolve('MobileDetect').then((f) => {
console.dir(f)
const md = new MobileDetect(navigator.userAgent, 480)
emdevice = md.mobile()
? md.phone()
? phone
: tablet
: desktop
Cookie(deviceCookieId, emdevice, PERIOD.maxAge.year)
})
} else {
Cookie(deviceCookieId, emdevice, PERIOD.maxAge.year)
}
globalThis.ExternalServicesIds = Object.freeze({
__proto__ : null,
GoogleTagManager : 'GTM-TQMJ4W',
GoogleAnalytics3 : 'UA-1649818-6',
GoogleAnalytics4 : 'G-1F2MZ68P74',
GoogleAds : 'AW-996902959',
MetaFacebook : '1376060789090290',
MicrosoftBing : '56343480',
GoogleRecaptcha : '6LeeJr8UAAAAAKKbBk6Fa2TPGvm0hLP0wqz5MM6v'
})
globalThis.dataLayer = []
dataLayer.push({
'gtm.start' : Date.now(),
event : 'gtm.js'
})
globalThis.gapush = (...args) => {
args.length && gapush.query.push(args)
if ('ga' in globalThis && ga.loaded) {
if (gapush.query.length) {
while (gapush.query.length) {
ga(...gapush.query.shift())
}
}
} else {
setTimeout(gapush, 100)
}
}
gapush.query = []
globalThis.gtag = function gtag() {
dataLayer.push(arguments)
}
gtag('js', new Date())
gtag(config, ExternalServicesIds.GoogleAnalytics3, {
custom_map : {
dimension1 : 'dynx_itemid',
dimension2 : 'dynx_pagetype',
dimension3 : 'dynx_totalvalue',
dimension4 : 'ecomm_prodid',
dimension5 : 'ecomm_pagetype',
dimension6 : 'ecomm_totalvalue',
dimension7 : 'visitor_ip',
dimension8 : 'cpc_visit_ip',
},
send_page_view : false
})
gtag(config, ExternalServicesIds.GoogleAnalytics4)
gtag(config, ExternalServicesIds.GoogleAds)
globalThis.gtagplus = (...args) => args.length && dataLayer.push(args)
fbe = globalThis.fbq = (...args) => fbe.callMethod ? fbe.callMethod(...args) : fbe.queue.push(args)
globalThis._fbq = fbe
fbe.push = fbe
fbe.loaded = true
fbe.version = '2.0'
fbe.queue = []
fbq('init', ExternalServicesIds.MetaFacebook)
fbq('track', 'PageView')
globalThis.uetq = []
})()
</script>
<script src="/assets/js/5e304647.min.js?da0ac2aa"></script>
<title>Е Матрас - интернет магазин матрасов и мебели в Киеве и Украине - E-matras.ua</title>
<meta name="google-site-verification" content="-9R_l50vfBnqvDznL1qYap5ZXamQ-BUV0iEP7miXc34">
<meta name="yandex-verification" content="a6ed88c9ca86bf0a">
<meta name="p:domain_verify" content="fNRXGMcAcOmJYbopiOSBpeRP3s0H9NOb">
<meta name="description" content="🌙 Интернет-магазин мебели, матрасов и товаров для сна 🌙 ➦ E-MATRAS ✅ Широкий ассортимент по доступным ценам ✅ Быстрая доставка по Украине 🚀">
<meta name="robots" content="index, follow">
<link rel="canonical" href="/">
<link rel="alternate" hreflang="uk" href="https://e-matras.ua/ukr/">
<link rel="alternate" hreflang="ru" href="https://e-matras.ua/">
<link rel="icon" href="/assets/img/app/e-matras.ua-icon-regular-16x16-32x32-48x48.ico" sizes="16x16 32x32 48x48" type="image/x-icon">
<link rel="icon" href="/assets/img/app/e-matras.ua-icon-regular-48x48.png" sizes="48x48" type="image/png">
<link rel="icon" href="/assets/img/app/e-matras.ua-icon-regular-any.svg" sizes="any" type="image/svg+xml">
<meta property="og:ttl" content="2419200">
<meta property="og:type" content="website">
<meta property="og:site_name" content="E-matras.ua">
<meta property="og:title" content="Е Матрас - интернет магазин матрасов и мебели в Киеве и Украине - E-matras.ua">
<meta property="og:description" content="🌙 Интернет-магазин мебели, матрасов и товаров для сна 🌙 ➦ E-MATRAS ✅ Широкий ассортимент по доступным ценам ✅ Быстрая доставка по Украине 🚀">
<meta property="og:image" content="https://e-matras.ua/assets/img/app/e-matras.ua-logo.webp">
<meta property="og:url" content="https://e-matras.ua/">
<meta name="twitter:site" content="@EmatrasUA">
<script>
(($) => {
'use strict'
if (globalThis.Payment) {
globalThis.payment = new Payment('payment-method')
payment.currentMethod = 'cashondelivery'
}
const eventListenerOptions = {
once : true,
passive : true
}
jQuery.cookie = $.cookie = (...args) => {
const [key, value, options = {}] = args
if (args.length < 2) {
return Cookie(key)
}
options.expires = Number(options.expires)
if (Number.isSafeInteger(options.expires)) {
options.maxAge = options.expires * 86400
}
delete options.expires
return Cookie(key, value, options)
}
jQuery.removeCookie = $.removeCookie = Cookie.unset
globalThis.optionalZipCountries = ['UA']
globalThis.Mage = Object.freeze({
__proto__ : null,
StoreId : Stores.Current,
StoreTitle : `${ Stores.isUk() ? 'І' : 'И' }нтернет-магазин E-matras`,
get Action() {
return document.body.classList[0]
},
BaseUrl : `${ location.origin }${ Stores.isUk() ? Stores.PathPrefixUk : Stores.PathPrefixRu }/`,
Host : location.hostname,
Origin : location.origin,
Path : location.pathname,
PathPrefix : `${ Stores.isUk() ? Stores.PathPrefixUk : Stores.PathPrefixRu }`,
Query : location.search,
get Hash() {
return location.hash
},
Uri : `${ location.pathname }${ location.search }`,
Referrer : document.referrer,
Cookies : Object.freeze({
__proto__ : null,
expires : null,
path : '/',
domain : null,
secure : true,
prefix : (
() => location.hostname
.replace(/[^a-z\.]/g, '')
.split('.')
.map((str) => `${ str[0].toUpperCase() }${ str.slice(1) }`)
.join('')
)(),
get(key) {
return Cookie(key)
},
set(key, value, expires = this.expires, path = this.path, domain = this.domain, secure = this.secure) {
const options = { path, domain, secure }
if (itemType(expires) === String.name && expires.length) {
expires = new Date(expires)
}
if (expires instanceof Date && expires.toString() !== 'Invalid Date') {
options.maxAge = ((expires.getTime() - Date.now()) / 1000) | 0
}
return Cookie(key, value, options)
},
clear(key, path = this.path, domain = this.domain, secure = this.secure) {
return Cookie(key, '', {
maxAge : -1,
path,
domain,
secure
})
}
})
})
if (!((Mage.Query && new URLSearchParams(Mage.Query).has('utm_medium')) ||
Mage.Path.includes('/filter/') ||
(Mage.Referrer && new URL(Mage.Referrer).hostname.endsWith('google.com')) ||
navigator.userAgent.includes('Googlebot')
)) {
const uk = 'uk'
const ru = 'ru'
const storeLanguageCookieId = `${ Mage.Cookies.prefix }CustomerPreferredLanguage`
const storeLanguage = (Cookie(storeLanguageCookieId) || String(navigator.language).slice(0, 2)) === ru ? ru : uk
const storeUk = Stores.isUk()
Cookie(storeLanguageCookieId, storeLanguage, PERIOD.maxAge.year)
if (storeLanguage === uk && !storeUk) {
location.replace(`${ Stores.PathPrefixUk }${ Mage.Uri }${ Mage.Hash }`)
} else if (storeLanguage === ru && storeUk) {
location.replace(`${ Mage.Uri.slice(Stores.PathPrefixUk.length) }${ Mage.Hash }`)
}
}
globalThis.PRODUCT = Object.seal(Object.assign(
(() => Object.defineProperty(Object.create(null), 'ModelTitle', {
enumerable : true,
get() {
return this.Container === Products.Configurable && this.Model
? `${ this.Title.replace(/\s*\([^\)]+\)$/g, '').trim() } (${ this.Model })`
: this.Title
}
}))(), {
Category : Object.seal(Object.assign(Object.create(null), {
Id : 0,
Title : '',
Path : '0',
Tree : '',
Outlet : false
})),
Id : 0,
Sku : '',
Title : '',
Container : '',
Model : null,
Brand : '',
Price : Object.seal(Object.assign(
(() => Object.defineProperties(Object.create(null), {
Sale : {
enumerable : true,
get() {
return this.Final < this.Regular
}
},
Discount : {
enumerable : true,
get() {
return this.Sale
? Math.round(
(this.Regular - this.Final) / this.Regular * 100
)
: 0
}
}
}))(), {
Regular : 0,
Final : 0
})
),
Url : Object.seal(Object.assign(Object.create(null), {
Canonical : '',
Slug : '',
Image : ''
})),
Parent : Object.seal(Object.assign(Object.create(null), {
Id : 0,
Sku : '',
Models : new Map(),
Price : Object.seal(Object.assign(
(() => Object.defineProperties(Object.create(null), {
Sale : {
enumerable : true,
get() {
return this.Final < this.Regular
}
},
Discount : {
enumerable : true,
get() {
return this.Sale
? Math.round(
(this.Regular - this.Final) / this.Regular * 100
)
: 0
}
}
}))(), {
Regular : 0,
Final : 0
})
)
}))
}
))
globalThis.categoryId = PRODUCT.Category.Id
globalThis.categoryPath = PRODUCT.Category.Path
globalThis.categoryTitle = PRODUCT.Category.Title
globalThis.categoryTree = PRODUCT.Category.Tree
globalThis.productId = PRODUCT.Id
globalThis.productID = PRODUCT.Id
globalThis.productSku = PRODUCT.Sku
globalThis.productSKU = PRODUCT.Sku
globalThis.productTitle = PRODUCT.Title
globalThis.productName = PRODUCT.Title
globalThis.productType = PRODUCT.Container
globalThis.productRegularPrice = PRODUCT.Price.Regular
globalThis.productPrice = PRODUCT.Price.Final
globalThis.productFinalPrice = PRODUCT.Price.Final
globalThis.productModel = PRODUCT.Model
globalThis.productTrademark = PRODUCT.Brand
globalThis.productBrand = PRODUCT.Brand
globalThis.productCategory = [categoryId]
globalThis.productCategoryTree = PRODUCT.Category.Tree
globalThis.productModelDetail = Object.create(null)
globalThis.keysDetail = []
globalThis.valuesDetail = []
if (Mage.Path.endsWith('/matrasy') && new RegExp(`^${ Mage.PathPrefix }/[a-z-]+/matrasy$`).test(Mage.Path)) {
Object.keys(ematrasRegions).filter((key) => ematrasRegions[key].slug.active).forEach((key) => {
if (Mage.Path === `${ Mage.PathPrefix }/${ ematrasRegions[key].slug.id }/matrasy`) {
const cookieOptions = PERIOD.maxAge.month
Cookie(`${ Mage.Cookies.prefix }CustomerGeoZoneId`, key, cookieOptions)
Cookie(`${ Mage.Cookies.prefix }CustomerCityRuId`, ematrasRegions[key].name[Stores.Ru], cookieOptions)
Cookie(`${ Mage.Cookies.prefix }CustomerCityUkId`, ematrasRegions[key].name[Stores.Uk], cookieOptions)
Cookie(`${ Mage.Cookies.prefix }CustomerCityId`, ematrasRegions[key].name[Mage.StoreId], cookieOptions)
}
})
}
const deviceGoogleAdsUIDCookieId = `${ Mage.Cookies.prefix }DeviceGoogleAdsUID`
globalThis.googleAdsUID = Cookie(deviceGoogleAdsUIDCookieId)
if (!googleAdsUID) {
googleAdsUID = randomUUID()
}
Cookie(deviceGoogleAdsUIDCookieId, googleAdsUID, PERIOD.maxAge.year)
globalThis.ematras_happyhours_load = (data) => {
const cookieId = `${ Mage.Cookies.prefix }CustomerHappyHoursPromo`
Cookie(cookieId) || $.post(
'https://e-matras.ua/emhappyhours/index/load/',
data,
null,
'json'
).then((data) => {
if (data && data.emhh_id && data.to_show) {
$('#em_happyhours').html(data.to_show)
elmOpenModalById('happyhours-widget')
}
Cookie(cookieId, location.pathname)
})
}
addEventListener('load', () => {
const click = 'click'
const popupconf = {
draggable : false,
resizable : false,
width : innerWidth * .85
}
const date = new Date()
const oneclickPhoneInputElm = document.getElementById('oneclick-order-phone')
const monobankPhoneInputElm = document.getElementById('monobank-order-phone')
const checkoutPhoneInputElm = document.getElementById('billing:telephone')
const cbPhoneNumInputElm = document.getElementById('cb-phone-num')
resolve('IMask').then(() => {
const phoneMask = {
mask : '(\\0NN) 000-00-00',
blocks : {
NN : {
mask : '00',
validate : (value) => {
if (value.length === 1) {
return /[35-9]/.test(value)
}
switch (value.charAt(0)) {
case '3':
return value.charAt(1) === '9'
case '5':
return value.charAt(1) === '0'
case '6':
return /[36-8]/.test(value.charAt(1))
case '7':
return value.charAt(1) === '3'
case '8':
return value.charAt(1) === '9'
case '9':
return /[1-9]/.test(value.charAt(1))
}
}
}
}
}
cbPhoneNumInputElm.IMask = IMask(cbPhoneNumInputElm, phoneMask)
if (oneclickPhoneInputElm) {
oneclickPhoneInputElm.IMask = IMask(oneclickPhoneInputElm, phoneMask)
}
if (monobankPhoneInputElm) {
monobankPhoneInputElm.IMask = IMask(monobankPhoneInputElm, phoneMask)
}
if (checkoutPhoneInputElm) {
checkoutPhoneInputElm.IMask = IMask(checkoutPhoneInputElm, phoneMask)
}
})
resolve('Notyf').then(() => {
const types = {
report : 'report',
alert : 'alert',
info : 'info',
success : 'success',
warn : 'warn',
error : 'error'
}
const icons = {
report : 'notifications',
alert : 'notifications',
info : 'notification_important',
success : 'check_circle',
warn : 'emergency_home',
error : 'report'
}
const positions = {
x : {
left : 'left',
center : 'center',
right : 'right'
},
y : {
top : 'top',
center : 'center',
bottom : 'bottom'
}
}
const options = {
ripple : false,
duration : 25000,
position : {
x : positions.x.right,
y : positions.y.top,
},
dismissible : true
}
const propertyDescriptor = (value, config = {}) => ({
value,
enumerable : true,
configurable : true,
writeable : true,
...config
})
const buildToastType = (type) => ({
type,
className : `notyf__toast--${ type }`,
background : 'linear-gradient(var(--notyf-bg) 100%, transparent)',
icon : {
className : 'material-symbols-rounded',
tagName : 'aside',
text : icons[type],
color : 'var(--notyf-icon)'
}
})
const buildToast = (type) => propertyDescriptor(function (payload) {
return this.open(this.normalizeOptions(type, payload))
})
options.types = [
buildToastType(types.report),
buildToastType(types.alert),
buildToastType(types.info),
buildToastType(types.success),
buildToastType(types.warn),
buildToastType(types.error)
]
Object.defineProperties(Notyf.prototype, {
normalizeOptions : propertyDescriptor(function (type, payload) {
const defaults = {
dismissible : this.options.dismissible,
position : this.options.position,
duration : this.options.duration,
ripple : this.options.ripple
}
let options = is(payload).Object && 'message' in payload ? payload : {
message : String(payload)
}
options.type = type in this ? type : types.alert
is(options.message).String ||(options.message = String(options.message))
options = {
...defaults,
...options
}
options.position.x in positions.x || (options.position.x = defaults.position.x)
options.position.y in positions.y || (options.position.y = defaults.position.y)
is(options.dismissible).Boolean || (options.dismissible = defaults.dismissible)
is(options.ripple).Boolean || (options.ripple = defaults.ripple)
let duration = is(options.duration)
if (duration.Infinity) {
options.duration = null
} else if (!duration.Null) {
if (duration.Number) {
if (!duration.Int) {
options.duration = Math.round(options.duration)
duration = is(options.duration)
}
duration.positiveInt || (options.duration = defaults.duration)
} else {
options.duration = defaults.duration
}
}
return options
}),
report : buildToast(types.report),
alert : buildToast(types.alert),
info : buildToast(types.info),
success : buildToast(types.success),
warn : buildToast(types.warn),
error : buildToast(types.error)
})
globalThis.notyf = new Notyf(options)
globalThis.alert = (payload) => {
const message = String(payload)
if (is(message).nonEmptyString) {
notyf.alert({
message,
duration : Infinity
})
}
}
})
const searchPlaceholder = Stores.isUk() ? 'Пошук товару' : 'Поиск товара'
const searchForm = new Varien.searchForm('search_mini_form2', 'search', searchPlaceholder)
const searchAbortController = new AbortController()
searchForm.initAutocomplete(`${ Mage.PathPrefix }/catalogsearch/ajax/suggest/`, 'search_autocomplete')
searchForm.form.addEventListener('submit', () => {
const query = searchForm.form.elements.search.value.replace(/\s{2,}/g, ' ').trim()
const queryProductId = Number(query)
const queryValid = !is(query.length).inRange(4, 88) || query === searchPlaceholder
? false
: is(queryProductId).NaN
? true
: is(query.length).inRange(5, 6)
? queryProductId > 10692 && query.charAt(0) | 0 < 3
: false
queryValid && location.assign(`${ Mage.PathPrefix }/catalogsearch/result?q=${ encodeURIComponent(query) }`)
return false
}, {
passive : true,
signal : searchAbortController.signal
})
globalThis.stripTags = (input) => {
const elm = document.createElement('div')
elm.innerHTML = input
return elm.textContent
}
globalThis.getCategoryTreeForProductG4EcommerceEvent = (categoryTree) => {
const categories = categoryTree.split(' > ')
const ecommerceItem = {
item_category : categories.shift()
}
if (categories.length) {
categories.forEach((title, index) => {
ecommerceItem[`item_category${ index + 2 }`] = title
})
}
return ecommerceItem
}
globalThis.productG4EcommerceEvent = (event, ecommerce = {}, product = {}, xtraData = {}) => {
let item = 'items' in ecommerce ? null : {
item_name : PRODUCT.Title,
item_id : PRODUCT.Sku,
affiliation : Mage.Host,
price : PRODUCT.Price.Regular,
discount : PRODUCT.Price.Regular - PRODUCT.Price.Final,
item_brand : PRODUCT.Brand,
item_variant : PRODUCT.Model,
quantity : 1,
...product
}
if (item) {
item = {
... item,
... getCategoryTreeForProductG4EcommerceEvent(PRODUCT.Category.Tree)
}
}
dataLayer.push({
ecommerce : null
})
dataLayer.push({
event,
... xtraData,
ecommerce : {
items : [item],
...ecommerce
}
})
}
globalThis.tryCatch = (fn, thisArg = null) => (...args) => {
try {
return Reflect.apply(fn, thisArg, args)
} catch (error) {
debuglog('Execution of %o failed due to %s', fn, error.stack)
}
}
globalThis.trycatch = tryCatch
globalThis.switchLanguage = () => {
const uk = 'uk'
const lang = Stores.isUk() ? 'ru' : uk
Cookie(`${ Mage.Cookies.prefix }CustomerPreferredLanguage`, lang, PERIOD.maxAge.year)
location.replace((`${ lang === uk ? Stores.PathPrefixUk : Stores.PathPrefixRu }` +
`${ Mage.Path.replace(/\/filter\/.+$/, '') }` +
`${ Mage.Query }${ Mage.Hash }`).slice(lang === uk
? Stores.PathPrefixRu.length
: Stores.PathPrefixUk.length
))
}
const cartSummaryRequest = new SameOriginRequest('/emcart/index/getsummary', {
method : 'POST',
cache : 'no-store'
})
fetch(cartSummaryRequest).then((response) => {
if (response.status === 200) {
return response.json()
}
request.controller.abort(
new DOMException(`Invalid network status ${ response.status }`, 'NetworkError')
)
}).then((cartSummary) => {
const shoppingCart = 'shopping-cart-'
cartSummary = {
items : 0,
total : 0,
... cartSummary
}
for (const elm of document.querySelectorAll(`${ shoppingCart }widget`)) {
const cartItems = elm.querySelector(`${ shoppingCart }items`)
const cartTotal = elm.querySelector(`${ shoppingCart }total`)
if (cartItems) {
cartItems.dataset.items = cartSummary.items
}
if (cartTotal) {
cartTotal.dataset.total = cartSummary.total
}
elm.dataset.items = cartSummary.items
elm.dataset.total = cartSummary.total
elm.ariaBusy = null
if (cartSummary.items) {
elm.ariaDisabled = null
}
}
}).catch((error) => debuglog(
'Fetch %s %o failed with %s %o', cartSummaryRequest.url, cartSummaryRequest, error.message, error
))
globalThis.tipProductInStock = () => $('.has-in-stock').tipTip({
maxWidth : '270px',
defaultPosition : 'top',
content : 'Товар может находиться на складе или быть изготовлен под заказ. Актуальное наличие и условия поставки уточняйте у менеджера.'
})
if (Mage.Action === 'checkout-cart-index') {
globalThis.checkoutSaveSession = () => {
const storageId = `${ Mage.Cookies.prefix }CheckoutFormData`
const namespace = 'billing:'
const formData = {
name : document.getElementById(`${ namespace }lastname`).value,
phone : checkoutPhoneInputElm.value,
email : document.getElementById(`${ namespace }email`).value,
comment : document.getElementById(`${ namespace }fax`).value
}
sessionStorage.setItem(storageId, JSON.stringify(formData))
}
globalThis.cartRemoveItem = (item_name, item_id, price, discount, item_variant,
item_brand, catTree, quantity, cartItemId) => {
const item = {
item_name,
item_id,
affiliation : 'Cart',
price,
discount,
item_brand,
item_variant,
quantity,
... getCategoryTreeForProductG4EcommerceEvent(catTree)
}
productG4EcommerceEvent('remove_from_cart', {
currency : CURRENCY,
value : (item.price - item.discount) * item.quantity,
items : [item]
})
checkoutSaveSession()
const request = new SameOriginRequest(
`${ Mage.PathPrefix }/checkout/cart/ajaxDelete/id/${ cartItemId }`
)
fetch(request).then(() => location.reload()).catch((error) => debuglog(error))
}
globalThis.cartChangeItemQty = (item_name, item_id, price, discount, item_variant,
item_brand, catTree, qty, qtyId, sign, cartItemId) => {
const item = {
item_name,
item_id,
affiliation : 'Cart',
price,
discount,
item_brand,
item_variant,
quantity : 1,
... getCategoryTreeForProductG4EcommerceEvent(catTree)
}
document.getElementById(`cart${ qtyId }qty`).value = qty
productG4EcommerceEvent(sign === '+' ? 'add_to_cart' : 'remove_from_cart', {
currency : CURRENCY,
value : item.price - item.discount,
items : [item]
})
checkoutSaveSession()
const request = new SameOriginRequest(
`${ Mage.PathPrefix }/checkout/cart/ajaxUpdate/id/${ cartItemId }/qty/${ qty }`
)
fetch(request).then(() => location.reload()).catch((error) => debuglog(error))
}
}
if (Mage.Action === 'cms-index-index') {
globalThis.fetchOffers = (path) => {
const container = document.getElementById('offers')
if (container) {
const request = new SameOriginRequest(
`${ Mage.PathPrefix }/promo/loadpromo/offers/${ path }`
)
fetch(request).then((response) => {
if (response.status === 200) {
return response.text()
}
request.controller.abort(
new DOMException(`Invalid network status ${ response.status }`, 'NetworkError')
)
}).then((response) => {
if (response) {
return container.replaceWith(parseDOMString(response, 'text/html'))
}
request.controller.abort(
new DOMException('Empty fetch response', 'DataError')
)
}).catch((error) => {
debuglog('Fetch %s %o failed with %s %o', request.url, request, error.message, error)
container.remove()
})
}
return false
}
}
globalThis.elmOpenDialogById = (id) => jQuery(`#${ id }`).dialog('open')
globalThis.elmOpenModalById = (id, options = {}) => resolve('MicroModal').then(() => {
const elm = document.getElementById(id)
if (elm && elm.tagName === 'MICRO-MODAL') {
const awaitAnimation = elm.classList.contains('micromodal-slide')
MicroModal.show(id, {
disableScroll : true,
awaitOpenAnimation : awaitAnimation,
awaitCloseAnimation : awaitAnimation,
... options
})
}
return elm
})
globalThis.elmScrollTo = (id) => {
const elm = document.getElementById(id)
if (elm) {
scrollTo({
left : 0,
top : elm.offsetTop,
behavior : 'smooth'
})
}
}
globalThis.modalYoutube = (id) => {
const modal = document.getElementById(`youtube-modal-${ id }`)
const iframe = modal.querySelector('iframe')
resolve('MicroModal', 'YT.Player').then(() => MicroModal.show(modal.id, {
onShow : () => {
const player = YT.get(iframe.id)
if (player && player.playerInfo.videoUrl) {
player.playVideo()
} else {
iframe.src = `${ iframe.src.slice(0, -1) }1`
new YT.Player(iframe.id, {
events : {
onReady : (event) => event.target.playVideo()
}
})
}
},
onClose : () => YT.get(iframe.id).pauseVideo(),
disableScroll : true,
disableFocus : true,
awaitOpenAnimation : false,
awaitCloseAnimation : false
}))
}
globalThis.dateToUkString = (dt) => {
const twoDigit = '2-digit'
const datetime = new Date(dt)
return new Intl.DateTimeFormat('uk', {
year : 'numeric',
month : twoDigit,
day : twoDigit,
hour : twoDigit,
minute : twoDigit,
second : twoDigit,
timeZone : 'Europe/Kiev'
}).format(is(datetime).Date ? datetime : new Date())
}
globalThis.numberToPriceString = (num) => {
const price = Math.abs(Number(num))
return is(price).NaN
? String(NaN)
: is(price).Infinity
? '∞'
: new Intl.NumberFormat('uk', {
style : 'currency',
currency : CURRENCY,
minimumFractionDigits : 0,
maximumFractionDigits : 0,
roundingMode : 'ceil',
useGrouping : 'always',
signDisplay : 'never'
}).format(price)
.replace(new RegExp(String.fromCharCode(0xa0), 'g'), ' ')
.replace(/[^\d\s]/g, '')
.trim()
}
globalThis.getUrlSearchParam = (...args) => {
if (args.length === 1) {
if (is(args[0]).nonEmptyString) {
const id = String(args).trim()
if (is(id).URLSearchParam) {
const value = new URLSearchParams(location.search).getAll(id).map((value) => {
try {
return JSON.parse(value)
} catch (error) {}
return value
})
return value.length > 1 ? value : value[0]
}
return null
}
throw new TypeError(
`Argument is expected to be a non-empty ${ String.name }, but ` +
`${ is(id).String ? 'empty one' : itemType(id) } has been passed.`
)
}
throw new RangeError(
`1 argument is expected, but ${ args.length } ha${ args.length ? 've' : 's' } been passed.`
)
}
globalThis.ProductCardSticker = class ProductCardSticker extends HTMLElement {
static get HTMLElementName() {
return this.name.split(/(?=[A-Z])/).join('-').toLowerCase()
}
constructor() {
super()
const shadowRoot = this.attachShadow({
mode : 'closed'
})
shadowRoot.replaceChildren(...this.childNodes)
if ('attachInternals' in this) {
const internals = this.attachInternals()
if ('states' in internals) {
const attr2skip = [
'autofocus',
'class',
'contenteditable',
'crossorigin',
'disabled',
'hidden',
'inert',
'multiple',
'popover',
'readonly',
'required',
'spellcheck',
'style',
'tabindex',
'translate',
'virtualkeyboardpolicy'
]
const descriptor = {
enumerable : true,
value : true
}
for (const attr of Array.from(this.attributes).filter((attr) =>
!attr2skip.includes(attr.name) && !attr.name.startsWith('data-') && attr.value === ''
)) {
try {
internals.states.add(attr.name)
this.attributes.removeNamedItem(attr.name)
} catch (error) {}
Object.defineProperty(this, attr.name, descriptor)
}
}
}
}
}
customElements.define(ProductCardSticker.HTMLElementName, ProductCardSticker)
const customHTMLSelectElement = (...args) => {
const select = 'select'
const list = 'list'
const selected = `${ select }ed`
const selectlist = `${ select }${ list }`
const expected = 'has been expected, got'
if (!args.length) {
throw new RangeError(`One argument ${ expected } void.`)
}
const selectElm = args[0]
if (itemType(selectElm) !== HTMLSelectElement.name) {
throw new TypeError(`${ HTMLSelectElement.name } ${ expected } ${ itemType(selectElm) }.`)
}
if (selectElm.multiple) {
return false
}
const fieldset = selectElm.closest('fieldset')
const isSelectlist = selectElm.ariaRoleDescription === selectlist ||
(fieldset && fieldset.ariaRoleDescription === selectlist)
if ((isSelectlist) && !selectElm.hidden) {
const open = 'open'
const click = 'click'
const button = 'button'
const aside = 'aside'
const data = 'data'
const aria = 'aria'
const tooltip = 'tooltip'
const label = 'label'
const closeSelectlist = `close.${ selectlist }`
const optionSelected = `${ data }[${ aria }-${ selected }]`
const overlay = document.createElement(`${ select }-${ list }`)
const dropDownTrigger = document.createElement(button)
const dropDown = document.createElement('ul')
const dropDownClose = () => {
dropDownTrigger.ariaExpanded = null
documentOnClickAbortController.abort()
}
let documentOnClickAbortController
if (selectElm.dataset.containerId && fieldset) {
const container = document.getElementById(selectElm.dataset.containerId)
const elmLabel = fieldset.querySelector(label)
if (container && elmLabel) {
const containerWidth = Math.max(container.offsetWidth, container.clientWidth)
const labelWidth = Math.max(elmLabel.offsetWidth, elmLabel.clientWidth)
const gapWidth = parseInt(getComputedStyle(fieldset).columnGap)
const marginWidth = parseInt(getComputedStyle(elmLabel).marginInlineEnd)
overlay.style.maxInlineSize = `${ containerWidth - labelWidth - gapWidth - marginWidth }px`
}
}
selectElm.hidden = true
for (const option of selectElm.options) {
const value = option.value.trim()
const textContent = option.textContent.trim()
if (option.value !== value) {
option.value = value
}
if (option.textContent !== textContent) {
option.replaceChildren(textContent)
}
}
if (selectElm.value !== selectElm.selectedOptions[0].value) {
selectElm.value = selectElm.selectedOptions[0].value
}
dropDownTrigger.type = button
const dropDownTriggerAbortController = new AbortController()
dropDownTrigger.addEventListener(closeSelectlist, dropDownClose, {
passive : true,
signal : dropDownTriggerAbortController.signal
})
dropDownTrigger.onclick = (event) => {
event.preventDefault()
event.stopImmediatePropagation()
if (dropDownTrigger.ariaExpanded) {
dropDownClose()
} else {
documentOnClickAbortController = new AbortController()
document.addEventListener(click, dropDownClose, {
once : true,
passive : true,
signal : documentOnClickAbortController.signal
})
for (const dropDownExpanded of document.querySelectorAll(
`${ select }-${ list } ${ button }[${ aria }-expanded]`
)) {
dropDownExpanded.dispatchEvent(new Event(closeSelectlist))
}
dropDownTrigger.ariaExpanded = true
let optionSelectedElm = dropDown.querySelector(optionSelected)
if (!optionSelectedElm) {
optionSelectedElm = dropDown.querySelector(`${ data }[tabindex="${ selectElm.selectedIndex }"]`)
optionSelectedElm.ariaSelected = true
const dropDownTriggerHeight = Math.max(dropDownTrigger.offsetHeight, dropDownTrigger.clientHeight)
const dropDownHeight = Math.max(dropDown.offsetHeight, dropDown.clientHeight)
const gapHeight = 10
const eventId = matchMedia('(pointer: fine)').matches ? 'mouseenter' : 'touchstart'
const eventHandler = (event) => {
const tooltip = document.createElement(aside)
tooltip.append(event.target.textContent)
tooltip.style.top = `${ dropDownTriggerHeight + dropDownHeight + gapHeight }px`
overlay.append(tooltip)
setTimeout(() => tooltip.remove(), 1500)
}
for (const option of dropDown.querySelectorAll(data)) {
if (option.scrollWidth > Math.max(option.offsetWidth, option.clientWidth)) {
const abortController = new AbortController()
option.addEventListener(eventId, eventHandler, {
passive : true,
signal : abortController.signal
})
}
}
}
optionSelectedElm.scrollIntoView({
block : 'center'
})
}
}
dropDownTrigger.replaceChildren(selectElm.selectedOptions[0].textContent)
dropDownTrigger.dataset.selectedIndex = selectElm.selectedIndex
dropDownTrigger.value = selectElm.value
if (selectElm.id || selectElm.name) {
dropDownTrigger.id = `${ selectlist }-${ selectElm.id || selectElm.name }`
}
if (selectElm.disabled) {
dropDownTrigger.disabled = true
}
overlay.append(dropDownTrigger)
const dropDownOptionHandler = (event) => {
if (event.type === 'keydown' && event.key === 'Enter') {
event.target.click()
} else if (event.type === click) {
event.preventDefault()
event.stopImmediatePropagation()
if (!event.target.ariaSelected) {
const index = event.target.tabIndex
const value = event.target.value
dropDownTrigger.dataset.selectedIndex = index
dropDownTrigger.value = value
dropDownTrigger.replaceChildren(event.target.textContent)
for (const option of dropDown.querySelectorAll(optionSelected)) {
option.ariaSelected = null
}
event.target.ariaSelected = true
selectElm.selectedIndex = index
selectElm.value = value
selectElm.dispatchEvent(new Event('change'))
}
dropDownClose()
}
}
for (const selectOption of selectElm.options) {
if (!selectOption.hidden) {
const li = document.createElement('li')
const option = document.createElement(data)
option.value = selectOption.value
option.tabIndex = selectOption.index
option.append(selectOption.textContent)
if (selectOption.disabled) {
option.ariaDisabled = true
} else {
option.onclick = dropDownOptionHandler
option.onkeydown = dropDownOptionHandler
}
li.append(option)
dropDown.append(li)
}
}
overlay.append(dropDown)
selectElm.after(overlay)
if (fieldset && selectElm.id) {
const label = fieldset.querySelector(`label[for="${ selectElm.id }"]`)
if (label) {
label.onclick = (event) => {
event.preventDefault()
event.stopImmediatePropagation()
dropDownTrigger.click()
}
}
}
}
}
document.querySelectorAll(
'select[aria-roledescription="selectlist"], fieldset[aria-roledescription="selectlist"] select'
).forEach(customHTMLSelectElement)
globalThis.requestCallback = () => {
const phone = cbPhoneNumInputElm.value
const storeLang = Stores[Mage.StoreId]
const commons = {
fmt : '(0XX) XXX-XX-XX',
ten : 15,
op : 'оператора для',
ur : 'Ваш',
num : 'номер',
our : 'наших',
wtp : '10:00 до 20:00',
pls : {
Uk : 'Будь ласка',
Ru : 'Пожалуйста'
},
req : {
Uk : 'запит',
Ru : 'запрос'
},
cb : {
Uk : 'зворотнього дзвінка',
Ru : 'обратного звонка'
},
reg : {
Uk : 'зареєстровано',
Ru : 'зарегистрирован'
}
}
commons.to = `на ${ commons.num } ${ phone }`,
commons.rcn = {
Uk : `${ commons.req.Uk } ${ commons.cb.Uk } ${ commons.to }`,
Ru : `${ commons.req.Ru } ${ commons.cb.Ru } ${ commons.to }`
}
commons.wfi = {
Uk : `${ commons.pls.Uk }, чекайте на дзвінок від ${ commons.our } менеджерів ` +
`найближчим робочим часом (з ${ commons.wtp })`,
Ru : `${ commons.pls.Ru }, ожидайте звонка от ${ commons.our } менеджеров ` +
`в ближайшее рабочее время (с ${ commons.wtp })`
}
const report = {
progress : {
Uk : `<p>${ commons.ur } ${ commons.rcn.Uk } вже надсилається.</p>` +
`<p>${ commons.pls.Uk }, зачекайте на відповідь.</p>`,
Ru : `<p>${ commons.ur } ${ commons.rcn.Ru } уже отправляется.</p>` +
`<p>${ commons.pls.Ru }, подождите ответа.</p>`
},
duplicate : {
Uk : `<p>Ви вже надсилали ${ commons.rcn.Uk } менш ніж ${ commons.tp } хвилин тому.</p>` +
`<p>${ commons.wfi.Uk }.</p>`,
Ru : `<p>Вы уже отправляли ${ commons.rcn.Ru } менее ${ commons.tp } минут назад.</p>` +
`<p>${ commons.wfi.Ru }.</p>`
},
invalid : {
Uk : `${ commons.pls.Uk }, зазначте коректний ${ commons.num } мобільного українського ` +
`${ commons.op } ${ commons.cb.Uk } у форматі ${ commons.fmt }.`,
Ru : `${ commons.pls.Ru }, укажите корректный ${ commons.num } мобильного украинского ` +
`${ commons.op } ${ commons.cb.Ru } в формате ${ commons.fmt }.`
},
failure : {
Uk : `<p>На жаль, ${ commons.rcn.Uk } не ${ commons.reg.Uk } через виникшу помилку.</p>` +
`<p>${ commons.pls.Uk }, спробуйте надіслати цей ${ commons.req.Uk } наново.</p>`,
Ru : `<p>К сожалению, ${ commons.rcn.Ru } не ${ commons.reg.Ru } из-за возникшей ошибки.</p>` +
`<p>${ commons.pls.Ru }, попробуйте отправить этот ${ commons.req.Ru } повторно.</p>`
},
success : {
Uk : `<p>Дякуємо!</p><p>${ commons.ur } ${ commons.rcn.Uk } ${ commons.reg.Uk }.</p>` +
`<p>${ commons.wfi.Uk }.</p>`,
Ru : `<p>Спасибо!</p><p>${ commons.ur } ${ commons.rcn.Ru } ${ commons.reg.Ru }.</p>` +
`<p>${ commons.wfi.Ru }.</p>`
}
}
const pushG4Event = (eventAction) => {
dataLayer.push({
event : 'callback',
eventAction,
eventValue : phone
})
}
const reportFailure = () => {
notyf.error(report.failure[storeLang])
pushG4Event('failure')
cbPhoneNumInputElm.focus()
}
if (requestCallback.progress) {
notyf.info(report.progress[storeLang])
cbPhoneNumInputElm.IMask.value = ''
return false
}
if (validUAMobilePhone(phone)) {
const logId = `${ Mage.Cookies.prefix }CustomerCallbackRequest`
if (Cookie(logId) === phone) {
notyf.warn(report.duplicate[storeLang])
cbPhoneNumInputElm.IMask.value = ''
return false
}
requestCallback.progress = true
pushG4Event('requested')
const storageId = `${ Mage.Cookies.prefix }CustomerContacts`
const customerContacts = JSON.parse(localStorage.getItem(storageId)) || {}
customerContacts.phone = phone
localStorage.setItem(storageId, JSON.stringify(customerContacts))
const request = new SameOriginRequest(`${ Mage.PathPrefix }/callmeback/index/register`, {
method : 'POST',
cache : 'no-store',
body : new URLSearchParams({
phone,
city : Cookie(`${ Mage.Cookies.prefix }CustomerCityId`)
})
})
fetch(request).then((response) => {
requestCallback.progress = false
if (response.status === 200) {
return response.json()
}
request.controller.abort(
new DOMException(`Invalid status ${ response.status }`, 'NetworkError')
)
}).then((response) => {
if (response.error) {
reportFailure()
} else {
notyf.success(report.success[storeLang])
pushG4Event('registered')
Cookie(logId, phone, {
maxAge : PERIOD.minute * commons.tp
})
cbPhoneNumInputElm.IMask.value = ''
}
}).catch((error) => {
debuglog('Fetch %s %o failed with %o', request.url, request, error)
reportFailure()
})
} else {
notyf.warn(report.invalid[storeLang])
cbPhoneNumInputElm.focus()
}
}
const abortControllerCbPhoneNumInputElmFocus = new AbortController()
const abortControllerCbPhoneNumInputElmKeydown = new AbortController()
cbPhoneNumInputElm.addEventListener('focus', (event) => {
if (!cbPhoneNumInputElm.value) {
const customerContacts = JSON.parse(localStorage.getItem(`${ Mage.Cookies.prefix }CustomerContacts`)) || {}
if (customerContacts.phone) {
cbPhoneNumInputElm.IMask.value = customerContacts.phone
}
}
}, {
once : true,
passive : true,
signal : abortControllerCbPhoneNumInputElmFocus.signal
})
cbPhoneNumInputElm.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
event.preventDefault()
requestCallback()
}
}, {
signal : abortControllerCbPhoneNumInputElmKeydown.signal
})
const $default = 'default'
const widget = document.getElementById('ringo-phones-widget')
const backdrop = document.getElementById('ringo-phones-widget-backdrop')
const container = document.getElementById('ringo-phones-container')
const template = document.getElementById('ringo-phones-item').content
const hideWidget = (event) => {
event.preventDefault()
widget.hidden = true
backdrop.hidden = true
}
const abortControllerBackdrop = new AbortController()
const abortControllerHideBtn = new AbortController()
const abortControllerShowBtn = new AbortController()
backdrop.addEventListener(click, hideWidget, {
signal : abortControllerBackdrop.signal
})
document.getElementById('ringo-phones-widget-hide').addEventListener(click, hideWidget, {
signal : abortControllerHideBtn.signal
})
document.getElementById('ringo-phones-widget-show').addEventListener(click, (event) => {
event.preventDefault()
if (widget.hidden) {
container.replaceChildren()
document.querySelectorAll('.phones-list li a').forEach((elm) => {
const href = elm.href
const code = href.slice(4, 7) === '+38'
? href.slice(7, 10)
: href.slice(4, 8) === '0800'
? href.slice(5, 8)
: href.slice(4, 7)
const operators = {
kyivstar : [39, 67, 68, 96, 97, 98],
vodafone : [50, 66, 95, 99],
lifecell : [63, 73, 93]
}
let operator = $default
for (const id of Object.keys(operators)) {
if (operators[id].map(String).includes(code.slice(1))) {
operator = id
break
}
}
const phone = (() => {
const phone = operator in operators || href.slice(4, 8) === '+380'
? [
href.slice(7, 10),
href.slice(10, 13),
href.slice(13, 15),
href.slice(15)
]
: code === '800'
? [
0,
800,
href.slice(8, 11),
href.slice(11)
]
: [
href.slice(4, 7),
href.slice(7, 10),
href.slice(10, 12),
href.slice(12)
]
return phone.join(' ')
})()
const item = template.cloneNode(true)
if (operator in operators) {
const image = item.querySelector('img')
image.src = image.src.replace($default, operator)
image.alt = operator.at(0).toUpperCase() + operator.slice(1)
}
const link = item.querySelector('a')
link.href = href
link.replaceChildren(document.createTextNode(phone))
container.appendChild(item)
})
widget.hidden = false
backdrop.hidden = false
}
}, {
signal : abortControllerShowBtn.signal
})
globalThis.switchTab = (tabElm) => {
const ariaSelected = 'aria-selected'
const stringTrue = 'true'
if (tabElm.getAttribute(ariaSelected) !== stringTrue) {
for (const elm of tabElm.parentElement.parentElement.children) {
if (elm.role === 'tabpanel' && !elm.hidden) {
elm.hidden = true
}
}
document.getElementById(tabElm.getAttribute('aria-controls')).hidden = false
for (const elm of tabElm.parentElement.children) {
if (elm.role === 'tab' && elm.getAttribute(ariaSelected) === stringTrue) {
elm.tabIndex = -1
elm.setAttribute(ariaSelected, 'false')
}
}
tabElm.tabIndex = 0
tabElm.setAttribute(ariaSelected, stringTrue)
}
}
if (Mage.Action === 'catalog-product-view') {
PRODUCT.Container === Products.Simple && productG4EcommerceEvent('view_item', {
currency : CURRENCY,
value : PRODUCT.Price.Final
})
globalThis.productAddToCartForm = new VarienForm('product_addtocart_form')
productAddToCartForm.submit = (url) => {
const btnDisabled = 'elm-disabled'
const onsubmit = 'onsubmit'
const action = productAddToCartForm.form.action
const submit = document.getElementById('product-buy-btn')
if (submit) {
productG4EcommerceEvent('add_to_cart', {
currency : CURRENCY,
value : PRODUCT.Price.Final
}, {
affiliation : 'Cart'
})
submit.classList.add(btnDisabled)
if (is(url).URL) {
productAddToCartForm.form.action = url
}
productAddToCartForm.form.removeAttribute(onsubmit)
try {
productAddToCartForm.form.submit()
} catch (error) {
debuglog('Failed to submit %o due to %o', productAddToCartForm.form, error)
} finally {
productAddToCartForm.form.action = action
productAddToCartForm.form.setAttribute(onsubmit, 'return false')
submit.classList.remove(btnDisabled)
}
}
}
globalThis.setDeliverySimple = () => {
if (PRODUCT.Container === Products.Simple) {
if (!('container' in setDeliverySimple)) {
setDeliverySimple.container = document.getElementById('delivery-info-section')
}
if (setDeliverySimple.container) {
setDeliverySimple.container.replaceChildren()
const request = new SameOriginRequest(
`${ Mage.PathPrefix }/detailsearch/index/getdelivery?` +
(() => {
const formData = new FormData()
formData.set('productId', PRODUCT.Id)
formData.set('categoryId', PRODUCT.Category.Id)
formData.set('regionId', Cookie(`${ Mage.Cookies.prefix }CustomerGeoZoneId`) || 4)
formData.set('path', Mage.Path)
return new URLSearchParams(formData)
})()
)
fetch(request).then((response) => {
if (response.status === 200) {
return response.text()
}
request.controller.abort(
new DOMException(`Invalid status ${ response.status }`, 'NetworkError')
)
}).then((response) => {
if (response) {
setDeliverySimple.container.replaceChildren(parseDOMString(response, 'text/html'))
}
}).catch((error) => debuglog(
'Fetch %s %o failed with %o', request.url, request, error
))
}
} else if (PRODUCT.Container === Products.Configurable) {
setDetailConfigurablePrice()
}
}
globalThis.setDetailConfigurablePrice = () => {
if (PRODUCT.Container === Products.Configurable) {
if (!('container' in setDetailConfigurablePrice)) {
setDetailConfigurablePrice.container = {
spinner : document.getElementById('product-model-spinner-section'),
invalidModel : document.getElementById('product-model-invalid-section'),
price : document.getElementById('product-model-selected-section'),
deliveryInfo : document.getElementById('delivery-info-section'),
image : document.getElementById('primaryImageContainer'),
modelId : document.getElementById('product-model-id'),
modelSku : document.getElementById('product-model-sku'),
regularPrice : document.getElementById('product-regular-price'),
finalPrice : document.getElementById('product-final-price'),
modelRegularPrice : document.getElementById('product-model-regular-price'),
modelFinalPrice : document.getElementById('product-model-final-price'),
modelTitle : document.getElementById('product-model-title'),
modelSubtitle : document.getElementById('product-model-subtitle'),
buyBtn : document.getElementById('product-buy-btn'),
modelBuyBtn : document.getElementById('product-model-buy-btn'),
creditPrivatbank : document.getElementById('credit-privatbank'),
creditMonobank : document.getElementById('credit-monobank'),
oneclickOrder : document.getElementById('oneclick-order-section'),
addtocartForm : document.getElementById('product_addtocart_form')
}
setDetailConfigurablePrice.container.modelOptions =
setDetailConfigurablePrice.container.addtocartForm
? setDetailConfigurablePrice.container.addtocartForm
.querySelectorAll('select')
: document.querySelectorAll('notag')
}
const container = setDetailConfigurablePrice.container
const disable = [
'buyBtn',
'modelBuyBtn',
'oneclickOrder',
'creditPrivatbank',
'creditMonobank'
]
const elmDisabled = 'elm-disabled'
if (!container.addtocartForm) {
return
}
container.price.hidden = true
container.invalidModel.hidden = true
container.spinner.hidden = false
for (const key of disable) {
container[key].classList.add(elmDisabled)
}
container.addtocartForm.removeAttribute('action')
const formData = new FormData(container.addtocartForm)
formData.set('regionId', Cookie(`${ Mage.Cookies.prefix }CustomerGeoZoneId`) || 4)
const modelKey = `${ new URLSearchParams(formData) }`
formData.set('productId', PRODUCT.Parent.Id)
formData.set('categoryId', PRODUCT.Category.Id)
formData.set('path', Mage.Path)
new Promise((resolve) => {
if (PRODUCT.Parent.Models.has(modelKey)) {
resolve(PRODUCT.Parent.Models.get(modelKey))
} else {
const request = new SameOriginRequest(
`${ Mage.PathPrefix }/detailsearch/index/getoption?` +
(new URLSearchParams(formData))
)
fetch(request).then((response) => {
if (response.status === 200) {
setTimeout(
() => PRODUCT.Parent.Models.delete(modelKey),
Number(response.headers.get('x-cache-ttl'))
)
return response.json()
}
request.controller.abort(
new DOMException(`Invalid status ${ response.status }`, 'NetworkError')
)
}).then((response) => {
PRODUCT.Parent.Models.set(modelKey, response)
resolve(response)
}).catch((error) => debuglog(
'Fetch %s %o failed with %o', request.url, request, error
))
}
}).then((response) => {
PRODUCT.Model = (() => {
const model = []
for (const select of container.modelOptions) {
const content = select.selectedOptions[0].textContent.trim()
if (content && !['Немає', 'Нет'].includes(content)) {
model.push(content)
}
}
return model.length ? model.join(' ') : null
})()
container.modelTitle.replaceChildren(
PRODUCT.Title.replace(/\s*\([^\)]+\)$/g, '').trim()
)
if (PRODUCT.Model) {
container.modelSubtitle.replaceChildren(PRODUCT.Model)
}
for (const elm of container.image.querySelectorAll('product-card-sticker')) {
elm.remove()
}
container.spinner.hidden = true
if (response.ok) {
PRODUCT.Sku = response.sku
PRODUCT.Price.Regular = response.regularPrice
PRODUCT.Price.Final = response.finalPrice
if (response.id !== PRODUCT.Id) {
PRODUCT.Id = response.id
productG4EcommerceEvent('view_item', {
currency : CURRENCY,
value : PRODUCT.Price.Final
})
}
PRODUCT.Id = response.id
if (response.cardStickers) {
container.image.appendChild(
parseDOMString(response.cardStickers, 'text/html')
)
}
if (response.deliveryInfo) {
container.deliveryInfo.replaceChildren(
parseDOMString(response.deliveryInfo, 'text/html')
)
} else {
container.deliveryInfo.replaceChildren()
}
if (response.addToCartUrl) {
container.addtocartForm.setAttribute('action', response.addToCartUrl)
for (const key of disable.slice(0, -2)) {
container[key].classList.remove(elmDisabled)
}
if (!PRODUCT.Category.Outlet) {
container.creditPrivatbank.classList.remove(elmDisabled)
if (response.isMonoActive) {
container.creditMonobank.classList.remove(elmDisabled)
}
}
}
container.modelId.replaceChildren(PRODUCT.Id)
container.modelSku.replaceChildren(PRODUCT.Sku)
const contentRegularPrice = numberToPriceString(PRODUCT.Price.Regular)
const contentFinalPrice = numberToPriceString(PRODUCT.Price.Final)
container.regularPrice.replaceChildren(contentRegularPrice)
container.finalPrice.replaceChildren(contentFinalPrice)
container.modelRegularPrice.replaceChildren(contentRegularPrice)
container.modelFinalPrice.replaceChildren(contentFinalPrice)
const regularPriceElmHiddenState = PRODUCT.Price.Regular === PRODUCT.Price.Final
container.regularPrice.hidden = regularPriceElmHiddenState
container.modelRegularPrice.hidden = regularPriceElmHiddenState
container.price.hidden = false
} else {
const empty = [
'regularPrice',
'finalPrice',
'modelRegularPrice',
'modelFinalPrice',
'modelTitle',
'modelSubtitle',
'deliveryInfo',
'modelId',
'modelSku'
]
for (const key of empty) {
container[key].replaceChildren()
}
container.invalidModel.hidden = false
}
})
} else if (PRODUCT.Container === Products.Simple) {
setDeliverySimple()
}
}
const productCardXtraContainer = document.getElementById('product-card-xtra-container')
if (productCardXtraContainer) {
const productBuyBtn = document.getElementById('product-buy-btn')
new IntersectionObserver((entries) => entries.forEach((entry) => {
if (scrollY > productBuyBtn.offsetTop + productBuyBtn.offsetHeight) {
if (productCardXtraContainer.hidden) {
productCardXtraContainer.hidden = false
}
} else {
if (!productCardXtraContainer.hidden) {
productCardXtraContainer.hidden = true
}
}
}), {
root : null
}).observe(productBuyBtn)
}
const fsProxy = (() => {
if (document.fullscreenEnabled || document.webkitFullscreenEnabled) {
return Object.freeze({
__proto__ : null,
get fullscreenEnabled() {
return document.fullscreenEnabled || document.webkitFullscreenEnabled
},
get fullscreenElement() {
return document.fullscreenElement || document.webkitFullscreenElement
},
get fullscreenchange() {
return document.fullscreenEnabled
? 'fullscreenchange'
: 'webkitfullscreenchange'
},
get fullscreenerror() {
return document.fullscreenEnabled
? 'fullscreenerror'
: 'webkitfullscreenerror'
},
requestFullscreen(elm) {
const options = {
navigationUI : 'hide'
}
return elm.requestFullscreen
? elm.requestFullscreen(options)
: elm.webkitRequestFullscreen(options)
},
exitFullscreen() {
return document.exitFullscreen
? document.exitFullscreen()
: document.webkitExitFullscreen()
},
})
}
return null
})()
const imageSetContainer = (() => {
if (fsProxy) {
const container = document.createElement('aside')
container.id = 'product-image-set-fullscreen-container'
document.body.appendChild(container)
return container
}
return document.body
})()
const lightbox = new PhotoSwipeLightbox({
gallery : '#product-image-set',
bgOpacity : 1,
children : 'a',
pswpModule : PhotoSwipe,
openPromise : () => new Promise((resolve) => {
if (!fsProxy || fsProxy.fullscreenElement) {
resolve()
} else {
fsProxy.requestFullscreen(imageSetContainer)
.catch((error) => debuglog(error))
.finally(() => resolve())
}
}),
appendToEl : imageSetContainer,
showAnimationDuration : 0,
hideAnimationDuration : 0
})
lightbox.on('close', () => {
if (fsProxy && fsProxy.fullscreenElement) {
fsProxy.exitFullscreen().catch((error) => debuglog(error))
}
})
lightbox.init()
if (oneclickPhoneInputElm) {
globalThis.oneclickOrderCheckout = () => {
const phone = oneclickPhoneInputElm.value
if (validUAMobilePhone(phone)) {
const oneclickOrderBuyBtn = document.getElementById('oneclick-order-buy-btn')
const oneclickOrderSpinner = document.getElementById('oneclick-order-spinner')
const unhideOneclickOrderForm = () => {
oneclickOrderSpinner.hidden = true
oneclickOrderBuyBtn.hidden = false
oneclickPhoneInputElm.hidden = false
}
oneclickPhoneInputElm.hidden = true
oneclickOrderBuyBtn.hidden = true
oneclickOrderSpinner.hidden = false
const storageId = `${ Mage.Cookies.prefix }CustomerContacts`
const customerContacts = JSON.parse(localStorage.getItem(storageId)) || {}
customerContacts.phone = phone
localStorage.setItem(storageId, JSON.stringify(customerContacts))
const request = new SameOriginRequest(`${ Mage.PathPrefix }/oneclickorder`, {
method : 'POST',
cache : 'no-store',
body : new URLSearchParams({
phone,
product_id : PRODUCT.Id
})
})
fetch(request).then((response) => {
if (response.status === 200) {
return response.json()
}
request.controller.abort(
new DOMException(`Invalid status ${ response.status }`, 'NetworkError')
)
}).then((response) => {
if (response.ok) {
const localStorageId = `${ Mage.Cookies.prefix }CustomerContacts`
const customerContacts = JSON.parse(localStorage.getItem(localStorageId)) || {}
const customerExtraInfo = {}
if (customerContacts.phone) {
customerExtraInfo.phone = `+38${ customerContacts.phone.replace(/\D/g, '') }`
}
if (customerContacts.email) {
customerExtraInfo.email = customerContacts.email
}
oneclickPhoneInputElm.IMask.value = ''
document.getElementById('oneclick-order-done-title')
.replaceChildren(PRODUCT.ModelTitle)
document.getElementById('oneclick-order-done-id')
.replaceChildren(response.order_id)
elmOpenModalById('oneclick-order-done')
productG4EcommerceEvent('add_to_cart', {
currency : CURRENCY,
value : PRODUCT.Price.Final
}, {
affiliation : 'OneClick'
})
setTimeout(() => productG4EcommerceEvent('begin_checkout', {
currency : CURRENCY,
value : PRODUCT.Price.Final,
coupon : null
}, {
affiliation : 'OneClick'
}), 250)
setTimeout(() => productG4EcommerceEvent('purchase', {
transaction_id : response.order_id + '-1click',
value : PRODUCT.Price.Final,
tax : 0,
shipping : 0,
currency : CURRENCY,
coupon : null
}, {
affiliation : 'OneClick'
}, customerExtraInfo), 500)
unhideOneclickOrderForm()
} else {
if (response.error) {
notyf.error(response.error)
} else {
notyf.error(Stores.isUk()
? '<p>На жаль, виникла помилка під час реєстрації замовлення на сервері.</p>' +
'<p>Будь ласка, спробуйте оформити ваше замовлення наново трохи пізніше.</p>'
: '<p>К сожалению, возникла ошибка в процессе регистрации заказа на сервере.</p>' +
'<p>Пожалуйста, попробуйте оформить ваш заказ повторно чуть позже.</p>'
)
}
unhideOneclickOrderForm()
}
}).catch((error) => {
debuglog('Fetch %s %o failed with %o', request.url, request, error)
notyf.error(Stores.isUk()
? '<p>На жаль, замовлення не було зареєстровано через помилку під час надсилання даних.</p>' +
'<p>Будь ласка, спробуйте оформити ваше замовлення наново трохи пізніше.</p>'
: '<p>К сожалению, заказ не был зарегистрирован из-за ошибки во время отправки данных.</p>' +
'<p>Пожалуйста, попробуйте оформить ваш заказ повторно чуть позже.</p>'
)
unhideOneclickOrderForm()
})
} else {
notyf.warn(
(Stores.isUk()
? 'Будь ласка, вкажіть коректний номер мобільного українського оператора у форматі'
: 'Пожалуйста, укажите корректный номер мобильного украинского оператора в формате'
) + ' (0XX) XXX-XX-XX'
)
oneclickPhoneInputElm.focus()
}
}
const abortControllerOneclickOrderPhoneFocus = new AbortController()
const abortControllerOneclickOrderPhoneKeydown = new AbortController()
oneclickPhoneInputElm.addEventListener('focus', () => {
if (!oneclickPhoneInputElm.value) {
const customerContacts = JSON.parse(localStorage.getItem(`${ Mage.Cookies.prefix }CustomerContacts`)) || {}
if (customerContacts.phone) {
oneclickPhoneInputElm.IMask.value = customerContacts.phone
}
}
}, {
once : true,
passive : true,
signal : abortControllerOneclickOrderPhoneFocus.signal
})
oneclickPhoneInputElm.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
event.preventDefault()
oneclickOrderCheckout()
}
}, {
signal : abortControllerOneclickOrderPhoneKeydown.signal
})
}
if (document.getElementById('monobank-order')) {
globalThis.monobankInstallment = new class MonobankInstallment {
constructor() {
this.monobankOrder = 'monobank-order'
this.affiliation = Object.freeze({
affiliation : 'Monobank'
})
this.purchased = null
this.phone = monobankPhoneInputElm
this.keys = Object.freeze([
'productTitle',
'productSubtitle',
'productRegularPrice',
'productFinalPrice',
'productPerpartPrice',
'parts',
'save',
'accept',
'email',
'lastname',
'firstname',
'midname'
])
for (const key of this.keys) {
this[key] = document.getElementById(
`${ this.monobankOrder }-${ key.replace(/[A-Z]/g, (char) => `-${ char.toLowerCase() }`) }`
)
}
const commons = {
fmt : '(0ХХ) XXX-XX-XX',
num : 'номер',
op : 'оператора',
ok : {
Uk : 'коректно',
Ru : 'корректно'
}
}
commons.set = {
Uk : `${ commons.ok.Uk } вкажіть`,
Ru : `${ commons.ok.Ru } укажите`
}
this.errorReport = Object.freeze({
Uk : Object.freeze({
intro : `На жаль, не усі рядки форми заповнено ${ commons.ok.Uk }. Будь ласка, усуньте ` +
'перелічені нижче недоліки і наново натисніть на кнопку «Оформити»',
lastname : `${ commons.set.Uk } прізвище`,
firstname : `${ commons.set.Uk } імʼя`,
midname : `${ commons.set.Uk } імʼя по батькові`,
phone : `${ commons.set.Uk } ${ commons.num } мобільного українського ${ commons.op } ` +
`у форматі ${ commons.fmt }`,
email : `${ commons.set.Uk } адресу скриньки електронної пошти`,
accept : 'підтвердьте згоду з нашою Політикою конфіденційності та Публічною офертою'
}),
Ru : Object.freeze({
intro : `К сожалению, не все поля формы заполнены ${ commons.ok.Ru }. Пожалуйста, устраните ` +
'перечисленные ниже недочеты и повторно нажмите на кнопку «Оформить»',
lastname : `${ commons.set.Ru } фамилию`,
firstname : `${ commons.set.Ru } имя`,
midname : `${ commons.set.Ru } отчество`,
phone : `${ commons.set.Ru } ${ commons.num } мобильного украинского ${ commons.op } ` +
`в формате ${ commons.fmt }`,
email : `${ commons.set.Ru } адрес электронной почты`,
accept : 'подтвердите согласие с нашей Политикой конфиденциальности и Публичной офертой'
})
})
const monobank = '<strong>monobank</strong>'
this.submitReport = Object.freeze({
Uk : Object.freeze({
thanks : 'Дякуємо',
saved : 'Ваше замовлення прийнято до обробки',
confirm : `Будь ласка, підтвердіть відкриття розстрочки у додатку ${ monobank } ` +
'на вашому телефоні'
}),
Ru : Object.freeze({
thanks : 'Спасибо',
saved : 'Ваш заказ принят к обработке',
confirm : `Пожалуйста, подтвердите открытие рассрочки в приложении ${ monobank } ` +
'на вашем телефоне'
})
})
Object.seal(this)
}
init() {
this.purchased = false
this.productTitle.replaceChildren(PRODUCT.Title.replace(/\(.+\)$/, '').trim())
this.productSubtitle.replaceChildren()
if (PRODUCT.Model) {
this.productSubtitle.replaceChildren(PRODUCT.Model)
}
this.productRegularPrice.replaceChildren(numberToPriceString(PRODUCT.Price.Regular))
this.productFinalPrice.replaceChildren(numberToPriceString(PRODUCT.Price.Final))
this.productRegularPrice.hidden = PRODUCT.Price.Regular === PRODUCT.Price.Final
this.productPerpartPrice.replaceChildren(
numberToPriceString(Math.round(PRODUCT.Price.Final / this.parts.value))
)
const customerContacts = JSON.parse(localStorage.getItem(`${ Mage.Cookies.prefix }CustomerContacts`)) || {}
if (!this.phone.value && customerContacts.phone) {
this.phone.IMask.value = customerContacts.phone
}
for (const key of this.keys.slice(-4)) {
if (!this[key].value && customerContacts[key]) {
this[key].value = customerContacts[key]
}
}
productG4EcommerceEvent('add_to_cart', {
currency : CURRENCY,
value : PRODUCT.Price.Final
}, this.affiliation)
elmOpenModalById(this.monobankOrder, {
onClose : () => {
if (!monobankInstallment.purchased) {
productG4EcommerceEvent('remove_from_cart', {
currency : CURRENCY,
value : PRODUCT.Price.Final
}, monobankInstallment.affiliation)
}
}
})
}
submit() {
const langId = Stores[Mage.StoreId]
const invalidFields = []
for (const key of this.keys.slice(-4)) {
this[key].value = this[key].value.trim()
}
for (const key of this.keys.slice(-3)) {
if (!(this[key].value && new RegExp(`^${ this[key].pattern }$`).test(this[key].value))) {
invalidFields.push(this.errorReport[langId][key])
}
}
if (!(this.email.value && is(this.email.value).email)) {
invalidFields.push(this.errorReport[langId].email)
}
if (!(this.phone.value && validUAMobilePhone(this.phone.value))) {
invalidFields.push(this.errorReport[langId].phone)
}
if (!this.accept.checked) {
invalidFields.push(this.errorReport[langId].accept)
}
if (invalidFields.length) {
notyf.error({
message : `<p>${ this.errorReport[langId].intro }</p><ul class="lind-xl tind"><li>- ` +
`${ invalidFields.join('</li><li>- ') }</li></ul>`,
duration : 30000
})
} else {
const elmDisabled = 'elm-disabled'
this.save.classList.add(elmDisabled)
const storageId = `${ Mage.Cookies.prefix }CustomerContacts`
const customerContacts = JSON.parse(localStorage.getItem(storageId)) || {}
for (const key of this.keys.slice(-5)) {
customerContacts[key] = this[key].value
}
customerContacts.name = `${ this.lastname.value } ${ this.firstname.value } ${ this.midname.value }`
localStorage.setItem(storageId, JSON.stringify(customerContacts))
productG4EcommerceEvent('begin_checkout', {
currency : CURRENCY,
value : PRODUCT.Price.Final,
coupon : null
}, this.affiliation)
const request = new SameOriginRequest(`${ Mage.PathPrefix }/eminstallment/index/mbinitorder`, {
method : 'POST',
cache : 'no-store',
body : new URLSearchParams({
clientLastname : this.lastname.value,
clientFirstname : this.firstname.value,
clientMidname : this.midname.value,
clientPhone : this.phone.value,
clientEmail : this.email.value,
productId : PRODUCT.Id,
productTitle : PRODUCT.ModelTitle,
productPrice : PRODUCT.Price.Final,
parts : Number(this.parts.value),
permonth : numberToPriceString(Math.round(PRODUCT.Price.Final / this.parts.value))
})
})
fetch(request).then((response) => {
if (response.status === 200) {
return response.json()
}
request.controller.abort(
new DOMException(`Invalid status ${ response.status }`, 'NetworkError')
)
}).then((response) => {
if (response.ok) {
const localStorageId = `${ Mage.Cookies.prefix }CustomerContacts`
const customerContacts = JSON.parse(localStorage.getItem(localStorageId)) || {}
const customerExtraInfo = {}
if (customerContacts.phone) {
customerExtraInfo.phone = `+38${ customerContacts.phone.replace(/\D/g, '') }`
}
if (customerContacts.email) {
customerExtraInfo.email = customerContacts.email
}
monobankInstallment.purchased = true
productG4EcommerceEvent('purchase', {
transaction_id : randomUUID(),
value : PRODUCT.Price.Final,
tax : 0,
shipping : 0,
currency : CURRENCY,
coupon : null
}, monobankInstallment.affiliation, customerExtraInfo)
MicroModal.close(monobankInstallment.monobankOrder)
const commons = {
p : '<p class="bind">',
pLast : '<p>',
pEnd : '</p>'
}
notyf.success(
`${ commons.p }${ monobankInstallment.submitReport[langId].thanks }!${ commons.pEnd }` +
`${ commons.p }${ monobankInstallment.submitReport[langId].saved }.${ commons.pEnd }` +
`${ commons.pLast }${ monobankInstallment.submitReport[langId].confirm }.${ commons.pEnd }`
)
} else {
if (response.error) {
notyf.error(response.error)
}
}
}).catch(
(error) => debuglog('Fetch %s %o failed with %o', request.url, request, error)
).finally(() => this.save.classList.remove(elmDisabled))
}
}
}
}
const reviewLikes = JSON.parse(localStorage.getItem(`${ Mage.Cookies.prefix }CustomerLikesOverProductsReviews`)) || []
for (const reviewId of reviewLikes) {
const elm = document.querySelector(`.addLikeForReview[data-review-id="${ reviewId }"]`)
if (elm) {
elm.style.display = 'none'
if (Number(elm.previousElementSibling.dataset.reviewLikes) === 0) {
elm.previousElementSibling.dataset.reviewLikes = 1
elm.previousElementSibling.replaceChildren('+1')
}
}
}
globalThis.addReview = (id) => {
const storageId = `${ Mage.Cookies.prefix }CustomerContacts`
const customerContacts = JSON.parse(localStorage.getItem(storageId)) || {}
const form = document.getElementById('review-form')
let name = ''
if (customerContacts.name) {
name = customerContacts.name
} else if (customerContacts.firstname) {
name = customerContacts.firstname
if (customerContacts.lastname) {
name = `${ customerContacts.lastname } ${ name }`
}
if (customerContacts.midname) {
name = `${ name } ${ customerContacts.midname }`
}
}
if (name) {
document.getElementById('review-nickname').value = name
}
if (customerContacts.email) {
document.getElementById('review-email').value = customerContacts.email
}
for (const elmId of ['rating', 'advantage', 'minus']) {
document.getElementById(`review-${ elmId }-set`).style.display = id ? 'none' : 'grid'
}
if (id) {
document.getElementById('rating-star-5').checked = true
} else {
for (const starId of [1, 2, 3, 4, 5]) {
document.getElementById(`rating-star-${ starId }`).checked = false
}
}
document.getElementById('review-parent-id').value = id || 0
elmOpenModalById('review-modal', {
disableFocus : true
})
}
globalThis.submitReview = () => {
for (const id of ['advantage', 'minus', 'detail', 'nickname', 'email']) {
const input = document.getElementById(`review-${ id }`)
if (input.value) {
input.value = stripTags(input.value).trim()
}
}
const errorReport = {
Uk : {
intro : 'На жаль, не усі рядки форми заповнено коректно. Будь ласка, усуньте ' +
'перелічені нижче недоліки і наново натисніть на кнопку «Надіслати»',
rate : 'надайте оцінку товару',
comment : 'надайте свій коментар',
name : 'вкажіть своє імʼя',
email : 'вкажіть коректну адресу скриньки електронної пошти'
},
Ru : {
intro : 'К сожалению, не все поля формы заполнены корректно. Пожалуйста, устраните ' +
'перечисленные ниже недочеты и повторно нажмите на кнопку «Отправить»',
rate : 'оцените товар',
comment : 'оставьте свой комментарий',
name : 'укажите свое имя',
email : 'укажите корректный адрес электронной почты'
}
}
const form = document.getElementById('review-form')
const langId = Stores[Mage.StoreId]
const invalidFields = []
if (!form.elements['ratings[4]'].value) {
invalidFields.push(errorReport[langId].rate)
}
if (!form.elements.detail.value) {
invalidFields.push(errorReport[langId].comment)
}
if (!(form.elements.nickname.value && is(form.elements.nickname.value.length).inRange(2, 255))) {
invalidFields.push(errorReport[langId].name)
}
if (form.elements.email.value) {
if (!is(form.elements.email.value).email) {
invalidFields.push(errorReport[langId].email)
}
}
if (invalidFields.length) {
notyf.error({
message : `<p>${ errorReport[langId].intro }</p><ul class="lind-xl tind"><li>- ` +
`${ invalidFields.join('</li><li>- ') }</li></ul>`,
duration : 30000
})
return
}
document.getElementById('review-title').value = dateToUkString()
document.getElementById('review-mood').value = 2
document.getElementById('review-category-id').value = PRODUCT.Category.Id
document.getElementById('review-product-id').value = PRODUCT.Parent.Id
grecaptcha.ready(() => {
grecaptcha.execute('6LeeJr8UAAAAAKKbBk6Fa2TPGvm0hLP0wqz5MM6v', {
action : 'review'
}).then((token) => {
document.getElementById('review-grecaptcha-token').value = token
const request = new SameOriginRequest(`${ Mage.PathPrefix }/review/product/store`, {
method : 'POST',
cache : 'no-store',
body : new URLSearchParams(new FormData(form))
})
MicroModal.close('review-modal')
fetch(request).then((response) => {
if (response.status !== 200) {
request.controller.abort(
new DOMException(`Invalid network status ${ response.status }`, 'NetworkError')
)
}
return response.json()
}).then((response) => {
if (response.ok) {
const confirmReport = {
Uk : {
thanks : 'Дякуємо!',
stored : 'Ваш відгук додано і буде опубліковано після перевірки модератором.'
},
Ru : {
thanks : 'Спасибо!',
stored : 'Ваш отзыв добавлен и будет опубликован после проверки модератором.'
}
}
notyf.success(
`<p class="bind">${ confirmReport[langId].thanks }</p>` +
`<p>${ confirmReport[langId].stored }</p>`
)
} else if (response.errors.length) {
const failireReport = {
Uk : 'На жаль, ваш відгук не було додано через наступні причини',
Ru : 'к сожалению, ваш отзыв не был добавлен по следующим причинам'
}
notyf.error(
`<p>${ failureReport[langId] }</p><ul class="lind-xl tind"><li>- ` +
`${ response.errors.join('</li><li>- ') }</li></ul>`
)
}
}).catch((error) => debuglog('Failed to fetch %o due to %s %o', request, error.message, error))
})
})
}
globalThis.addLikeForReview = (reviewId) => {
const storageId = `${ Mage.Cookies.prefix }CustomerLikesOverProductsReviews`
const customerLikesOverProductsReviews = JSON.parse(localStorage.getItem(storageId)) || []
const elmLikesTotal = document.querySelector(`.reviewLikesTotal[data-review-id="${ reviewId }"]`)
const likesTotal = Number(elmLikesTotal.dataset.reviewLikes) + 1
customerLikesOverProductsReviews.push(reviewId)
elmLikesTotal.dataset.reviewLikes = likesTotal
elmLikesTotal.replaceChildren(`+${ likesTotal }`)
elmLikesTotal.nextElementSibling.style.display = 'none'
const formData = new FormData()
formData.set('reviewId', reviewId)
const request = new SameOriginRequest('/fourdreview/votes/increment', {
method : 'POST',
cache : 'no-store',
body : new URLSearchParams(formData)
})
fetch(request).then((response) => {
if (response.status !== 200) {
request.controller.abort(
new DOMException(`Invalid network status ${ response.status }`, 'NetworkError')
)
}
localStorage.setItem(storageId, JSON.stringify(customerLikesOverProductsReviews))
}).catch((error) => debuglog('Failed to fetch %o due to %s %o', request, error.message, error))
}
if ([Products.Configurable, Products.Simple].includes(PRODUCT.Container)) {
PRODUCT.Container === Products.Simple
? 'setDeliverySimple' in globalThis && setDeliverySimple()
: 'setDetailConfigurablePrice' in globalThis && setDetailConfigurablePrice()
const storageId = `${ Mage.Cookies.prefix }ViewedProducts${ Stores[Mage.StoreId] }`
const record = {
Id : PRODUCT.Parent.Id,
Sku : PRODUCT.Parent.Sku,
Title : PRODUCT.Title,
Price : {
Regular : PRODUCT.Parent.Price.Regular,
Final : PRODUCT.Parent.Price.Final,
Sale : PRODUCT.Parent.Price.Sale,
Discount : PRODUCT.Parent.Price.Discount
},
Brand : PRODUCT.Brand,
Container : PRODUCT.Container,
Category : {
Id : PRODUCT.Category.Id,
Title : PRODUCT.Category.Title,
Tree : PRODUCT.Category.Tree,
},
Url : {
Canonical : PRODUCT.Url.Canonical,
Image : PRODUCT.Url.Image
},
Date : (
(() => {
const date = {
UTS : Date.now()
}
date.Readable = dateToUkString(date.UTS)
return date
})()
)
}
let container = JSON.parse(localStorage.getItem(storageId)) || []
if (!(container.length && container[0].Id === record.Id)) {
container.unshift(record)
}
try {
localStorage.setItem(storageId, JSON.stringify(container))
} catch (error) {
debuglog(
'Failed to store %o to %s container at localStorage due to %o',
container,
storageId,
error
)
}
container = (
(() => {
container.forEach((value, key) => {
if (record.Id === value.Id) {
delete container[key]
} else {
container.forEach((v, k) => {
if (value.Id === v.Id && key !== k) {
delete container[k]
}
})
}
})
return container.filter((value) => value)
})()
)
if (container.length) {
const elm = document.getElementById('viewed-products')
if (elm) {
const title = Stores.isUk() ? 'Раніше ви переглядали' : 'Ранее вы просматривали'
const pricePrefix = Stores.isUk() ? 'Від ' : 'От '
const buyBtnTitle = 'Купит' + (Stores.isUk() ? 'и' : 'ь')
const template = {
header : '<div id="viewed-products" class="block"><div class="block-title"><strong>' +
title +
'</strong></div><div class="block-content"><ol id="recently-viewed-items">',
footer : '</ol></div></div>',
layout : '<li class="item"><section role="image"><a href="%url%" class="product-image">' +
'<img src="%imageUrl%" alt="%title%"></a></section><section role="info">' +
'<p class="product-name"><a href="%url%">%title%</a></p><div class="price-box">' +
'%priceHtml%</div><a role="button"%disabled% class="add2cart%disabled%" href="%url%">' +
buyBtnTitle +
'</a></section></li>',
price : {
regular : '<span class="price">%priceFinal% <span class="symbol">грн</span></span>',
special : '<p class="old-price"><span class="price">%priceRegular% <span class="symbol">' +
'грн</span></span></p><p class="special-price"> <span class="price">%priceFinal% ' +
'<span class="symbol">грн</span></span></p>'
}
}
let html = template.header
for (const record of container.slice(0, 3)) {
const priceHtml = record.Price.Final === 0
? ''
: ((record.Container === configurable
? pricePrefix
: ''
) + template.price[record.Price.Sale ? 'special' : 'regular'])
.replace('%priceRegular%', record.Price.Regular)
.replace('%priceFinal%', record.Price.Final)
html += template.layout
.replace(/%url%/g, record.Url.Canonical)
.replace('%imageUrl%', record.Url.Image)
.replace(/%title%/g, record.Title)
.replace('%priceHtml%', priceHtml)
.replace(/%disabled%/g, record.Price.Final === 0
? ' disabled'
: ''
)
}
html += template.footer
try {
elm.replaceWith(parseDOMString(html, 'text/html'))
} catch (error) {
debuglog(
'Failed to inject documentFragment %s into DOM %o due to %o',
html,
elm,
error
)
elm.remove()
}
}
}
}
}
if (['catalog-category-view', 'catalogsearch-result-index'].includes(Mage.Action)) {
const items = []
globalThis.getCategoryListNameListIdForProductG4EcommerceEvent = () => {
const listName = Mage.Action === 'catalog-category-view'
? `Catalog Category ${ PRODUCT.Category.Title } page ${ getUrlSearchParam('p') || 1 } ` +
`order ${ getUrlSearchParam('order') || 'default' } dir ${ getUrlSearchParam('dir') || 'asc' }`
: `Search Results ${ getUrlSearchParam('q') } page ${ getUrlSearchParam('p') || 1 }`
return {
item_list_name : listName,
item_list_id : TranslitEd[`translit${ Stores[Mage.StoreId] }`](listName)
.replace(/ > /g, ' ')
.replace(/[\(\),\.!\?:;]/g, '')
.replace(/[\s-]/g, '_')
.toLowerCase()
}
}
const listNameListId = getCategoryListNameListIdForProductG4EcommerceEvent()
for (const card of document.querySelectorAll('#catalog-category-products .item')) {
items.push({
item_name : card.dataset.title,
item_id : card.dataset.sku,
affiliation : location.hostname,
price : Number(card.dataset.price),
discount : Number(card.dataset.discount),
item_brand : card.dataset.brand,
item_variant : null,
index : Number(card.dataset.slot),
quantity : 1,
... listNameListId,
... getCategoryTreeForProductG4EcommerceEvent(card.dataset.categoryTree)
})
}
if (items.length) {
productG4EcommerceEvent('view_item_list', {
items,
... listNameListId
})
}
globalThis.productG4Select = (elm) => {
const card = elm.closest('.item')
if (card) {
const listNameListId = getCategoryListNameListIdForProductG4EcommerceEvent()
productG4EcommerceEvent('select_item', {
items : [{
item_name : card.dataset.title,
item_id : card.dataset.sku,
price : Number(card.dataset.price),
discount : Number(card.dataset.discount),
item_brand : card.dataset.brand,
item_variant : null,
index : Number(card.dataset.slot),
quantity : 1,
... listNameListId,
... getCategoryTreeForProductG4EcommerceEvent(card.dataset.categoryTree)
}],
... listNameListId
})
const target = new URL(card.dataset.href, location.origin)
if (elm.dataset.color) {
target.searchParams.set('color', elm.dataset.color)
}
location.assign(target.href)
}
}
globalThis.filtersToggle = (elm) => {
elm.nextElementSibling.hidden = !elm.nextElementSibling.hidden
}
globalThis.filtersShowAllOptions = (elm) => {
for (const li of elm.parentElement.parentElement.parentElement.querySelectorAll('li')) {
li.hidden = false
}
elm.parentElement.hidden = true
}
globalThis.filtersHideExtraOptions = (elm, maxVisible) => {
elm.parentElement.parentElement.parentElement.querySelectorAll('li').forEach((elm, idx) => {
elm.hidden = idx >= maxVisible
})
elm.parentElement.previousElementSibling.hidden = false
elm.parentElement.hidden = true
}
const responsiveFilters = 'responsive-filters'
const responsiveFiltersTrigger = `${ responsiveFilters }-trigger`
const filters = document.getElementById(responsiveFilters)
const showFilters = document.getElementById(responsiveFiltersTrigger)
const hideFilters = document.getElementById(`${ responsiveFiltersTrigger }-hide`)
if (document.getElementById('filter-cat')) {
const showFiltersBtnTitle = Stores.isUk() ? 'Оберіть підкатегорію' : 'Выберите подкатегорию'
const hideFiltersBtnTitle = Stores.isUk() ? 'Сховати підкатегорії' : 'Скрыть подкатегории'
if (showFilters) {
showFilters.replaceChildren(showFiltersBtnTitle)
}
if (hideFilters) {
hideFilters.replaceChildren(hideFiltersBtnTitle)
}
}
if (filters && showFilters && hideFilters) {
const placeLeftSidebarBeforeMainColumn = () => {
const mainContainer = document.querySelector('.main-container .main')
if (mainContainer) {
const leftSidebar = document.querySelector('.col-left.sidebar')
const mainColumn = mainContainer.querySelector('.col-main')
if (leftSidebar && mainColumn) {
mainContainer.insertBefore(leftSidebar, mainColumn)
}
}
filters.hidden = true
hideFilters.hidden = true
}
const click = 'click'
const viewportBreakpoint = 768
const options = {
passive : true
}
const abortControllerShowFilters = new AbortController()
const abortControllerHideFilters = new AbortController()
showFilters.addEventListener(click, () => {
filters.hidden = false
hideFilters.hidden = false
showFilters.hidden = true
}, {
...options,
signal : abortControllerShowFilters.signal
})
hideFilters.addEventListener(click, () => {
filters.hidden = true
hideFilters.hidden = true
showFilters.hidden = false
}, {
...options,
signal : abortControllerHideFilters.signal
})
if (innerWidth < viewportBreakpoint) {
placeLeftSidebarBeforeMainColumn()
} else {
const abortController = new AbortController()
addEventListener('resize', () => {
if (innerWidth < viewportBreakpoint) {
placeLeftSidebarBeforeMainColumn()
abortController.abort()
}
}, {
...options,
signal : abortController.signal
})
}
}
}
if (Mage.Action === 'checkout-cart-index') {
const namespace = 'billing:'
const name = document.getElementById(`${ namespace }lastname`)
const phone = checkoutPhoneInputElm
const email = document.getElementById(`${ namespace }email`)
const comment = document.getElementById(`${ namespace }fax`)
const sessionStorageId = `${ Mage.Cookies.prefix }CheckoutFormData`
const checkoutFormData = JSON.parse(sessionStorage.getItem(sessionStorageId))
const customerContacts = JSON.parse(localStorage.getItem(`${ Mage.Cookies.prefix }CustomerContacts`)) || {}
if (checkoutFormData) {
sessionStorage.removeItem(sessionStorageId)
if (name && checkoutFormData.name) {
name.value = checkoutFormData.name
}
if (phone && checkoutFormData.phone) {
resolve('IMask').then(() => {
phone.IMask.value = checkoutFormData.phone
})
}
if (email && checkoutFormData.email) {
email.value = checkoutFormData.email
}
if (comment && checkoutFormData.comment) {
comment.value = checkoutFormData.comment
}
}
if (name && !name.value && customerContacts.name) {
name.value = customerContacts.name
}
resolve('IMask').then(() => setTimeout(() => {
if (phone && !phone.value && customerContacts.phone) {
phone.IMask.value = customerContacts.phone
}
}, 100))
if (email && !email.value && customerContacts.email) {
email.value = customerContacts.email
}
const hash = '#emcart'
if (location.hash !== hash) {
location.hash = hash
}
const promocode = 'promocode'
const promocodeUserInput = document.getElementById(`${ promocode }-user-input`)
const promocodeInput = document.getElementById(`${ promocode }-input`)
const promocodeProcess = document.getElementById(`${ promocode }-process`)
const promocodeRemove = document.getElementById(`${ promocode }-remove`)
const promocodeApply = document.getElementById(`${ promocode }-apply`)
const promocodeRevert = document.getElementById(`${ promocode }-revert`)
if (promocodeApply || promocodeRevert) {
const promocodeAbortController = new AbortController()
;(promocodeApply || promocodeRevert).addEventListener('click', (event) => {
if (!promocodeUserInput.value.trim()) {
notyf.warn(`${ Stores.isUk() ? 'Будь ласка, введіть' : 'Пожалуйста, введите' } промокод.`)
return promocodeUserInput.focus()
}
event.target.disabled = true
if (promocodeApply) {
promocodeInput.value = promocodeUserInput.value.trim()
}
promocodeProcess.value = 1
if (promocodeRevert) {
promocodeRemove.value = 1
}
checkoutSaveSession()
checkout.update({
process_coupon : 1,
review : 1
})
}, {
passive : true,
signal : promocodeAbortController.signal
})
}
if (globalThis.BillingAddress) {
globalThis.billing = new BillingAddress()
}
if (globalThis.RegionUpdater) {
RegionUpdater.prototype.setMarkDisplay = () => {}
}
if (globalThis.ZipUpdater) {
ZipUpdater.prototype._setPostcodeOptional = () => {}
}
const items = []
for (const product of document.querySelectorAll('.cart-product-data')) {
items.push({
item_name : product.dataset.title,
item_id : product.dataset.sku,
affiliation : 'Cart',
price : Number(product.dataset.price),
discount : Number(product.dataset.discount),
item_brand : product.dataset.brand,
item_variant : product.dataset.model || null,
quantity : Number(product.dataset.quantity),
... getCategoryTreeForProductG4EcommerceEvent(product.dataset.categoryTree)
})
}
if (items.length) {
const ecommerce = {
currency : CURRENCY,
value : Number(document.getElementById('shopping-cart-totals-table').dataset.total),
coupon : document.getElementById('promocode-input').value ||
document.getElementById('promocode-user-input').value ||
null,
items
}
sessionStorage.setItem(`${ Mage.Cookies.prefix }CheckoutEcommerce`, JSON.stringify(ecommerce))
productG4EcommerceEvent('view_cart', ecommerce)
}
globalThis.checkoutProcess = () => {
const namespace = 'billing:'
const lastname = document.getElementById(`${ namespace }lastname`).value.trim()
const phone = checkoutPhoneInputElm.value.trim()
const email = document.getElementById(`${ namespace }email`).value.trim()
const checkbox = document.getElementById('agree')
const errorReport = {
Uk : {
intro : 'На жаль, не усі рядки форми заповнено коректно. Будь ласка, усуньте ' +
'перелічені нижче недоліки і наново натисніть на кнопку «Оформити»',
lastname : 'вкажіть своє імʼя та прізвище',
phone : 'вкажіть коректний номер мобільного українського оператора у форматі (0ХХ) XXX-XX-XX',
email : 'вкажіть коректну адресу скриньки електронної пошти',
agree : 'підтвердьте свою згоду з нашою Політикою конфіденційності та Публічною офертою'
},
Ru : {
intro : 'К сожалению, не все поля формы заполнены корректно. Пожалуйста, устраните ' +
'перечисленные ниже недочеты и повторно нажмите на кнопку «Оформить»',
lastname : 'укажите свое имя и фамилию',
phone : 'укажите корректный номер мобильного украинского оператора в формате (0ХХ) XXX-XX-XX',
email : 'укажите корректный адрес электронной почты',
agree : 'подтвердите свое согласие с нашей Политикой конфиденциальности и Публичной офертой'
}
}
const langId = Stores[Mage.StoreId]
const invalidFields = []
if (!is(lastname.length).inRange(2, 180)) {
invalidFields.push(errorReport[langId].lastname)
}
if (!validUAMobilePhone(phone)) {
invalidFields.push(errorReport[langId].phone)
}
if (!is(email).email) {
invalidFields.push(errorReport[langId].email)
}
if (!checkbox.checked) {
invalidFields.push(errorReport[langId].agree)
}
if (invalidFields.length) {
notyf.error({
message : `<p>${ errorReport[langId].intro }</p><ul class="lind-xl tind"><li>- ` +
`${ invalidFields.join('</li><li>- ') }</li></ul>`,
duration : 60000
})
} else {
checkout.save()
}
}
}
if (Mage.Action === 'onepagecheckout-index-success') {
const storageId = `${ Mage.Cookies.prefix }CheckoutEcommerce`
const ecommerce = JSON.parse(sessionStorage.getItem(storageId))
const customerContacts = JSON.parse(localStorage.getItem(`${ Mage.Cookies.prefix }CustomerContacts`)) || {}
const customerExtraInfo = {}
if (customerContacts.phone) {
customerExtraInfo.phone = `+38${ customerContacts.phone.replace(/\D/g, '') }`
}
if (customerContacts.email) {
customerExtraInfo.email = customerContacts.email
}
sessionStorage.removeItem(storageId)
if (ecommerce) {
productG4EcommerceEvent('purchase', {
transaction_id : document.getElementById('transaction-id').textContent,
tax : 0,
shipping : 0,
...ecommerce
}, {}, customerExtraInfo)
}
}
if (Mage.Action === 'cms-page-view') {
if (location.pathname.endsWith('/customer-service')) {
const abortControllers = new Map()
const scroller = (event) => {
event.preventDefault()
const id = String(event.currentTarget.hash || event.currentTarget.href.baseVal).slice(1)
const elm = document.getElementById(id)
if (elm) {
elm.scrollIntoView({
behavior : 'smooth'
})
}
}
for (const elm of document.querySelectorAll('a[href^="#zone-"]')) {
const abortController = new AbortController()
elm.addEventListener('click', scroller, {
signal : abortController.signal
})
abortControllers.set(elm, abortController)
}
}
}
function dependenciesCannon () {
return new Promise(function (resolve, reject) {
var handles = []
while (dependenciesCannon.magazine.length) {
try {
handles.push(
injectExternalJS.apply(globalThis, dependenciesCannon.magazine.shift())
)
} catch (error) {
debuglog(error)
handles.push(null)
}
}
resolve(handles)
})
}
Object.defineProperty(dependenciesCannon, 'magazine', {
enumerable: true,
value : []
})
function tasksCannon () {
return new Promise(function (resolve, reject) {
var handles = {}
Object.keys(tasksCannon.sections).forEach(function (section) {
if ((section === 'any' || tasksCannon.sections[section].includes(Mage.Action)) && tasksCannon.magazine[section].length) {
handles[section] = []
tasksCannon.magazine[section].forEach((fn) => {
handles[section].push(itemType(fn) === Function.name
? defer(() => {
try {
fn()
} catch (error) {
debuglog(error)
}
})
: fn
)
})
}
})
globalThis.tasksCannon.handles = handles
resolve(handles)
})
}
globalThis.dependenciesCannon = dependenciesCannon
globalThis.tasksCannon = tasksCannon
Object.defineProperty(tasksCannon, 'sections', {
value : Object.freeze({
any : '*',
catalog : 'catalog-category-view',
catalogProduct : 'catalog-category-view, catalog-product-view',
catalogSearch : 'catalog-category-view, catalogsearch-result-index',
home : 'cms-index-index',
homeCatalogSearchProduct : 'cms-index-index, catalog-category-view, catalogsearch-result-index, catalog-product-view',
homeProduct : 'cms-index-index, catalog-product-view',
product : 'catalog-product-view',
})
})
Object.defineProperty(tasksCannon, 'magazine', {
enumerable: true,
value : (function () {
var magazine = {}
Object.keys(tasksCannon.sections).forEach(function (section) {
Object.defineProperty(magazine, section, {
enumerable : true,
value : []
})
})
return magazine
})()
})
dependenciesCannon.magazine.push(
[
`https://www.googletagmanager.com/gtm.js?id=${ ExternalServicesIds.GoogleTagManager }`,
5000
],
[
/* `https://www.googletagmanager.com/gtag/js?id=${ ExternalServicesIds.GoogleAnalytics3 }`, */
`https://www.googletagmanager.com/gtag/js?id=${ ExternalServicesIds.GoogleAnalytics4 }`,
5500
],
[
'https://bat.bing.com/bat.js',
6000,
(elm) => {
elm.onload = () => {
uetq = new UET({
ti : ExternalServicesIds.MicrosoftBing,
q : uetq
})
uetq.push('pageLoad')
}
return elm
}
],
[
'https://connect.facebook.net/en_US/fbevents.js',
6500
]
)
/* Mage.Action === 'catalog-product-view' && dependenciesCannon.magazine.push([
`https://www.google.com/recaptcha/api.js?render=${ ExternalServicesIds.GoogleRecaptcha }`,
7500
]) */
tasksCannon.magazine.any.push(
function () {
const errorForm = 'error-form'
const $errorcontainer = $(`div[role="${ errorForm }"]`)
const $errorform = $errorcontainer.children('form')
const $errorbtn = $(`div[role="${ errorForm }-trigger"]`)
$errorbtn.on(click, function (evt) {
evt.preventDefault()
$errorcontainer.show()
})
$errorform.find('.close_form').on(click, function (evt) {
evt.preventDefault()
$errorcontainer.hide()
})
$errorform.on('submit', function (evt) {
var $inputfio = $errorform.find('input[name=error_reporting_fio]')
var $informer = $errorform.find('div[role=informer]')
evt.preventDefault()
if ($inputfio.val()) {
$errorform.find('.form-data').hide()
$errorform.find('#send_button').hide()
$informer.text('Пожалуйста, подождите…')
$.ajax({
url : 'https://e-matras.ua/promo/index/senderrormessage/',
type : 'POST',
data : $errorform.serialize(),
cache : false,
dataType : 'html'
}).then(function (response) {
$informer.html(response)
})
} else {
alert('Пожалуйста, укажите ФИО')
$inputfio.focus()
}
})
}
)
tasksCannon.magazine.catalogSearch.push(
function () {
$('.all-colors').on(click, function (evt) {
evt.preventDefault()
$('#colors-popup-' + $(evt.currentTarget).attr('data-product-id')).dialog(popupconf)
})
}
)
tasksCannon.magazine.home.push(
function () {
var data = 'data-'
$('div.promo-box').each(function () {
var $elm = $(this)
if (emdevice === $elm.attr(data + 'exclude-device')) {
$elm.remove()
} else {
$.get(
Mage.PathPrefix +
'/promo/loadpromo/' +
$elm.attr(data + 'block-id') +
'/'
).then(function (response) {
if (response) {
$elm.replaceWith(response)
}
})
}
})
}
)
tasksCannon.magazine.homeCatalogSearchProduct.push(
tipProductInStock
)
tasksCannon.magazine.homeProduct.push(() => {
const video = document.querySelector('video.promo')
if (video) {
if (globalThis.innerWidth > 766) {
const abortController = new AbortController()
video.Pause = () => {
if (!video.paused) {
video.pause()
}
}
video.Play = () => {
if (video.paused) {
video.play()
}
}
video.addEventListener('canplaythrough', () => {
const abortController = new AbortController()
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
video.Pause()
} else {
if (video.inViewport) {
video.Play()
}
}
}, {
passive : true,
signal : abortController.signal
})
video.style.display = 'block'
video.Pause()
new IntersectionObserver((entries) => entries.forEach((entry) => {
if (entry.isIntersecting) {
video.inViewport = true
if (!document.hidden) {
video.Play()
}
} else {
video.inViewport = false
video.Pause()
}
}), {
root : null
}).observe(video)
}, {
...eventListenerOptions,
signal : abortController.signal
})
video.firstElementChild.src = video.firstElementChild.dataset.src
video.load()
} else {
video.parentNode.remove()
}
}
})
tasksCannon.magazine.product.push(
function () {
var $anchor = $(location.hash + '-anchor')
$anchor.length && $('html, body').animate({
scrollTop : $anchor.offset().top
})
}
)
dependenciesCannon()
tasksCannon()
setTimeout(() => resolve('Glide', 'glideJs').then(() => {
for (const container of document.querySelectorAll('[data-glider]')) {
const gliderId = container.id
const gliderOptions = 'gliderOptions' in container.dataset
? JSON.parse(container.dataset.gliderOptions)
: {}
if (!glideJs.gliders[gliderId]) {
const glider = new Glide(container, {
...glideJs.config.primary,
...glideJs.config.responsive,
...gliderOptions
}).mount()
glider.Pause = () => {
if (!glider.paused) {
glider.pause()
glider.paused = true
}
}
glider.Play = () => {
if (glider.paused) {
glider.play()
glider.paused = false
}
}
glider.Pause()
const abortController = new AbortController()
document.addEventListener(
'visibilitychange',
() => {
if (document.hidden) {
glider.Pause()
} else {
if (glider.inViewport) {
glider.Play()
}
}
},
{
passive : true,
signal : abortController.signal
}
)
glideJs.gliders[gliderId] = glider
new IntersectionObserver((entries) => entries.forEach((entry) => {
if (entry.isIntersecting) {
glider.inViewport = true
if (!document.hidden) {
glider.Play()
}
} else {
glider.inViewport = false
glider.Pause()
}
}), {
root : null
}).observe(container)
}
}
}), 3000)
}, eventListenerOptions)
})(jQuery)
</script>
</head>
Е Матрас - интернет магазин матрасов и мебели в Киеве и Украине - E-matras.ua
Recherche META Description de la page
🌙 Интернет-магазин мебели, матрасов и товаров для сна 🌙 ➦ E-MATRAS ✅ Широкий ассортимент по доступным ценам ✅ Быстрая доставка по Украине 🚀
Recherche META Keywords de la page
UPDATE DOMAINES SET server='cloudflare',redirection='https://e-matras.ua/',Status='200',err='',[TITRE]=N'Е Матрас - интернет магазин матрасов и мебели в Киеве и Украине - E-matras.ua',[DESCRIPTION]=N'🌙 Интернет-магазин мебели, матрасов и товаров для сна 🌙 ➦ E-MATRAS ✅ Широкий ассортимент по доступным ценам ✅ Быстрая доставка по Украине 🚀',[KEYWORDS]=N'' WHERE id=36214965
0 Е Матрас - интернет магазин матрасов и мебели в Киеве и Украине - E-matras.ua 🌙 Интернет-магазин мебели матрасов и товаров для сна 🌙 ➦ E-MATRAS ✅ Широкий ассортимент по доступным ценам ✅ Быстрая доставка по Украине 🚀
0. Е (1)--------->0
1. Матрас (6)--------->0
2. - (1)--------->0
3. интернет (8)--------->0
4. магазин (7)--------->0
5. матрасов (8)--------->0
6. и (1)--------->0
7. мебели (6)--------->0
8. в (1)--------->0
9. Киеве (5)--------->0
11. Украине (7)--------->0
13. E-matras.ua (11)--------->0
14. 🌙 (2)--------->0
15. Интернет-магазин (16)--------->0
20. товаров (7)--------->0
21. для (3)--------->0
22. сна (3)--------->0
24. ➦ (1)--------->0
25. E-MATRAS (8)--------->0
26. ✅ (1)--------->-2147217873 Violation de la contrainte PRIMARY KEY « PK_keywords ». Impossible d'insérer une clé en double dans l'objet « dbo.keywords ». Valeur de clé dupliquée : (✅, 36214965).
27. Широкий (7)--------->0
28. ассортимент (11)--------->0
29. по (2)--------->0
30. доступным (9)--------->0
31. ценам (5)--------->0
33. Быстрая (7)--------->0
34. доставка (8)--------->0
37. 🚀 (2)--------->-2147217873 Violation de la contrainte PRIMARY KEY « PK_keywords ». Impossible d'insérer une clé en double dans l'objet « dbo.keywords ». Valeur de clé dupliquée : (🚀, 36214965).
INSERT INTO KEYWORDS (keyword,id_domaine) VALUES (N'Е',36214965),(N'Матрас',36214965),(N'-',36214965),(N'интернет',36214965),(N'магазин',36214965),(N'матрасов',36214965),(N'и',36214965),(N'мебели',36214965),(N'в',36214965),(N'Киеве',36214965),(N'Украине',36214965),(N'E-matras.ua',36214965),(N'🌙',36214965),(N'Интернет-магазин',36214965),(N'товаров',36214965),(N'для',36214965),(N'сна',36214965),(N'➦',36214965),(N'E-MATRAS',36214965),(N'✅',36214965),(N'Широкий',36214965),(N'ассортимент',36214965),(N'по',36214965),(N'доступным',36214965),(N'ценам',36214965),(N'Быстрая',36214965),(N'доставка',36214965),(N'🚀',36214965)