PageSourceSearch

https://k767.ru/bitrix/components/bitrix/catalog.item/templates/store_v3/script.js?178350638576501

js k767.ru collected 2026-10-08 07:28:33 UTC 76,501 bytes, 3,126 lines download raw bytes

1(function (window){
2	'use strict';
3
4	if (window.JCCatalogItemStoreV3)
5		return;
6
7	var BasketButton = function(params)
8	{
9		params = params || {};
10		this.popupWindow = null;
11		this.params = params;
12		this.text = params.text || '';
13		this.id = params.id || '';
14		this.className = params.className || '';
15		this.events = params.events || {};
16		this.contextEvents = {};
17
18		for (var eventName in this.events)
19		{
20			if (this.events.hasOwnProperty(eventName) && BX.type.isFunction(this.events[eventName]))
21			{
22				this.contextEvents[eventName] = BX.proxy(this.events[eventName], this);
23			}
24		}
25
26		this.buttonNode = BX.create('button', {
27			props: {className: 'btn btn-primary btn-buy btn-sm', id: this.id},
28			attrs: {type: 'button', tabindex: '0'},
29			style: typeof params.style === 'object' ? params.style : {},
30			text: this.text,
31			events: this.contextEvents
32		});
33
34		if (BX.browser.IsIE())
35		{
36			this.buttonNode.setAttribute("hideFocus", "hidefocus");
37		}
38	};
39	BX.extend(BasketButton, BX.PopupWindowButton);
40
41	window.JCCatalogItemStoreV3 = function (arParams)
42	{
43		this.productType = 0;
44		this.showQuantity = true;
45		this.showAbsent = true;
46		this.secondPict = false;
47		this.showOldPrice = false;
48		this.showMaxQuantity = 'N';
49		this.relativeQuantityFactor = 5;
50		this.showSkuProps = false;
51		this.basketAction = 'ADD';
52		this.showClosePopup = false;
53		this.useCompare = false;
54		this.showSubscription = false;
55		this.useOfferName = false;
56		this.visual = {
57			ID: '',
58			NAME: '',
59			PICT_SLIDER_ID: '',
60			QUANTITY_ID: '',
61			QUANTITY_UP_ID: '',
62			QUANTITY_DOWN_ID: '',
63			QUANTITY_COUNTER_ID: '',
64			QUANTITY_LIMIT: '',
65			QUANTITY_MEASURE: '',
66			PRICE_ID: '',
67			PRICE_TWIN_ID: '',
68			PRICE_TOTAL_ID: '',
69			BLOCK_PRICE_OLD_ID: '',
70			BLOCK_PRICE_OLD_TWIN_ID: '',
71			PRICE_OLD_ID: '',
72			PRICE_OLD_TWIN_ID: '',
73			PRICE_DISCOUNT_ID: '',
74			PRICE_DISCOUNT_TWIN_ID: '',
75			PREBUY_SWIPE_BTN: '',
76			PREBUY_OPEN_BTN: '',
77			PREBUY_CLOSE_BTN: '',
78			PREBUY_OVERLAY: '',
79			PREBUY_CONTAINER: '',
80			PREBUY_NAME: '',
81			PREBUY_PICT: '',
82			PREBUY: '',
83			DISPLAY_PROP_DIV: '',
84			BASKET_PROP_DIV: '',
85			SUBSCRIBE_ID: '',
86			BUY_ID: '',
87			ADD_BASKET_ID: '',
88			TREE_ID: ''
89		};
90		this.product = {
91			checkQuantity: false,
92			maxQuantity: 0,
93			stepQuantity: 1,
94			isDblQuantity: false,
95			canBuy: true,
96			name: '',
97			pict: {},
98			id: 0,
99			addUrl: '',
100			buyUrl: ''
101		};
102
103		this.basketMode = '';
104		this.basketData = {
105			useProps: false,
106			emptyProps: false,
107			quantity: 'quantity',
108			props: 'prop',
109			basketUrl: '',
110			sku_props: '',
111			sku_props_var: 'basket_props',
112			add_url: '',
113			buy_url: ''
114		};
115
116		this.compareData = {
117			compareUrl: '',
118			compareDeleteUrl: '',
119			comparePath: ''
120		};
121
122		this.defaultPict = {
123			pict: null,
124			secondPict: null
125		};
126
127		this.defaultSliderOptions = {
128			interval: 3000,
129			wrap: true
130		};
131		this.slider = {
132			options: {},
133			items: [],
134			active: null,
135			sliding: null,
136			paused: null,
137			interval: null,
138			progress: null
139		};
140		this.touch = null;
141
142		this.quantityDelay = null;
143		this.quantityTimer = null;
144
145		this.checkQuantity = false;
146		this.maxQuantity = 0;
147		this.minQuantity = 0;
148		this.stepQuantity = 1;
149		this.isDblQuantity = false;
150		this.canBuy = true;
151		this.precision = 6;
152		this.precisionFactor = Math.pow(10, this.precision);
153		this.bigData = false;
154		this.fullDisplayMode = false;
155		this.templateTheme = '';
156
157		this.currentPriceMode = '';
158		this.currentPrices = [];
159		this.currentPriceSelected = 0;
160		this.currentQuantityRanges = [];
161		this.currentQuantityRangeSelected = 0;
162
163		this.offers = [];
164		this.offerNum = 0;
165		this.treeProps = [];
166		this.selectedValues = {};
167		this.viewedParams = {};
168
169		this.prebuyPopup = null;
170		this.prebuyPopupContainer = null;
171		this.prebuyPopupSwipeBtn = null;
172		this.prebuyPopupOpenBtn = null;
173		this.prebuyPopupOverlay = null;
174		this.prebuyPopupCloseBtn = null;
175		this.prebuyPopupName = null;
176		this.prebuyPopupPict = null;
177
178		this.obProduct = null;
179		this.blockNodes = {};
180		this.obQuantity = null;
181		this.obQuantityUp = null;
182		this.obQuantityDown = null;
183		this.obQuantityLimit = {};
184		this.obQuantityCounter = null;
185		this.obName = null;
186		this.obPictSlider = null;
187		this.obPictSliderIndicator = null;
188
189		this.obPrice = null;
190		this.priceTwin = null;
191		this.blockOldPrice = null;
192		this.blockOldPriceTwin = null;
193		this.obPriceOld = null;
194		this.priceOldTwin = null;
195		this.priceDiscount = null;
196		this.priceDiscountTwin = null;
197
198		this.obTree = null;
199		this.obBuyBtn = null;
200		this.obAddBasketBtn = null;
201		this.obBasketActions = null;
202		this.obNotAvail = null;
203		this.obSubscribe = null;
204		this.obSkuProps = null;
205		this.obMeasure = null;
206		this.obMeasureContainer = null;
207		this.obCompare = null;
208
209		this.obPopupWin = null;
210		this.basketUrl = '';
211		this.basketParams = {};
212		this.isTouchDevice = BX.hasClass(document.documentElement, 'bx-touch');
213		this.hoverTimer = null;
214		this.hoverStateChangeForbidden = false;
215		this.mouseX = null;
216		this.mouseY = null;
217
218		this.useEnhancedEcommerce = false;
219		this.dataLayerName = 'dataLayer';
220		this.brandProperty = false;
221
222		this.errorCode = 0;
223
224		if (typeof arParams === 'object')
225		{
226			if (arParams.PRODUCT_TYPE)
227			{
228				this.productType = parseInt(arParams.PRODUCT_TYPE, 10);
229			}
230
231			this.showQuantity = arParams.SHOW_QUANTITY;
232			this.showAbsent = arParams.SHOW_ABSENT;
233			this.secondPict = arParams.SECOND_PICT;
234			this.showOldPrice = arParams.SHOW_OLD_PRICE;
235			this.showMaxQuantity = arParams.SHOW_MAX_QUANTITY;
236			this.relativeQuantityFactor = parseInt(arParams.RELATIVE_QUANTITY_FACTOR);
237			this.showSkuProps = arParams.SHOW_SKU_PROPS;
238			this.showSubscription = arParams.USE_SUBSCRIBE;
239			this.useOfferName = arParams.USE_OFFER_NAME;
240			this.viewedParams.siteId = arParams.SITE_ID;
241			if (arParams.ADD_TO_BASKET_ACTION)
242			{
243				this.basketAction = arParams.ADD_TO_BASKET_ACTION;
244			}
245
246			this.showClosePopup = arParams.SHOW_CLOSE_POPUP;
247			this.useCompare = arParams.DISPLAY_COMPARE;
248			this.fullDisplayMode = arParam
248s.PRODUCT_DISPLAY_MODE === 'Y';
249			this.bigData = arParams.BIG_DATA;
250			this.templateTheme = arParams.TEMPLATE_THEME || '';
251			this.useEnhancedEcommerce = arParams.USE_ENHANCED_ECOMMERCE === 'Y';
252			this.dataLayerName = arParams.DATA_LAYER_NAME;
253			this.brandProperty = arParams.BRAND_PROPERTY;
254
255			this.visual = arParams.VISUAL;
256
257			switch (this.productType)
258			{
259				case 0: // no catalog
260				case 1: // product
261				case 2: // set
262				case 7: // service
263					if (arParams.PRODUCT && typeof arParams.PRODUCT === 'object')
264					{
265						this.currentPriceMode = arParams.PRODUCT.ITEM_PRICE_MODE;
266						this.currentPrices = arParams.PRODUCT.ITEM_PRICES;
267						this.currentPriceSelected = arParams.PRODUCT.ITEM_PRICE_SELECTED;
268						this.currentQuantityRanges = arParams.PRODUCT.ITEM_QUANTITY_RANGES;
269						this.currentQuantityRangeSelected = arParams.PRODUCT.ITEM_QUANTITY_RANGE_SELECTED;
270
271						if (this.showQuantity)
272						{
273							this.product.checkQuantity = arParams.PRODUCT.CHECK_QUANTITY;
274							this.product.isDblQuantity = arParams.PRODUCT.QUANTITY_FLOAT;
275
276							if (this.product.checkQuantity)
277							{
278								this.product.maxQuantity = (this.product.isDblQuantity ? parseFloat(arParams.PRODUCT.MAX_QUANTITY) : parseInt(arParams.PRODUCT.MAX_QUANTITY, 10));
279							}
280
281							this.product.stepQuantity = (this.product.isDblQuantity ? parseFloat(arParams.PRODUCT.STEP_QUANTITY) : parseInt(arParams.PRODUCT.STEP_QUANTITY, 10));
282
283							this.checkQuantity = this.product.checkQuantity;
284							this.isDblQuantity = this.product.isDblQuantity;
285							this.stepQuantity = this.product.stepQuantity;
286							this.maxQuantity = this.product.maxQuantity;
287							this.minQuantity = this.currentPriceMode === 'Q'
288								? parseFloat(this.currentPrices[this.currentPriceSelected].MIN_QUANTITY)
289								: this.stepQuantity;
290
291							if (this.isDblQuantity)
292							{
293								this.stepQuantity = Math.round(this.stepQuantity * this.precisionFactor) / this.precisionFactor;
294							}
295						}
296
297						this.product.canBuy = arParams.PRODUCT.CAN_BUY;
298
299						if (arParams.PRODUCT.MORE_PHOTO_COUNT)
300						{
301							this.product.morePhotoCount = arParams.PRODUCT.MORE_PHOTO_COUNT;
302							this.product.morePhoto = arParams.PRODUCT.MORE_PHOTO;
303							this.product.sliderPhoto = arParams.PRODUCT.RESIZED_SLIDER;
304						}
305
306						if (arParams.PRODUCT.RCM_ID)
307						{
308							this.product.rcmId = arParams.PRODUCT.RCM_ID;
309						}
310
311						this.canBuy = this.product.canBuy;
312						this.product.name = arParams.PRODUCT.NAME;
313						this.product.pict = arParams.PRODUCT.PICT;
314						this.product.id = arParams.PRODUCT.ID;
315						this.viewedParams.skuId = parseInt(arParams.PRODUCT.ID);
316						this.viewedParams.productId = parseInt(arParams.PRODUCT.ID);
317						this.product.DETAIL_PAGE_URL = arParams.PRODUCT.DETAIL_PAGE_URL;
318
319						if (arParams.PRODUCT.ADD_URL)
320						{
321							this.product.addUrl = arParams.PRODUCT.ADD_URL;
322						}
323
324						if (arParams.PRODUCT.BUY_URL)
325						{
326							this.product.buyUrl = arParams.PRODUCT.BUY_URL;
327						}
328
329						if (arParams.BASKET && typeof arParams.BASKET === 'object')
330						{
331							this.basketData.useProps = arParams.BASKET.ADD_PROPS;
332							this.basketData.emptyProps = arParams.BASKET.EMPTY_PROPS;
333						}
334					}
335					else
336					{
337						this.errorCode = -1;
338					}
339
340					break;
341				case 3: // sku
342					if (arParams.PRODUCT && typeof arParams.PRODUCT === 'object')
343					{
344						this.product.name = arParams.PRODUCT.NAME;
345						this.product.id = arParams.PRODUCT.ID;
346						this.product.DETAIL_PAGE_URL = arParams.PRODUCT.DETAIL_PAGE_URL;
347						this.product.morePhotoCount = arParams.PRODUCT.MORE_PHOTO_COUNT;
348						this.product.morePhoto = arParams.PRODUCT.MORE_PHOTO;
349						this.product.sliderPhoto = arParams.PRODUCT.RESIZED_SLIDER;
350
351						if (arParams.PRODUCT.RCM_ID)
352						{
353							this.product.rcmId = arParams.PRODUCT.RCM_ID;
354						}
355					}
356
357					if (arParams.OFFERS && BX.type.isArray(arParams.OFFERS))
358					{
359						this.offers = arParams.OFFERS;
360						this.offerNum = 0;
361
362						if (arParams.OFFER_SELECTED)
363						{
364							this.offerNum = parseInt(arParams.OFFER_SELECTED, 10);
365						}
366
367						if (isNaN(this.offerNum))
368						{
369							this.offerNum = 0;
370						}
371
372						if (arParams.TREE_PROPS)
373						{
374							this.treeProps = arParams.TREE_PROPS;
375						}
376
377						if (arParams.DEFAULT_PICTURE)
378						{
379							this.defaultPict.pict = arParams.DEFAULT_PICTURE.PICTURE;
380							this.defaultPict.secondPict = arParams.DEFAULT_PICTURE.PICTURE_SECOND;
381						}
382					}
383
384					if (BX.type.isPlainObject(this.offers[this.offerNum]))
385					{
386						this.viewedParams.skuId = parseInt(this.offers[this.offerNum].ID);
387					}
388					else
389					{
390						this.viewedParams.skuId = 0;
391					}
392					this.viewedParams.productId = parseInt(arParams.PRODUCT.ID);
393
394					break;
395				default:
396					this.errorCode = -1;
397			}
398			if (arParams.BASKET && typeof arParams.BASKET === 'object')
399			{
400				if (arParams.BASKET.QUANTITY)
401				{
402					this.basketData.quantity = arParams.BASKET.QUANTITY;
403				}
404
405				if (arParams.BASKET.PROPS)
406				{
407					this.basketData.props = arParams.BASKET.PROPS;
408				}
409
410				if (arParams.BASKET.BASKET_URL)
411				{
412					this.basketData.basketUrl = arParams.BASKET.BASKET_URL;
413				}
414
415				if (3 === this.productType)
416				{
417					if (arParams.BASKET.SKU_PROPS)
418					{
419						this.basketData.sku_props = arParams.BASKET.SKU_PROPS;
420					}
421				}
422
423				if (arParams.BASKET.ADD_URL_TEMPLATE)
424				{
425					this.basketData.add_url = arParams.BASKET.ADD_URL_TEMPLATE;
426				}
427
428				if (arParams.BASKET.BUY_URL_TEMPLATE)
429				{
430					this.basketData.buy_url = arParams.BASKET.BUY_URL_TEMPLATE;
431				}
432
433				if (this.basketData.add_url === '' && this.basketData.buy_url === '')
434				{
435					this.errorCode = -1024;
436				}
437			}
438
439			if (this.useCompare)
440			{
441				if (arParams.COMPARE && typeof arParams.COMPARE === 'object')
442				{
443					if (arParams.COMPARE.COMPARE_PATH)
444					{
445						this.compareData.comparePath = arParams.COMPARE.COMPARE_PATH;
446					}
447
448					if (arParams.COMPARE.COMPARE_URL_TEMPLATE)
449					{
450						this.compareData.compareUrl = arParams.COMPARE.COMPARE_URL_TEMPLATE;
451					}
452					else
453					{
454						this.useCompare = false;
455					}
456
457					if (arParams.COMPARE.COMPARE_DELETE_URL_TEMPLATE)
458					{
459						this.compareData.compareDeleteUrl = arParams.COMPARE.COMPARE_DELETE_URL_TEMPLATE;
460					}
461					else
462					{
463						this.useCompare = false;
464					}
465				}
466				else
467				{
468					this.useCompare = false;
469				}
470			}
471
472			this.isFacebookConversionCustomizeProductEventEnabled =
473				arParams.IS_FACEBOOK_CONVERSION_CUSTOMIZE_PRODUCT_EVENT_ENABLED
474			;
475		}
476
477		if (this.errorCode === 0)
478		{
479			BX.ready(BX.delegate(this.init,this));
480		}
481	};
482
483	window.JCCatalogItemStoreV3.prototype = {
484		init: function()
485		{
486			var i = 0,
487				treeItems = null;
488
489			this.obProduct = BX(this.visual.ID);
490			if (!this.obProduct)
491			{
492				this.errorCode = -1;
493			}
494
495			this.obPictSlider = BX(this.visual.PICT_SLIDER_ID);
496			this.sliderControlLeft = this.obPictSlider.parentNode.querySelector(
497				'[data-entity="slider-control-left"]'
498			);
499			this.sliderControlRight = this.obPictSlider.parentNode.querySelector(
500				'[data-entity="slider-control-right"]'
501			);
502
503			if (this.product.morePhoto.length > 1 && this.sliderControlLeft && this.sliderControlRight)
504			{
505				this.sliderControlRight.style.display = '';
506				this.sliderControlLeft.style.display = '';
507			}
508
509			this.obPictSliderIndicator = BX(this.visual.PICT_SLIDER_ID + '_indicator');
510			this.obPictSliderPager = BX(this.visual.PICT_SLIDER_ID + '_pager');
511			if (!this.obPictSlider)
512			{
513				this.errorCode = -4;
514			}
515
516			this.obPrice = BX(this.visual.PRICE_ID);
517			if (!this.obPrice)
518			{
519				this.errorCode = -16;
520			}
521			this.priceTwin = BX(this.visual.PRICE_TWIN_ID);
522			if (!this.priceTwin)
523			{
524				this.errorCode = -16;
525			}
526			this.obPriceTotal = BX(this.visual.PRICE_TOTAL_ID);
527
528			this.initOldPriceDom();
529
530			this.prebuyPopup = BX(this.visual.PREBUY);
531			this.prebuyPopupSwipeBtn = BX(this.visual.PREBUY_SWIPE_BTN);
532			this.prebuyPopupContainer = BX(this.visual.PREBUY_CONTAINER);
533			this.prebuyPopupOverlay = BX(this.visual.PREBUY_OVERLAY);
534			this.prebuyPopupOpenBtn = BX(this.visual.PREBUY_OPEN_BTN);
535			this.prebuyPopupCloseBtn = BX(this.visual.PREBUY_CLOSE_BTN);
536			this.prebuyPopupPict = BX(this.visual.PREBUY_PICT);
537
538			if (this.showQuantity && this.visual.QUANTITY_ID)
539			{
540				this.obQuantity = BX(this.visual.QUANTITY_ID);
541				this.obQuantityCounter = BX(this.visual.QUANTITY_COUNTER_ID);
542				this.blockNodes.quantity = this.obProduct.querySelector('[data-entity="quantity-block"]');
543
544				if (!this.isTouchDevice)
545				{
546					BX.bind(this.obQuantity, 'focus', BX.proxy(this.onFocus, this));
547					BX.bind(this.obQuantity, 'blur', BX.proxy(this.onBlur, this));
548				}
549
550				if (this.visual.QUANTITY_UP_ID)
551				{
552					this.obQuantityUp = BX(this.visual.QUANTITY_UP_ID);
553				}
554
555				if (this.visual.QUANTITY_DOWN_ID)
556				{
557					this.obQuantityDown = BX(this.visual.QUANTITY_DOWN_ID);
558				}
559			}
560
561			if (this.visual.QUANTITY_LIMIT && this.showMaxQuantity !== 'N')
562			{
563				this.obQuantityLimit.all = BX(this.visual.QUANTITY_LIMIT);
564				if (this.obQuantityLimit.all)
565				{
566					this.obQuantityLimit.value = this.obQuantityLimit.all.querySelector('[data-entity="quantity-limit-value"]');
567					if (!this.obQuantityLimit.value)
568					{
569						this.obQuantityLimit.all = null;
570					}
571				}
572			}
573
574			if (this.productType === 3 && this.fullDisplayMode)
575			{
576				if (this.visual.TREE_ID)
577				{
578					this.obTree = BX(this.visual.TREE_ID);
579					if (!this.obTree)
580					{
581						this.errorCode = -256;
582					}
583				}
584
585				if (this.visual.QUANTITY_MEASURE)
586				{
587					this.obMeasure = BX(this.visual.QUANTITY_MEASURE);
588				}
589			}
590
591			this.obBasketActions = BX(this.visual.BASKET_ACTIONS_ID);
592			if (this.obBasketActions)
593			{
594				if (this.visual.BUY_ID)
595				{
596					this.obBuyBtn = BX(this.visual.BUY_ID);
597				}
598				if (this.visual.ADD_BASKET_ID)
599				{
600					this.obAddBasketBtn = BX(this.visual.ADD_BASKET_ID);
601				}
602			}
603
604			this.obNotAvail = BX(this.visual.NOT_AVAILABLE_MESS);
605
606			if (this.showSubscription)
607			{
608				this.obSubscribe = BX(this.visual.SUBSCRIBE_ID);
609			}
610
611			if (this.showSkuProps)
612			{
613				if (this.visual.DISPLAY_PROP_DIV)
614				{
615					this.obSkuProps = BX(this.visual.DISPLAY_PROP_DIV);
616				}
617			}
618
619			if (this.useOfferName)
620			{
621				this.obName = BX(this.visual.NAME);
622				this.prebuyPopupName = BX(this.visual.PREBUY_NAME);
623			}
624
625			if (this.errorCode === 0)
626			{
627				// product slider events
628				BX.bind(this.prebuyPopupOpenBtn, 'click', BX.proxy(this.prebuyPopupOpen, this));
629				BX.bind(this.prebuyPopupOverlay, 'click', BX.proxy(this.prebuyPopupClose, this));
630				BX.bind(this.prebuyPopupCloseBtn, 'click', BX.proxy(this.prebuyPopupClose, this));
631				BX.bind(this.obAddBasketBtn, 'click', BX.proxy(this.prebuyPopupClose, this));
632				BX.bind(this.sliderControlLeft, 'click', BX.proxy(this.slidePrev, this));
633				BX.bind(this.sliderControlRight, 'click', BX.proxy(this.slideNext, this));
634
635				if (this.isTouchDevice)
636				{
637					BX.bind(this.obPictSlider, 'touchstart', BX.proxy(this.touchStartEvent, this));
638					BX.bind(this.obPictSlider, 'touchend', BX.proxy(this.touchEndEvent, this));
639					BX.bind(this.obPictSlider, 'touchmove', BX.proxy(this.touchMoveEvent, this));
640					BX.bind(this.obPictSlider, 'touchcancel', BX.proxy(this.touchEndEvent, this));
641
642					BX.bind(this.prebuyPopupSwipeBtn, 'touchstart', BX.proxy(this.prebuyPopupStartEvent, this));
643					BX.bind(this.prebuyPopupSwipeBtn, 'touchend', BX.proxy(this.prebuyPopupEndEvent, this));
644					BX.bind(this.prebuyPopupSwipeBtn, 'touchmove', BX.proxy(this.prebuyPopupMoveEvent, this));
645					BX.bind(this.prebuyPopupSwipeBtn, 'touchcancel', BX.proxy(this.prebuyPopupEndEvent, this));
646				}
647				else
648				{
649					// product slider events
650					BX.bind(this.obProduct, 'mouseenter', BX.proxy(this.cycleSlider, this));
651					BX.bind(this.obProduct, 'mouseleave', BX.proxy(this.stopSlider, this));
652				}
653
654				if (this.bigData)
655				{
656					var links = BX.findChildren(this.obProduct, {tag:'a'}, true);
657					if (links)
658					{
659						for (i in links)
660						{
661							if (links.hasOwnProperty(i))
662							{
663								if (links[i].getAttribute('href') == this.product.DETAIL_PAGE_URL)
664								{
665									BX.bind(links[i], 'click', BX.proxy(this.rememberProductRecommendation, this));
666								}
667							}
668						}
669					}
670				}
671
672				if (this.showQuantity)
673				{
674					var startEventName = this.isTouchDevice ? 'touchstart' : 'mousedown';
675					var endEventName = this.isTouchDevice ? 'touchend' : 'mouseup';
676
677					if (this.obQuantityUp)
678					{
679						BX.bind(this.obQuantityUp, startEventName, BX.proxy(this.startQuantityInterval, this));
680						BX.bind(this.obQuantityUp, endEventName, BX.proxy(this.clearQuantityInterval, this));
681						BX.bind(this.obQuantityUp, 'mouseout', BX.proxy(this.clearQuantityInterval, this));
682						BX.bind(this.obQuantityUp, 'click', BX.delegate(this.quantityUp, this));
683					}
684
685					if (this.obQuantityDown)
686					{
687						BX.bind(this.obQuantityDown, startEventName, BX.proxy(this.startQuantityInterval, this));
688						BX.bind(this.obQuantityDown, endEventName, BX.proxy(this.clearQuantityInterval, this));
689						BX.bind(this.obQuantityDown, 'mouseout', BX.proxy(this.clearQuantityInterval, this));
690						BX.bind(this.obQuantityDown, 'click', BX.delegate(this.quantityDown, this));
691					}
692
693					if (this.obQuantity)
694					{
695						BX.bind(this.obQuantity, 'change', BX.delegate(this.quantityChange, this));
696						BX.bind(this.obQuantity, 'input', BX.delegate(this.quantityInput, this));
697					}
698				}
699
700				switch (this.productType)
701				{
702					case 0: // no catalog
703					case 1: // product
704					case 2: // set
705					case 7: // service
706						this.checkQuantityControls();
707
708						break;
709					case 3: // sku
710						if (this.offers.length > 0)
711						{
712							treeItems = BX.findChildren(this.obTree, {tagName: 'li'}, true);
713
714							if (treeItems && treeItems.length)
715							{
716								for (i = 0; i < treeItems.length; i++)
717								{
718									BX.bind(treeItems[i], 'click', BX.delegate(this.selectOfferProp, this));
719								}
720							}
721
722							this.setCurrent();
723						}
724
725						break;
726				}
727				this.initializeSlider();
728
729				if (this.obBuyBtn)
730				{
731					BX.bind(this.obBuyBtn, 'click', BX.proxy(this.buyBasket, this));
732				}
733				if (this.obAddBasketBtn)
734				{
735					BX.bind(this.obAddBasketBtn, 'click', BX.proxy(this.add2Basket, this));
736				}
737
738				if (this.useCompare)
739				{
740					this.obCompare = BX(this.visual.COMPARE_LINK_ID);
741					if (this.obCompare)
742					{
743						BX.bind(this.obCompare, 'click', BX.proxy(this.compare, this));
744					}
745
746					BX.addCustomEvent('onCatalogDeleteCompare', BX.proxy(this.checkDeletedCompare, this));
747				}
748			}
749		},
750
751		initOldPriceDom: function()
752		{
753			if (!(this.productType === 3 && this.fullDisplayMode))
754			{
755				this.showOldPrice = false;
756			}
757
758			if (this.showOldPrice)
759			{
760				if (BX.type.isNotEmptyString(this.visual.BLOCK_PRICE_OLD_ID))
761				{
762					this.blockOldPrice = BX(this.visual.BLOCK_PRICE_OLD_ID);
763					if (BX.type.isElementNode(this.blockOldPrice))
764					{
765						if (BX.type.isNotEmptyString(this.visual.PRICE_OLD_ID))
766						{
767							this.obPriceOld = BX(this.visual.PRICE_OLD_ID);
768						}
769						if (BX.type.isNotEmptyString(this.visual.PRICE_DISCOUNT_ID))
770						{
771							this.priceDiscount = BX(this.visual.PRICE_DISCOUNT_ID);
772						}
773					}
774				}
775				if (BX.type.isNotEmptyString(this.visual.BLOCK_PRICE_OLD_TWIN_ID))
776				{
777					this.blockOldPriceTwin = BX(this.visual.BLOCK_PRICE_OLD_TWIN_ID);
778					if (BX.type.isElementNode(this.blockOldPriceTwin))
779					{
780						if (BX.type.isNotEmptyString(this.visual.PRICE_OLD_TWIN_ID))
781						{
782							this.priceOldTwin = BX(this.visual.PRICE_OLD_TWIN_ID);
783						}
784						if (BX.type.isNotEmptyString(this.visual.PRICE_DISCOUNT_TWIN_ID))
785						{
786							this.priceDiscountTwin = BX(this.visual.PRICE_DISCOUNT_TWIN_ID);
787						}
788					}
789				}
790
791			}
792		},
793
794		setAnalyticsDataLayer: function(action)
795		{
796			if (!this.useEnhancedEcommerce || !this.dataLayerName)
797				return;
798
799			var item = {},
800				info = {},
801				variants = [],
802				i, k, j, propId, skuId, propValues;
803
804			switch (this.productType)
805			{
806				case 0: //no catalog
807				case 1: //product
808				case 2: //set
809				case 7: // service
810					item = {
811						'id': this.product.id,
812						'name': this.product.name,
813						'price': this.currentPrices[this.currentPriceSelected] && this.currentPrices[this.currentPriceSelected].PRICE,
814						'brand': BX.type.isArray(this.brandProperty) ? this.brandProperty.join('/') : this.brandProperty
815					};
816					break;
817				case 3: //sku
818					for (i in this.offers[this.offerNum].TREE)
819					{
820						if (this.offers[this.offerNum].TREE.hasOwnProperty(i))
821						{
822							propId = i.substring(5);
823							skuId = this.offers[this.offerNum].TREE[i];
824
825							for (k in this.treeProps)
826							{
827								if (this.treeProps.hasOwnProperty(k) && this.treeProps[k].ID == propId)
828								{
829									for (j in this.treeProps[k].VALUES)
830									{
831										propValues = this.treeProps[k].VALUES[j];
832										if (propValues.ID == skuId)
833										{
834											variants.push(propValues.NAME);
835											break;
836										}
837									}
838
839								}
840							}
841						}
842					}
843
844					item = {
845						'id': this.offers[this.offerNum].ID,
846						'name': this.offers[this.offerNum].NAME,
847						'price': this.currentPrices[this.currentPriceSelected] && this.currentPrices[this.currentPriceSelected].PRICE,
848						'brand': BX.type.isArray(this.brandProperty) ? this.brandProperty.join('/') : this.brandProperty,
849						'variant': variants.join('/')
850					};
851					break;
852			}
853
854			switch (action)
855			{
856				case 'addToCart':
857					info = {
858						'ecommerce': {
859							'currencyCode': this.currentPrices[this.currentPriceSelected] && this.currentPrices[this.currentPriceSelected].CURRENCY || '',
860							'add': {
861								'products': [{
862									'name': item.name || '',
863									'id': item.id || '',
864									'price': item.price || 0,
865									'brand': item.brand || '',
866									'category': item.category || '',
867									'variant': item.variant || '',
868									'quantity': this.showQuantity && this.obQuantity ? this.obQuantity.value : 1
869								}]
870							}
871						}
872					};
873					break;
874			}
875
876			window[this.dataLayerName] = window[this.dataLayerName] || [];
877			window[this.dataLayerName].push(info);
878		},
879
880		hoverOn: function(event)
881		{
882			clearTimeout(this.hoverTimer);
883			this.obProduct.style.height = getComputedStyle(this.obProduct).height;
884			BX.addClass(this.obProduct, 'hover');
885
886			BX.PreventDefault(event);
887		},
888
889		hoverOff: function(event)
890		{
891			if (this.hoverStateChangeForbidden)
892				return;
893
894			BX.removeClass(this.obProduct, 'hover');
895			this.hoverTimer = setTimeout(
896				BX.delegate(function(){
897					this.obProduct.style.height = 'auto';
898				}, this),
899				300
900			);
901
902			BX.PreventDefault(event);
903		},
904
905		onFocus: function()
906		{
907			this.hoverStateChangeForbidden = true;
908			BX.bind(document, 'mousemove', BX.proxy(this.captureMousePosition, this));
909		},
910
911		onBlur: function()
912		{
913			this.hoverStateChangeForbidden = false;
914			BX.unbind(document, 'mousemove', BX.proxy(this.captureMousePosition, this));
915
916			var cursorElement = document.elementFromPoint(this.mouseX, this.mouseY);
917			if (!cursorElement || !this.obProduct.contains(cursorElement))
918			{
919				this.hoverOff();
920			}
921		},
922
923		captureMousePosition: function(event)
924		{
925			this.mouseX = event.clientX;
926			this.mouseY = event.clientY;
927		},
928
929		getCookie: function(name)
930		{
931			var matches = document.cookie.match(new RegExp(
932				"(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)"
933			));
934
935			return matches ? decodeURIComponent(matches[1]) : null;
936		},
937
938		rememberProductRecommendation: function()
939		{
940			// save to RCM_PRODUCT_LOG
941			var cookieName = BX.cookie_prefix + '_RCM_PRODUCT_LOG',
942				cookie = this.getCookie(cookieName),
943				itemFound = false;
944
945			var cItems = [],
946				cItem;
947
948			if (cookie)
949			{
950				cItems = cookie.split('.');
951			}
952
953			var i = cItems.length;
954
955			while (i--)
956			{
957				cItem = cItems[i].split('-');
958
959				if (cItem[0] == this.product.id)
960				{
961					// it's already in recommendations, update the date
962					cItem = cItems[i].split('-');
963
964					// update rcmId and date
965					cItem[1] = this.product.rcmId;
966					cItem[2] = BX.current_server_time;
967
968					cItems[i] = cItem.join('-');
969					itemFound = true;
970				}
971				else
972				{
973					if ((BX.current_server_time - cItem[2]) > 3600 * 24 * 30)
974					{
975						cItems.splice(i, 1);
976					}
977				}
978			}
979
980			if (!itemFound)
981			{
982				// add recommendation
983				cItems.push([this.product.id, this.product.rcmId, BX.current_server_time].join('-'));
984			}
985
986			// serialize
987			var plNewCookie = cItems.join('.'),
988				cookieDate = new Date(new Date().getTime() + 1000 * 3600 * 24 * 365 * 10).toUTCString();
989
990			document.cookie = cookieName + "=" + plNewCookie + "; path=/; expires=" + cookieDate + "; domain=" + BX.cookie_domain;
991		},
992
993		startQuantityInterval: function()
994		{
995			var target = BX.proxy_context;
996			var func = target.id === this.visual.QUANTITY_DOWN_ID
997				? BX.proxy(this.quantityDown, this)
998				: BX.proxy(this.quantityUp, this);
999
1000			this.quantityDelay = setTimeout(
1001				BX.delegate(function() {
1002					this.quantityTimer = setInterval(func, 250);
1003				}, this),
1004				300
1005			);
1006		},
1007
1008		clearQuantityInterval: function()
1009		{
1010			clearTimeout(this.quantityDelay);
1011			clearInterval(this.quantityTimer);
1012		},
1013
1014		quantityUp: function()
1015		{
1016			var curValue = 0,
1017				boolSet = true;
1018
1019			if (this.errorCode === 0 && this.showQuantity && this.canBuy)
1020			{
1021				curValue = (this.isDblQuantity ? parseFloat(this.obQuantity.value) : parseInt(this.obQuantity.value, 10));
1022				if (!isNaN(curValue))
1023				{
1024					curValue += this.stepQuantity;
1025					if (this.checkQuantity)
1026					{
1027						if (curValue > this.maxQuantity)
1028						{
1029							boolSet = false;
1030						}
1031					}
1032
1033					if (boolSet)
1034					{
1035						if (this.isDblQuantity)
1036						{
1037							curValue = Math.round(curValue * this.precisionFactor) / this.precisionFactor;
1038						}
1039
1040						this.obQuantity.value = curValue;
1041						this.obQuantityCounter.innerText = curValue;
1042
1043						if (this.checkQuantity)
1044						{
1045							if (curValue == this.maxQuantity)
1046							{
1047								this.disableQuantityButton(this.obQuantityUp);
1048							}
1049						}
1050						this.enableQuantityButton(this.obQuantityDown);
1051						this.setPrice();
1052					}
1053					else
1054					{
1055						this.disableQuantityButton(this.obQuantityUp);
1056					}
1057				}
1058			}
1059		},
1060
1061		quantityDown: function()
1062		{
1063			var curValue = 0,
1064				boolSet = true;
1065
1066			if (this.errorCode === 0 && this.showQuantity && this.canBuy)
1067			{
1068				curValue = (this.isDblQuantity ? parseFloat(this.obQuantity.value) : parseInt(this.obQuantity.value, 10));
1069				if (!isNaN(curValue))
1070				{
1071					curValue -= this.stepQuantity;
1072
1073					this.checkPriceRange(curValue);
1074
1075					if (curValue < this.minQuantity)
1076					{
1077						boolSet = false;
1078					}
1079
1080					if (boolSet)
1081					{
1082						if (this.isDblQuantity)
1083						{
1084							curValue = Math.round(curValue * this.precisionFactor) / this.precisionFactor;
1085						}
1086
1087						this.obQuantity.value = curValue;
1088						this.obQuantityCounter.innerText = curValue;
1089
1090						if (curValue == this.minQuantity)
1091						{
1092							this.disableQuantityButton(this.obQuantityDown);
1093						}
1094						this.enableQuantityButton(this.obQuantityUp);
1095						this.setPrice();
1096					}
1097					else
1098					{
1099						this.disableQuantityButton(this.obQuantityDown);
1100					}
1101				}
1102			}
1103		},
1104
1105		quantityChange: function()
1106		{
1107			var curValue = 0,
1108				intCount;
1109
1110			if (this.errorCode === 0 && this.showQuantity)
1111			{
1112				if (this.canBuy)
1113				{
1114					curValue = this.isDblQuantity ? parseFloat(this.obQuantity.value) : Math.round(this.obQuantity.value);
1115					if (!isNaN(curValue))
1116					{
1117						if (this.checkQuantity)
1118						{
1119							if (curValue > this.maxQuantity)
1120							{
1121								curValue = this.maxQuantity;
1122							}
1123							if (curValue == this.maxQuantity)
1124							{
1125								this.disableQuantityButton(this.obQuantityUp);
1126							}
1127						}
1128
1129						this.checkPriceRange(curValue);
1130
1131						intCount = Math.floor(
1132							Math.round(curValue * this.precisionFactor / this.stepQuantity) / this.precisionFactor
1133						) || 1;
1134						curValue = (intCount <= 1 ? this.stepQuantity : intCount * this.stepQuantity);
1135						curValue = Math.round(curValue * this.precisionFactor) / this.precisionFactor;
1136
1137						if (curValue < this.minQuantity)
1138						{
1139							curValue = this.minQuantity;
1140						}
1141						if (curValue == this.minQuantity)
1142						{
1143							this.disableQuantityButton(this.obQuantityDown);
1144						}
1145
1146						this.obQuantity.value = curValue;
1147						this.obQuantityCounter.innerText = curValue;
1148					}
1149					else
1150					{
1151						this.disableQuantityButton(this.obQuantityDown);
1152						this.enableQuantityButton(this.obQuantityUp);
1153						this.obQuantity.value = this.minQuantity;
1154						this.obQuantityCounter.innerText = this.minQuantity;
1155					}
1156				}
1157				else
1158				{
1159					this.disableQuantityButton(this.obQuantityDown);
1160					this.enableQuantityButton(this.obQuantityUp);
1161					this.obQuantity.value = this.minQuantity;
1162					this.obQuantityCounter.innerText = this.minQuantity;
1163				}
1164
1165				this.setPrice();
1166			}
1167		},
1168
1169		quantityInput: function()
1170		{
1171			var curValue = 0,
1172				intCount;
1173
1174			if (this.errorCode === 0 && this.showQuantity)
1175			{
1176				if (this.canBuy)
1177				{
1178					curValue = this.isDblQuantity ? parseFloat(this.obQuantity.value) : Math.round(this.obQuantity.value);
1179					if (!isNaN(curValue))
1180					{
1181						if (this.checkQuantity)
1182						{
1183							if (curValue > this.maxQuantity)
1184							{
1185								curValue = this.maxQuantity;
1186							}
1187						}
1188
1189						this.checkPriceRange(curValue);
1190
1191						intCount = Math.floor(
1192							Math.round(curValue * this.precisionFactor / this.stepQuantity) / this.precisionFactor
1193						) || 1;
1194						curValue = (intCount <= 1 ? this.stepQuantity : intCount * this.stepQuantity);
1195						curValue = Math.round(curValue * this.precisionFactor) / this.precisionFactor;
1196
1197						if (curValue < this.minQuantity)
1198						{
1199							curValue = this.minQuantity;
1200						}
1201						this.obQuantityCounter.innerText = curValue;
1202					}
1203				}
1204			}
1205		},
1206
1207		quantitySet: function(index)
1208		{
1209			var resetQuantity, strLimit;
1210
1211			var newOffer = this.offers[index],
1212				oldOffer = this.offers[this.offerNum];
1213
1214			if (this.errorCode === 0)
1215			{
1216				this.canBuy = newOffer.CAN_BUY;
1217
1218				this.currentPriceMode = newOffer.ITEM_PRICE_MODE;
1219				this.currentPrices = newOffer.ITEM_PRICES;
1220				this.currentPriceSelected = newOffer.ITEM_PRICE_SELECTED;
1221				this.currentQuantityRanges = newOffer.ITEM_QUANTITY_RANGES;
1222				this.currentQuantityRangeSelected = newOffer.ITEM_QUANTITY_RANGE_SELECTED;
1223
1224				if (this.canBuy)
1225				{
1226					if (this.blockNodes.quantity)
1227					{
1228						BX.style(this.blockNodes.quantity, 'display', '');
1229					}
1230
1231					if (this.obBasketActions)
1232					{
1233						BX.style(this.obBasketActions, 'display', '');
1234					}
1235
1236					if (this.obNotAvail)
1237					{
1238						BX.style(this.obNotAvail, 'display', 'none');
1239					}
1240
1241					if (this.obSubscribe)
1242					{
1243						BX.style(this.obSubscribe, 'display', 'none');
1244					}
1245				}
1246				else
1247				{
1248					if (this.blockNodes.quantity)
1249					{
1250						BX.style(this.blockNodes.quantity, 'display', 'none');
1251					}
1252
1253					if (this.obBasketActions)
1254					{
1255						BX.style(this.obBasketActions, 'display', 'none');
1256					}
1257
1258					if (this.obNotAvail)
1259					{
1260						BX.style(this.obNotAvail, 'display', '');
1261					}
1262
1263					if (this.obSubscribe)
1264					{
1265						if (newOffer.CATALOG_SUBSCRIBE === 'Y')
1266						{
1267							BX.style(this.obSubscribe, 'display', '');
1268							this.obSubscribe.setAttribute('data-item', newOffer.ID);
1269							BX(this.visual.SUBSCRIBE_ID + '_hidden').click();
1270						}
1271						else
1272						{
1273							BX.style(this.obSubscribe, 'display', 'none');
1274						}
1275					}
1276				}
1277
1278				this.isDblQuantity = newOffer.QUANTITY_FLOAT;
1279				this.checkQuantity = newOffer.CHECK_QUANTITY;
1280
1281				if (this.isDblQuantity)
1282				{
1283					this.stepQuantity = Math.round(parseFloat(newOffer.STEP_QUANTITY) * this.precisionFactor) / this.precisionFactor;
1284					this.maxQuantity = parseFloat(newOffer.MAX_QUANTITY);
1285					this.minQuantity = this.currentPriceMode === 'Q' ? parseFloat(this.currentPrices[this.currentPriceSelected].MIN_QUANTITY) : this.stepQuantity;
1286				}
1287				else
1288				{
1289					this.stepQuantity = parseInt(newOffer.STEP_QUANTITY, 10);
1290					this.maxQuantity = parseInt(newOffer.MAX_QUANTITY, 10);
1291					this.minQuantity = this.currentPriceMode === 'Q' ? parseInt(this.currentPrices[this.currentPriceSelected].MIN_QUANTITY) : this.stepQuantity;
1292				}
1293
1294				if (this.showQuantity)
1295				{
1296					var isDifferentMinQuantity = oldOffer.ITEM_PRICES.length
1297						&& oldOffer.ITEM_PRICES[oldOffer.ITEM_PRICE_SELECTED]
1298						&& oldOffer.ITEM_PRICES[oldOffer.ITEM_PRICE_SELECTED].MIN_QUANTITY != this.minQuantity;
1299
1300					if (this.isDblQuantity)
1301					{
1302						resetQuantity = Math.round(parseFloat(oldOffer.STEP_QUANTITY) * this.precisionFactor) / this.precisionFactor !== this.stepQuantity
1303							|| isDifferentMinQuantity
1304							|| oldOffer.MEASURE !== newOffer.MEASURE
1305							|| (
1306								this.checkQuantity
1307								&& parseFloat(oldOffer.MAX_QUANTITY) > this.maxQuantity
1308								&& parseFloat(this.obQuantity.value) > this.maxQuantity
1309							);
1310					}
1311					else
1312					{
1313						resetQuantity = parseInt(oldOffer.STEP_QUANTITY, 10) !== this.stepQuantity
1314							|| isDifferentMinQuantity
1315							|| oldOffer.MEASURE !== newOffer.MEASURE
1316							|| (
1317								this.checkQuantity
1318								&& parseInt(oldOffer.MAX_QUANTITY, 10) > this.maxQuantity
1319								&& parseInt(this.obQuantity.value, 10) > this.maxQuantity
1320							);
1321					}
1322
1323					this.obQuantity.disabled = !this.canBuy;
1324
1325					if (resetQuantity)
1326					{
1327						this.obQuantity.value = this.minQuantity;
1328						this.obQuantityCounter.innerText = this.minQuantity;
1329					}
1330					if (this.obQuantity.value == this.minQuantity)
1331					{
1332						this.disableQuantityButton(this.obQuantityDown);
1333					}
1334					else
1335					{
1336						this.enableQuantityButton(this.obQuantityDown);
1337					}
1338					if (this.checkQuantity)
1339					{
1340						if (this.obQuantity.value >= this.maxQuantity)
1341						{
1342							this.disableQuantityButton(this.obQuantityUp);
1343						}
1344						else
1345						{
1346							this.enableQuantityButton(this.obQuantityUp);
1347						}
1348					}
1349					else
1350					{
1351						this.enableQuantityButton(this.obQuantityUp);
1352					}
1353
1354					if (this.obMeasure)
1355					{
1356						if (newOffer.MEASURE)
1357						{
1358							BX.adjust(this.obMeasure, {html: newOffer.MEASURE});
1359						}
1360						else
1361						{
1362							BX.adjust(this.obMeasure, {html: ''});
1363						}
1364					}
1365				}
1366
1367				if (this.obQuantityLimit.all)
1368				{
1369					if (!this.checkQuantity || this.maxQuantity == 0)
1370					{
1371						BX.adjust(this.obQuantityLimit.value, {html: ''});
1372						BX.adjust(this.obQuantityLimit.all, {style: {display: 'none'}});
1373					}
1374					else
1375					{
1376						if (this.showMaxQuantity === 'M')
1377						{
1378							strLimit = (this.maxQuantity / this.stepQuantity >= this.relativeQuantityFactor)
1379								? BX.message('RELATIVE_QUANTITY_MANY')
1380								: BX.message('RELATIVE_QUANTITY_FEW');
1381						}
1382						else
1383						{
1384							strLimit = this.maxQuantity;
1385
1386							if (newOffer.MEASURE)
1387							{
1388								strLimit += (' ' + newOffer.MEASURE);
1389							}
1390						}
1391
1392						BX.adjust(this.obQuantityLimit.value, {html: strLimit});
1393						BX.adjust(this.obQuantityLimit.all, {style: {display: ''}});
1394					}
1395				}
1396			}
1397		},
1398
1399		disableQuantityButton: function(button)
1400		{
1401			button.classList.add('catalog-section-item-quantity-btn-disabled');
1402		},
1403
1404		enableQuantityButton: function(button)
1405		{
1406			button.classList.remove('catalog-section-item-quantity-btn-disabled');
1407		},
1408
1409		initializeSlider: function()
1410		{
1411			var wrap = this.obPictSlider.getAttribute('data-slider-wrap');
1412			if (wrap)
1413			{
1414				this.slider.options.wrap = (wrap === 'true');
1415			}
1416			else
1417			{
1418				this.slider.options.wrap = this.defaultSliderOptions.wrap;
1419			}
1420
1421			if (!this.isTouchDevice)
1422			{
1423				if (this.obPictSliderIndicator)
1424				{
1425					BX.bindDelegate(
1426						this.obPictSliderIndicator,
1427						'mouseover',
1428						{'attribute': 'data-entity'},
1429						BX.proxy(this.sliderClickHandler, this)
1430					);
1431
1432					if (this.obPictSliderPager)
1433					{
1434						BX.bindDelegate(
1435							this.obPictSliderPager,
1436							'mouseover',
1437							{ 'attribute': 'data-entity' },
1438							BX.proxy(this.sliderClickHandler, this)
1439						);
1440					}
1441				}
1442			}
1443		},
1444
1445		checkTouch: function(event)
1446		{
1447			if (!event || !event.changedTouches)
1448				return false;
1449
1450			return event.changedTouches[0].identifier === this.touch.identifier;
1451		},
1452
1453		touchStartEvent: function(event)
1454		{
1455			if (event.touches.length != 1)
1456				return;
1457
1458			this.touch = event.changedTouches[0];
1459		},
1460
1461		touchEndEvent: function(event)
1462		{
1463			if (!this.checkTouch(event))
1464				return;
1465
1466			if (this.touchDirectionEvent(event) === "left")
1467			{
1468				this.slideNext();
1469			}
1470
1471			if (this.touchDirectionEvent(event) === "right")
1472			{
1473				this.slidePrev();
1474			}
1475
1476		},
1477
1478		touchMoveEvent: function(event)
1479		{
1480			if (!this.checkTouch(event))
1481				return;
1482
1483			var deltaX = this.touch.pageX - event.changedTouches[0].pageX,
1484				deltaY = this.touch.pageY - event.changedTouches[0].pageY;
1485
1486			if (Math.abs(deltaY) >= Math.abs(deltaX) + 30)
1487			{
1488				document.body.style.overflow = "";
1489			}
1490			else
1491			{
1492				document.body.style.overflow = "hidden";
1493			}
1494		},
1495
1496		touchDirectionEvent: function(event)
1497		{
1498			if (!this.checkTouch(event))
1499				return;
1500
1501			var deltaX = this.touch.pageX - event.changedTouches[0].pageX,
1502				deltaY = this.touch.pageY - event.changedTouches[0].pageY;
1503
1504			if (Math.abs(deltaX) >= Math.abs(deltaY))
1505			{
1506				if (deltaX > 0)
1507				{
1508					return "left";
1509				}
1510
1511				if (deltaX < 0)
1512				{
1513					return "right";
1514				}
1515			}
1516			else
1517			{
1518				if (deltaY > 0)
1519				{
1520					return "up"
1521				}
1522
1523				if (deltaY < 0)
1524				{
1525					return "down"
1526				}
1527			}
1528		},
1529
1530		prebuyPopupOpen: function()
1531		{
1532			var scrollTop = document.documentElement.scrollTop;
1533			document.documentElement.setAttribute("data-scroll-top", scrollTop);
1534			BX.addClass(this.prebuyPopup, "opened");
1535			BX.removeClass(this.prebuyPopup, "closed");
1536			document.documentElement.style.overflow = "hidden";
1537			document.documentElement.style.height = "100vh";
1538			document.documentElement.scrollTop = scrollTop;
1539			this.sendToViewed();
1540		},
1541
1542		prebuyPopupClose: function()
1543		{
1544			var scrollTop = document.documentElement.getAttribute("data-scroll-top");
1545			BX.addClass(this.prebuyPopup, "closed");
1546			BX.removeClass(this.prebuyPopup, "opened");
1547			document.documentElement.style.overflow = "";
1548			document.documentElement.style.height = "";
1549			document.documentElement.scrollTop = scrollTop;
1550			document.documentElement.removeAttribute("data-scroll-top");
1551		},
1552
1553		prebuyPopupStartEvent: function(event)
1554		{
1555			if (event.touches.length != 1)
1556				return;
1557
1558			this.touch = event.changedTouches[0];
1559			this.prebuyPopupContainer.style.transition = "linear all";
1560		},
1561
1562		prebuyPopupEndEvent: function(event)
1563		{
1564			if (!this.checkTouch(event))
1565				return;
1566
1567			var deltaX = this.touch.pageX - event.changedTouches[0].pageX,
1568				deltaY = this.touch.pageY - event.changedTouches[0].pageY;
1569
1570			if (deltaY  > -150){
1571				this.prebuyPopupContainer.style.bottom = "";
1572			}
1573
1574			if (deltaY  < -50){
1575				this.prebuyPopupContainer.style.bottom = "";
1576				this.prebuyPopupContainer.style.transition = "";
1577				setTimeout(BX.delegate(this.prebuyPopupClose, this), 10);
1578			}
1579		},
1580
1581		prebuyPopupMoveEvent: function(event)
1582		{
1583			if (!this.checkTouch(event))
1584				return;
1585
1586			var deltaX = this.touch.pageX - event.changedTouches[0].pageX,
1587				deltaY = this.touch.pageY - event.changedTouches[0].pageY;
1588
1589			if (this.touchDirectionEvent(event) == "down")
1590			{
1591				if (deltaY < 0)
1592				{
1593					this.prebuyPopupContainer.style.bottom = deltaY + "px";
1594				}
1595			}
1596		},
1597
1598		sliderClickHandler: function(event)
1599		{
1600			var target = BX.getEventTarget(event),
1601				slideIndex = target.getAttribute('data-go-to');
1602
1603			if (slideIndex)
1604			{
1605				this.slideTo(slideIndex)
1606			}
1607
1608			BX.PreventDefault(event);
1609		},
1610
1611		slideNext: function()
1612		{
1613			if (this.slider.sliding)
1614				return;
1615
1616			return this.slide('next');
1617		},
1618
1619		slidePrev: function()
1620		{
1621			if (this.slider.sliding)
1622				return;
1623
1624			return this.slide('prev');
1625		},
1626
1627		slideTo: function(pos)
1628		{
1629			this.slider.active = BX.findChild(this.obPictSlider, {className: 'active'}, true, false);
1630			//this.slider.progress && (this.slider.interval = true);
1631
1632			var activeIndex = this.getItemIndex(this.slider.active);
1633
1634			if (pos > (this.slider.items.length - 1) || pos < 0)
1635				return;
1636
1637			if (this.slider.sliding)
1638				return false;
1639
1640			if (activeIndex == pos)
1641			{
1642				//this.stopSlider();
1643				this.cycleSlider();
1644				return;
1645			}
1646
1647			return this.slide(pos > activeIndex ? 'next' : 'prev', this.eq(this.slider.items, pos));
1648		},
1649
1650		slide: function(type, next)
1651		{
1652			document.body.style.overflow = "";
1653
1654			var active = BX.findChild(this.obPictSlider, {className: 'catalog-section-item-slider-image active'}, true, false),
1655				isCycling = this.slider.interval,
1656				direction = type === 'next' ? 'left' : 'right';
1657
1658			next = next || this.getItemForDirection(type, active);
1659
1660			if (BX.hasClass(next, 'active'))
1661			{
1662				return (this.slider.sliding = false);
1663			}
1664
1665			this.slider.sliding = true;
1666
1667			isCycling && this.stopSlider();
1668
1669			if (this.obPictSliderIndicator)
1670			{
1671				BX.removeClass(this.obPictSliderIndicator.querySelector('.active'), 'active');
1672				var nextIndicator = this.obPictSliderIndicator.querySelectorAll("[data-entity='slider-control']")[this.getItemIndex(next)];
1673				nextIndicator && BX.addClass(nextIndicator, 'active');
1674			}
1675
1676			if (BX.hasClass(this.obPictSlider, 'catalog-section-item-slider-images-container') && !BX.browser.IsIE())
1677			{
1678				var self = this;
1679				BX.addClass(next, type);
1680				next.offsetWidth; // force reflow
1681				BX.addClass(active, direction);
1682				BX.addClass(next, direction);
1683				if (this.isTouchDevice)
1684				{
1685					BX.addClass(this.obPictSlider, 'catalog-section-item-slider-animation');
1686
1687					setTimeout(function() {
1688						BX.addClass(next, 'active');
1689						BX.removeClass(active, 'active');
1690						BX.removeClass(active, direction);
1691						BX.removeClass(next, type);
1692						BX.removeClass(next, direction);
1693						self.slider.sliding = false;
1694					}, 250);
1695				}
1696				else
1697				{
1698					BX.addClass(next, 'active');
1699					BX.removeClass(active, 'active');
1700					BX.removeClass(active, direction);
1701					BX.removeClass(next, type);
1702					BX.removeClass(next, direction);
1703					self.slider.sliding = false;
1704				}
1705			}
1706			else
1707			{
1708				BX.addClass(next, 'active');
1709				this.slider.sliding = false;
1710			}
1711
1712			isCycling && this.cycleSlider();
1713		},
1714
1715		stopSlider: function(event)
1716		{
1717			event || (this.slider.paused = true);
1718
1719			this.slider.interval && clearInterval(this.slider.interval);
1720
1721			if (this.slider.progress)
1722			{
1723				this.slider.progress.stop();
1724
1725				var width = parseInt(this.obPictSliderProgressBar.style.width);
1726
1727				this.slider.progress.options.duration = this.slider.options.interval * width / 200;
1728				this.slider.progress.options.start = {width: width * 10};
1729				this.slider.progress.options.finish = {width: 0};
1730				this.slider.progress.options.complete = null;
1731				this.slider.progress.animate();
1732			}
1733		},
1734
1735		cycleSlider: function(event)
1736		{
1737			event || (this.slider.paused = false);
1738
1739			this.slider.interval && clearInterval(this.slider.interval);
1740
1741			if (this.slider.options.interval && !this.slider.paused)
1742			{
1743				if (this.slider.progress)
1744				{
1745					this.slider.progress.stop();
1746
1747					var width = parseInt(this.obPictSliderProgressBar.style.width);
1748
1749					this.slider.progress.options.duration = this.slider.options.interval * (100 - width) / 100;
1750					this.slider.progress.options.start = {width: width * 10};
1751					this.slider.progress.options.finish = {width: 1000};
1752					this.slider.progress.options.complete = BX.delegate(function(){
1753						this.slider.interval = true;
1754						this.slideNext();
1755					}, this);
1756					this.slider.progress.animate();
1757				}
1758				else
1759				{
1760					this.slider.interval = setInterval(BX.proxy(this.slideNext, this), this.slider.options.interval);
1761				}
1762			}
1763		},
1764
1765		getItemForDirection: function(direction, active)
1766		{
1767			var activeIndex = this.getItemIndex(active),
1768				willWrap = direction === 'prev' && activeIndex === 0
1769					|| direction === 'next' && activeIndex == (this.slider.items.length - 1);
1770
1771			if (willWrap && !this.slider.options.wrap)
1772				return active;
1773
1774			var delta = direction === 'prev' ? -1 : 1,
1775				itemIndex = (activeIndex + delta) % this.slider.items.length;
1776
1777			return this.eq(this.slider.items, itemIndex);
1778		},
1779
1780		getItemIndex: function(item)
1781		{
1782			this.slider.items = BX.findChildren(item.parentNode, {className: 'catalog-section-item-slider-image'}, true);
1783
1784			return this.slider.items.indexOf(item || this.slider.active);
1785		},
1786
1787		eq: function(obj, i)
1788		{
1789			var len = obj.length,
1790				j = +i + (i < 0 ? len : 0);
1791
1792			return j >= 0 && j < len ? obj[j] : {};
1793		},
1794
1795		selectOfferProp: function()
1796		{
1797			var i = 0,
1798				value = '',
1799				strTreeValue = '',
1800				arTreeItem = [],
1801				rowItems = null,
1802				target = BX.proxy_context;
1803
1804			if (target && target.hasAttribute('data-treevalue'))
1805			{
1806				if (BX.hasClass(target, 'selected'))
1807					return;
1808
1809				strTreeValue = target.getAttribute('data-treevalue');
1810				arTreeItem = strTreeValue.split('_');
1811				if (this.searchOfferPropIndex(arTreeItem[0], arTreeItem[1]))
1812				{
1813					rowItems = BX.findChildren(target.parentNode, {tagName: 'li'}, false);
1814					if (rowItems && 0 < rowItems.length)
1815					{
1816						for (i = 0; i < rowItems.length; i++)
1817						{
1818							value = rowItems[i].getAttribute('data-onevalue');
1819							if (value === arTreeItem[1])
1820							{
1821								BX.addClass(rowItems[i], 'selected');
1822							}
1823							else
1824							{
1825								BX.removeClass(rowItems[i], 'selected');
1826							}
1827						}
1828					}
1829
1830					if (
1831						this.isFacebookConversionCustomizeProductEventEnabled
1832						&& BX.Type.isArrayFilled(this.offers)
1833						&& BX.Type.isObject(this.offers[this.offerNum])
1834					)
1835					{
1836						BX.ajax.runAction(
1837							'sale.facebookconversion.customizeProduct',
1838							{
1839								data: {
1840									offerId: this.offers[this.offerNum]['ID']
1841								}
1842							}
1843						);
1844					}
1845				}
1846			}
1847		},
1848
1849		searchOfferPropIndex: function(strPropID, strPropValue)
1850		{
1851			var strName = '',
1852				arShowValues = false,
1853				i, j,
1854				arCanBuyValues = [],
1855				allValues = [],
1856				index = -1,
1857				arFilter = {},
1858				tmpFilter = [];
1859
1860			for (i = 0; i < this.treeProps.length; i++)
1861			{
1862				if (this.treeProps[i].ID === strPropID)
1863				{
1864					index = i;
1865					break;
1866				}
1867			}
1868
1869			if (-1 < index)
1870			{
1871				for (i = 0; i < index; i++)
1872				{
1873					strName = 'PROP_'+this.treeProps[i].ID;
1874					arFilter[strName] = this.selectedValues[strName];
1875				}
1876				strName = 'PROP_'+this.treeProps[index].ID;
1877				arShowValues = this.getRowValues(arFilter, strName);
1878				if (!arShowValues)
1879				{
1880					return false;
1881				}
1882				if (!BX.util.in_array(strPropValue, arShowValues))
1883				{
1884					return false;
1885				}
1886				arFilter[strName] = strPropValue;
1887				for (i = index+1; i < this.treeProps.length; i++)
1888				{
1889					strName = 'PROP_'+this.treeProps[i].ID;
1890					arShowValues = this.getRowValues(arFilter, strName);
1891					if (!arShowValues)
1892					{
1893						return false;
1894					}
1895					allValues = [];
1896					if (this.showAbsent)
1897					{
1898						arCanBuyValues = [];
1899						tmpFilter = [];
1900						tmpFilter = BX.clone(arFilter, true);
1901						for (j = 0; j < arShowValues.length; j++)
1902						{
1903							tmpFilter[strName] = arShowValues[j];
1904							allValues[allValues.length] = arShowValues[j];
1905							if (this.getCanBuy(tmpFilter))
1906								arCanBuyValues[arCanBuyValues.length] = arShowValues[j];
1907						}
1908					}
1909					else
1910					{
1911						arCanBuyValues = arShowValues;
1912					}
1913					if (this.selectedValues[strName] && BX.util.in_array(this.selectedValues[strName], arCanBuyValues))
1914					{
1915						arFilter[strName] = this.selectedValues[strName];
1916					}
1917					else
1918					{
1919						if (this.showAbsent)
1920							arFilter[strName] = (arCanBuyValues.length > 0 ? arCanBuyValues[0] : allValues[0]);
1921						else
1922							arFilter[strName] = arCanBuyValues[0];
1923					}
1924					this.updateRow(i, arFilter[strName], arShowValues, arCanBuyValues);
1925				}
1926				this.selectedValues = arFilter;
1927				this.changeInfo({
1928					addToViewed: true
1929				});
1930			}
1931			return true;
1932		},
1933
1934		updateRow: function(intNumber, activeID, showID, canBuyID)
1935		{
1936			var i = 0,
1937				value = '',
1938				isCurrent = false,
1939				rowItems = null;
1940
1941			var lineContainer = this.obTree.querySelectorAll('[data-entity="sku-line-block"]'),
1942				listContainer;
1943
1944			if (intNumber > -1 && intNumber < lineContainer.length)
1945			{
1946				listContainer = lineContainer[intNumber].querySelector('ul');
1947				rowItems = BX.findChildren(listContainer, {tagName: 'li'}, false);
1948				if (rowItems && 0 < rowItems.length)
1949				{
1950					for (i = 0; i < rowItems.length; i++)
1951					{
1952						value = rowItems[i].getAttribute('data-onevalue');
1953						isCurrent = value === activeID;
1954
1955						if (isCurrent)
1956						{
1957							BX.addClass(rowItems[i], 'selected');
1958						}
1959						else
1960						{
1961							BX.removeClass(rowItems[i], 'selected');
1962						}
1963
1964						if (BX.util.in_array(value, canBuyID))
1965						{
1966							BX.removeClass(rowItems[i], 'notallowed');
1967						}
1968						else
1969						{
1970							BX.addClass(rowItems[i], 'notallowed');
1971						}
1972
1973						rowItems[i].style.display = BX.util.in_array(value, showID) ? '' : 'none';
1974
1975						if (isCurrent)
1976						{
1977							lineContainer[intNumber].style.display = (value == 0 && canBuyID.length == 1) ? 'none' : '';
1978						}
1979					}
1980				}
1981			}
1982		},
1983
1984		getRowValues: function(arFilter, index)
1985		{
1986			var i = 0,
1987				j,
1988				arValues = [],
1989				boolSearch = false,
1990				boolOneSearch = true;
1991
1992			if (0 === arFilter.length)
1993			{
1994				for (i = 0; i < this.offers.length; i++)
1995				{
1996					if (!BX.util.in_array(this.offers[i].TREE[index], arValues))
1997					{
1998						arValues[arValues.length] = this.offers[i].TREE[index];
1999					}
2000				}
2001				boolSearch = true;
2002			}
2003			else
2004			{
2005				for (i = 0; i < this.offers.length; i++)
2006				{
2007					boolOneSearch = true;
2008					for (j in arFilter)
2009					{
2010						if (arFilter[j] !== this.offers[i].TREE[j])
2011						{
2012							boolOneSearch = false;
2013							break;
2014						}
2015					}
2016					if (boolOneSearch)
2017					{
2018						if (!BX.util.in_array(this.offers[i].TREE[index], arValues))
2019						{
2020							arValues[arValues.length] = this.offers[i].TREE[index];
2021						}
2022						boolSearch = true;
2023					}
2024				}
2025			}
2026			return (boolSearch ? arValues : false);
2027		},
2028
2029		getCanBuy: function(arFilter)
2030		{
2031			var i, j,
2032				boolSearch = false,
2033				boolOneSearch = true;
2034
2035			for (i = 0; i < this.offers.length; i++)
2036			{
2037				boolOneSearch = true;
2038				for (j in arFilter)
2039				{
2040					if (arFilter[j] !== this.offers[i].TREE[j])
2041					{
2042						boolOneSearch = false;
2043						break;
2044					}
2045				}
2046				if (boolOneSearch)
2047				{
2048					if (this.offers[i].CAN_BUY)
2049					{
2050						boolSearch = true;
2051						break;
2052					}
2053				}
2054			}
2055
2056			return boolSearch;
2057		},
2058
2059		setCurrent: function()
2060		{
2061			var i,
2062				j = 0,
2063				arCanBuyValues = [],
2064				strName = '',
2065				arShowValues = false,
2066				arFilter = {},
2067				tmpFilter = [],
2068				current = this.offers[this.offerNum].TREE;
2069
2070			for (i = 0; i < this.treeProps.length; i++)
2071			{
2072				strName = 'PROP_'+this.treeProps[i].ID;
2073				arShowValues = this.getRowValues(arFilter, strName);
2074				if (!arShowValues)
2075				{
2076					break;
2077				}
2078				if (BX.util.in_array(current[strName], arShowValues))
2079				{
2080					arFilter[strName] = current[strName];
2081				}
2082				else
2083				{
2084					arFilter[strName] = arShowValues[0];
2085					this.offerNum = 0;
2086				}
2087				if (this.showAbsent)
2088				{
2089					arCanBuyValues = [];
2090					tmpFilter = [];
2091					tmpFilter = BX.clone(arFilter, true);
2092					for (j = 0; j < arShowValues.length; j++)
2093					{
2094						tmpFilter[strName] = arShowValues[j];
2095						if (this.getCanBuy(tmpFilter))
2096						{
2097							arCanBuyValues[arCanBuyValues.length] = arShowValues[j];
2098						}
2099					}
2100				}
2101				else
2102				{
2103					arCanBuyValues = arShowValues;
2104				}
2105				this.updateRow(i, arFilter[strName], arShowValues, arCanBuyValues);
2106			}
2107			this.selectedValues = arFilter;
2108			this.changeInfo();
2109		},
2110
2111		changeInfo: function(options)
2112		{
2113			var i, j,
2114				index = -1,
2115				boolOneSearch = true,
2116				activePhoto = 0;
2117
2118			for (i = 0; i < this.offers.length; i++)
2119			{
2120				boolOneSearch = true;
2121				for (j in this.selectedValues)
2122				{
2123					if (this.selectedValues[j] !== this.offers[i].TREE[j])
2124					{
2125						boolOneSearch = false;
2126						break;
2127					}
2128				}
2129				if (boolOneSearch)
2130				{
2131					index = i;
2132					break;
2133				}
2134			}
2135			if (index > -1)
2136			{
2137				if (parseInt(this.offers[index].MORE_PHOTO_COUNT) > 0)
2138				{
2139					if (typeof this.offers[index].MORE_PHOTO_SELECTED !== 'undefined')
2140					{
2141						activePhoto = parseInt(this.offers[index].MORE_PHOTO_SELECTED);
2142					}
2143					this.setPictureSlider(this.offers[index].RESIZED_SLIDER, activePhoto);
2144				}
2145				else if (this.product.morePhoto)
2146				{
2147					this.setPictureSlider(this.product.sliderPhoto, activePhoto);
2148				}
2149				else
2150				{
2151					this.hidePictureSlider();
2152				}
2153
2154				if (this.showSkuProps && this.obSkuProps)
2155				{
2156					if (this.offers[index].DISPLAY_PROPERTIES.length)
2157					{
2158						BX.adjust(this.obSkuProps, {style: {display: ''}, html: this.offers[index].DISPLAY_PROPERTIES});
2159					}
2160					else
2161					{
2162						BX.adjust(this.obSkuProps, {style: {display: 'none'}, html: ''});
2163					}
2164				}
2165
2166				if (this.useOfferName)
2167				{
2168					if (this.obName)
2169					{
2170						this.obName.innerHTML = BX.Text.encode(this.offers[index].NAME);
2171					}
2172					if (this.prebuyPopupName)
2173					{
2174						this.prebuyPopupName.innerHTML = BX.Text.encode(this.offers[index].NAME);
2175
2176					}
2177				}
2178
2179				this.quantitySet(index);
2180				this.setPrice();
2181				this.setCompared(this.offers[index].COMPARED);
2182
2183				this.offerNum = index;
2184				if (BX.type.isPlainObject(options) && options.addToViewed === true)
2185				{
2186					this.viewedParams.skuId = parseInt(this.offers[this.offerNum].ID);
2187					this.sendToViewed();
2188				}
2189			}
2190		},
2191
2192		setPictureSlider: function(photos, activePhoto)
2193		{
2194			var xPhotos = photos.X;
2195			var x2Photos = photos.X2;
2196
2197			if (!BX.type.isElementNode(this.obPictSlider))
2198			{
2199				return;
2200			}
2201			var indicatorExist = BX.type.isElementNode(this.obPictSliderIndicator);
2202			BX.cleanNode(this.obPictSlider);
2203			if (indicatorExist)
2204			{
2205				BX.cleanNode(this.obPictSliderIndicator);
2206			}
2207
2208			var i,
2209				currentIndex = 0,
2210				useIndicator = xPhotos.length > 1,
2211				selected;
2212			for (i in xPhotos)
2213			{
2214				if (!xPhotos.hasOwnProperty(i))
2215				{
2216					continue;
2217				}
2218
2219				var image = BX.create(
2220					'IMG',
2221					{
2222						attrs: {
2223							src: xPhotos[i].SRC,
2224							srcset: xPhotos[i].SRC + " 1x, " + x2Photos[i].SRC + " 2x",
2225						}
2226					}
2227				);
2228
2229				var overlay = BX.create(
2230					'div',
2231					{
2232						props: {
2233							className: 'catalog-section-item-slider-image-overlay'
2234						}
2235					}
2236				);
2237
2238				overlay.setAttribute('style',
2239					'background-image: url(\'' + xPhotos[i].SRC + '\');'
2240					+ 'background-image: -webkit-image-set(url(\'' + xPhotos[i].SRC + '\') 1x, url(\'' + x2Photos[i].SRC + '\') 2x);'
2241					+ 'background-image: image-set(url(\'' + xPhotos[i].SRC + '\') 1x, url(\'' + x2Photos[i].SRC + '\') 2x);'
2242				);
2243
2244				selected = currentIndex === activePhoto;
2245				this.obPictSlider.appendChild(
2246					BX.create(
2247						'A',
2248						{
2249							attrs: {
2250								'data-entity': 'image',
2251								'data-id': xPhotos[i].ID
2252							},
2253							props: {
2254								href: this.product.DETAIL_PAGE_URL,
2255								className: 'catalog-section-item-slider-image' + (selected ? ' active' : '')
2256							},
2257							children: [
2258								image,
2259								overlay
2260							]
2261						}
2262					)
2263				);
2264
2265				if (indicatorExist && useIndicator)
2266				{
2267					this.obPictSliderIndicator.appendChild(
2268						BX.create(
2269							'DIV',
2270							{
2271								attrs: {
2272									'data-go-to': i,
2273									'data-value': xPhotos[i].ID,
2274									'data-entity': 'slider-control'
2275								},
2276								props: {
2277									className: 'catalog-section-item-slider-controls-image' + (selected ? ' active' : '')
2278								},
2279								children: [
2280									document.createTextNode(' '),
2281									BX.create(
2282										'DIV',
2283										{
2284											attrs: {
2285												'data-go-to': i,
2286												'data-value': xPhotos[i].ID,
2287												'data-entity': 'slider-control-dot'
2288											},
2289											props: {
2290												className: 'catalog-section-item-slider-controls-dot'
2291											}
2292										}
2293									)
2294								]
2295							}
2296						)
2297					);
2298				}
2299
2300				if (selected)
2301				{
2302					if (BX.type.isElementNode(this.prebuyPopupPict))
2303					{
2304						this.prebuyPopupPict.src = xPhotos[i].SRC;
2305					}
2306				}
2307
2308				currentIndex++;
2309			}
2310
2311			if (this.sliderControlLeft)
2312			{
2313				this.sliderControlLeft.style.display = (useIndicator ? '' : 'none');
2314			}
2315			if (this.sliderControlRight)
2316			{
2317				this.sliderControlRight.style.display = (useIndicator ? '' : 'none');
2318			}
2319
2320			if (indicatorExist)
2321			{
2322				this.obPictSliderIndicator.style.display = (useIndicator ? '' : 'none');
2323			}
2324		},
2325
2326		hidePictureSlider: function()
2327		{
2328			if (!BX.type.isElementNode(this.obPictSlider))
2329			{
2330				return;
2331			}
2332			if (BX.type.isElementNode(this.obPictSliderIndicator))
2333			{
2334				this.obPictSliderIndicator.style.display = 'none';
2335			}
2336		},
2337
2338		checkPriceRange: function(quantity)
2339		{
2340			if (typeof quantity === 'undefined'|| this.currentPriceMode != 'Q')
2341				return;
2342
2343			var range, found = false;
2344
2345			for (var i in this.currentQuantityRanges)
2346			{
2347				if (this.currentQuantityRanges.hasOwnProperty(i))
2348				{
2349					range = this.currentQuantityRanges[i];
2350
2351					if (
2352						parseInt(quantity) >= parseInt(range.SORT_FROM)
2353						&& (
2354							range.SORT_TO == 'INF'
2355							|| parseInt(quantity) <= parseInt(range.SORT_TO)
2356						)
2357					)
2358					{
2359						found = true;
2360						this.currentQuantityRangeSelected = range.HASH;
2361						break;
2362					}
2363				}
2364			}
2365
2366			if (!found && (range = this.getMinPriceRange()))
2367			{
2368				this.currentQuantityRangeSelected = range.HASH;
2369			}
2370
2371			for (var k in this.currentPrices)
2372			{
2373				if (this.currentPrices.hasOwnProperty(k))
2374				{
2375					if (this.currentPrices[k].QUANTITY_HASH == this.currentQuantityRangeSelected)
2376					{
2377						this.currentPriceSelected = k;
2378						break;
2379					}
2380				}
2381			}
2382		},
2383
2384		getMinPriceRange: function()
2385		{
2386			var range;
2387
2388			for (var i in this.currentQuantityRanges)
2389			{
2390				if (this.currentQuantityRanges.hasOwnProperty(i))
2391				{
2392					if (
2393						!range
2394						|| parseInt(this.currentQuantityRanges[i].SORT_FROM) < parseInt(range.SORT_FROM)
2395					)
2396					{
2397						range = this.currentQuantityRanges[i];
2398					}
2399				}
2400			}
2401
2402			return range;
2403		},
2404
2405		checkQuantityControls: function()
2406		{
2407			if (!this.obQuantity)
2408				return;
2409
2410			var reachedTopLimit = this.checkQuantity && parseFloat(this.obQuantity.value) + this.stepQuantity > this.maxQuantity,
2411				reachedBottomLimit = parseFloat(this.obQuantity.value) - this.stepQuantity < this.minQuantity;
2412
2413			if (reachedTopLimit)
2414			{
2415				this.disableQuantityButton(this.obQuantityUp);
2416			}
2417			else
2418			{
2419				this.enableQuantityButton(this.obQuantityUp);
2420			}
2421
2422			if (reachedBottomLimit)
2423			{
2424				this.disableQuantityButton(this.obQuantityDown);
2425			}
2426			else
2427			{
2428				this.enableQuantityButton(this.obQuantityDown);
2429			}
2430
2431			if (this.obQuantity.value == this.minQuantity)
2432			{
2433				this.disableQuantityButton(this.obQuantityDown);
2434			}
2435			else
2436			{
2437				this.enableQuantityButton(this.obQuantityDown);
2438			}
2439		},
2440
2441		setPrice: function()
2442		{
2443			var price,
2444				formatPrice = '',
2445				formatOldPrice = '',
2446				formatDiscount = '',
2447				showBlock;
2448
2449			if (this.obQuantity)
2450			{
2451				this.checkPriceRange(this.obQuantity.value);
2452			}
2453
2454			this.checkQuantityControls();
2455
2456			price = this.currentPrices[this.currentPriceSelected];
2457
2458			if (this.obPrice)
2459			{
2460				if (price)
2461				{
2462					formatPrice = BX.Currency.currencyFormat(price.RATIO_PRICE, price.CURRENCY, true);
2463				}
2464				BX.adjust(this.obPrice, {html: formatPrice});
2465				if (this.priceTwin)
2466				{
2467					BX.adjust(this.priceTwin, {html: formatPrice});
2468				}
2469
2470				if (this.showOldPrice)
2471				{
2472					showBlock = price && price.RATIO_PRICE !== price.RATIO_BASE_PRICE;
2473					if (showBlock)
2474					{
2475						formatOldPrice = BX.Currency.currencyFormat(price.RATIO_BASE_PRICE, price.CURRENCY, true);
2476						formatDiscount = BX.Currency.currencyFormat(price.RATIO_DISCOUNT, price.CURRENCY, true);
2477
2478						if (this.blockOldPrice)
2479						{
2480							if (this.obPriceOld)
2481							{
2482								BX.adjust(this.obPriceOld, { html: formatOldPrice });
2483							}
2484							if (this.priceDiscount)
2485							{
2486								BX.adjust(this.priceDiscount, { html: formatDiscount });
2487							}
2488							BX.adjust(this.blockOldPrice, { style: {display: ''} });
2489						}
2490						if (this.blockOldPriceTwin)
2491						{
2492							if (this.priceOldTwin)
2493							{
2494								BX.adjust(this.priceOldTwin, { html: formatOldPrice });
2495							}
2496							if (this.priceDiscountTwin)
2497							{
2498								BX.adjust(this.priceDiscountTwin, { html: formatDiscount });
2499							}
2500							BX.adjust(this.blockOldPriceTwin, { style: {display: ''} });
2501						}
2502					}
2503					else
2504					{
2505						if (this.blockOldPrice)
2506						{
2507							if (this.obPriceOld)
2508							{
2509								BX.adjust(this.obPriceOld, { html: '' });
2510							}
2511							if (this.priceDiscount)
2512							{
2513								BX.adjust(this.priceDiscount, { html: '' });
2514							}
2515							BX.adjust(this.blockOldPrice, { style: {display: 'none'} });
2516						}
2517						if (this.blockOldPriceTwin)
2518						{
2519							if (this.priceOldTwin)
2520							{
2521								BX.adjust(this.priceOldTwin, { html: '' });
2522							}
2523							if (this.priceDiscountTwin)
2524							{
2525								BX.adjust(this.priceDiscountTwin, { html: '' });
2526							}
2527							BX.adjust(this.blockOldPriceTwin, { style: {display: 'none'} });
2528						}
2529					}
2530				}
2531
2532				if (this.showOldPrice && this.obPriceOld)
2533				{
2534					if (price && price.RATIO_PRICE !== price.RATIO_BASE_PRICE)
2535					{
2536						BX.adjust(this.obPriceOld, {
2537							style: {display: ''},
2538							html: BX.Currency.currencyFormat(price.RATIO_BASE_PRICE, price.CURRENCY, true)
2539						});
2540					}
2541					else
2542					{
2543						BX.adjust(this.obPriceOld, {
2544							style: {display: 'none'},
2545							html: ''
2546						});
2547					}
2548				}
2549
2550				if (this.obPriceTotal)
2551				{
2552					this.obMeasureContainer = BX(this.visual.QUANTITY_MEASURE_CONTAINER);
2553
2554					if (price && this.obQuantity && this.obQuantity.value != this.stepQuantity)
2555					{
2556						if (this.obMeasureContainer)
2557						{
2558							BX.adjust(this.obMeasureContainer, {
2559								style: {
2560									"text-align": "right",
2561									"left": "auto"
2562								}
2563							})
2564						}
2565
2566						BX.adjust(this.obPriceTotal, {
2567							html: BX.message('PRICE_TOTAL_PREFIX') + ' <span>'
2568							+ BX.Currency.currencyFormat(price.PRICE * this.obQuantity.value, price.CURRENCY, true)
2569							+ '</span>',
2570							style: {display: ''}
2571						});
2572					}
2573					else
2574					{
2575						BX.adjust(this.obPriceTotal, {
2576							html: '',
2577							style: {display: 'none'}
2578						});
2579						if (this.obMeasureContainer)
2580						{
2581							BX.adjust(this.obMeasureContainer, {
2582								style: {
2583									"text-align": "",
2584									"left": 0
2585								}
2586							})
2587						}
2588					}
2589				}
2590			}
2591		},
2592
2593		compare: function(event)
2594		{
2595			var checkbox = this.obCompare.querySelector('[data-entity="compare-checkbox"]'),
2596				target = BX.getEventTarget(event),
2597				checked = true;
2598
2599			if (checkbox)
2600			{
2601				checked = target === checkbox ? checkbox.checked : !checkbox.checked;
2602			}
2603
2604			var url = checked ? this.compareData.compareUrl : this.compareData.compareDeleteUrl,
2605				compareLink;
2606
2607			if (url)
2608			{
2609				if (target !== checkbox)
2610				{
2611					BX.PreventDefault(event);
2612					this.setCompared(checked);
2613				}
2614
2615				switch (this.productType)
2616				{
2617					case 0: // no catalog
2618					case 1: // product
2619					case 2: // set
2620					case 7: // service
2621						compareLink = url.replace('#ID#', this.product.id.toString());
2622						break;
2623					case 3: // sku
2624						compareLink = url.replace('#ID#', this.offers[this.offerNum].ID);
2625						break;
2626				}
2627
2628				BX.ajax({
2629					method: 'POST',
2630					dataType: checked ? 'json' : 'html',
2631					url: compareLink + (compareLink.indexOf('?') !== -1 ? '&' : '?') + 'ajax_action=Y',
2632					onsuccess: checked
2633						? BX.proxy(this.compareResult, this)
2634						: BX.proxy(this.compareDeleteResult, this)
2635				});
2636			}
2637		},
2638
2639		compareResult: function(result)
2640		{
2641			var popupContent, popupButtons;
2642
2643			if (this.obPopupWin)
2644			{
2645				this.obPopupWin.close();
2646			}
2647
2648			if (!BX.type.isPlainObject(result))
2649				return;
2650
2651			this.initPopupWindow();
2652
2653			if (this.offers.length > 0)
2654			{
2655				this.offers[this.offerNum].COMPARED = result.STATUS === 'OK';
2656			}
2657
2658			if (result.STATUS === 'OK')
2659			{
2660				BX.onCustomEvent('OnCompareChange');
2661
2662				popupContent = '<div style="width: 100%; margin: 0; text-align: center;"><p>'
2663					+ BX.message('COMPARE_MESSAGE_OK')
2664					+ '</p></div>';
2665
2666				if (this.showClosePopup)
2667				{
2668					popupButtons = [
2669						new BasketButton({
2670							text: BX.message('BTN_MESSAGE_COMPARE_REDIRECT'),
2671							events: {
2672								click: BX.delegate(this.compareRedirect, this)
2673							},
2674							style: {marginRight: '10px'}
2675						}),
2676						new BasketButton({
2677							text: BX.message('BTN_MESSAGE_CLOSE_POPUP'),
2678							events: {
2679								click: BX.delegate(this.obPopupWin.close, this.obPopupWin)
2680							}
2681						})
2682					];
2683				}
2684				else
2685				{
2686					popupButtons = [
2687						new BasketButton({
2688							text: BX.message('BTN_MESSAGE_COMPARE_REDIRECT'),
2689							events: {
2690								click: BX.delegate(this.compareRedirect, this)
2691							}
2692						})
2693					];
2694				}
2695			}
2696			else
2697			{
2698				popupContent = '<div style="width: 100%; margin: 0; text-align: center;"><p>'
2699					+ (result.MESSAGE ? result.MESSAGE : BX.message('COMPARE_UNKNOWN_ERROR'))
2700					+ '</p></div>';
2701				popupButtons = [
2702					new BasketButton({
2703						text: BX.message('BTN_MESSAGE_CLOSE'),
2704						events: {
2705							click: BX.delegate(this.obPopupWin.close, this.obPopupWin)
2706						}
2707					})
2708				];
2709			}
2710
2711			this.obPopupWin.setTitleBar(BX.message('COMPARE_TITLE'));
2712			this.obPopupWin.setContent(popupContent);
2713			this.obPopupWin.setButtons(popupButtons);
2714			this.obPopupWin.show();
2715		},
2716
2717		compareDeleteResult: function()
2718		{
2719			BX.onCustomEvent('OnCompareChange');
2720
2721			if (this.offers && this.offers.length)
2722			{
2723				this.offers[this.offerNum].COMPARED = false;
2724			}
2725		},
2726
2727		setCompared: function(state)
2728		{
2729			if (!this.obCompare)
2730				return;
2731
2732			var checkbox = this.obCompare.querySelector('[data-entity="compare-checkbox"]');
2733			if (checkbox)
2734			{
2735				checkbox.checked = state;
2736			}
2737		},
2738
2739		setCompareInfo: function(comparedIds)
2740		{
2741			if (!BX.type.isArray(comparedIds))
2742				return;
2743
2744			for (var i in this.offers)
2745			{
2746				if (this.offers.hasOwnProperty(i))
2747				{
2748					this.offers[i].COMPARED = BX.util.in_array(this.offers[i].ID, comparedIds);
2749				}
2750			}
2751		},
2752
2753		compareRedirect: function()
2754		{
2755			if (this.compareData.comparePath)
2756			{
2757				location.href = this.compareData.comparePath;
2758			}
2759			else
2760			{
2761				this.obPopupWin.close();
2762			}
2763		},
2764
2765		checkDeletedCompare: function(id)
2766		{
2767			switch (this.productType)
2768			{
2769				case 0: // no catalog
2770				case 1: // product
2771				case 2: // set
2772				case 7: // service
2773					if (this.product.id == id)
2774					{
2775						this.setCompared(false);
2776					}
2777
2778					break;
2779				case 3: // sku
2780					var i = this.offers.length;
2781					while (i--)
2782					{
2783						if (this.offers[i].ID == id)
2784						{
2785							this.offers[i].COMPARED = false;
2786
2787							if (this.offerNum == i)
2788							{
2789								this.setCompared(false);
2790							}
2791
2792							break;
2793						}
2794					}
2795			}
2796		},
2797
2798		initBasketUrl: function()
2799		{
2800			this.basketUrl = (this.basketMode === 'ADD' ? this.basketData.add_url : this.basketData.buy_url);
2801			switch (this.productType)
2802			{
2803				case 1: // product
2804				case 2: // set
2805				case 7: // service
2806					this.basketUrl = this.basketUrl.replace('#ID#', this.product.id.toString());
2807					break;
2808				case 3: // sku
2809					this.basketUrl = this.basketUrl.replace('#ID#', this.offers[this.offerNum].ID);
2810					break;
2811			}
2812			this.basketParams = {
2813				'ajax_basket': 'Y'
2814			};
2815			if (this.showQuantity)
2816			{
2817				this.basketParams[this.basketData.quantity] = this.obQuantity.value;
2818			}
2819			if (this.basketData.sku_props)
2820			{
2821				this.basketParams[this.basketData.sku_props_var] = this.basketData.sku_props;
2822			}
2823		},
2824
2825		fillBasketProps: function()
2826		{
2827			if (!this.visual.BASKET_PROP_DIV)
2828			{
2829				return;
2830			}
2831			var
2832				i = 0,
2833				propCollection = null,
2834				foundValues = false,
2835				obBasketProps = null;
2836
2837			if (this.basketData.useProps && !this.basketData.emptyProps)
2838			{
2839				if (this.obPopupWin && this.obPopupWin.contentContainer)
2840				{
2841					obBasketProps = this.obPopupWin.contentContainer;
2842				}
2843			}
2844			else
2845			{
2846				obBasketProps = BX(this.visual.BASKET_PROP_DIV);
2847			}
2848			if (obBasketProps)
2849			{
2850				propCollection = obBasketProps.getElementsByTagName('select');
2851				if (propCollection && propCollection.length)
2852				{
2853					for (i = 0; i < propCollection.length; i++)
2854					{
2855						if (!propCollection[i].disabled)
2856						{
2857							switch (propCollection[i].type.toLowerCase())
2858							{
2859								case 'select-one':
2860									this.basketParams[propCollection[i].name] = propCollection[i].value;
2861									foundValues = true;
2862									break;
2863								default:
2864									break;
2865							}
2866						}
2867					}
2868				}
2869				propCollection = obBasketProps.getElementsByTagName('input');
2870				if (propCollection && propCollection.length)
2871				{
2872					for (i = 0; i < propCollection.length; i++)
2873					{
2874						if (!propCollection[i].disabled)
2875						{
2876							switch (propCollection[i].type.toLowerCase())
2877							{
2878								case 'hidden':
2879									this.basketParams[propCollection[i].name] = propCollection[i].value;
2880									foundValues = true;
2881									break;
2882								case 'radio':
2883									if (propCollection[i].checked)
2884									{
2885										this.basketParams[propCollection[i].name] = propCollection[i].value;
2886										foundValues = true;
2887									}
2888									break;
2889								default:
2890									break;
2891							}
2892						}
2893					}
2894				}
2895			}
2896			if (!foundValues)
2897			{
2898				this.basketParams[this.basketData.props] = [];
2899				this.basketParams[this.basketData.props][0] = 0;
2900			}
2901		},
2902
2903		add2Basket: function()
2904		{
2905			this.basketMode = 'ADD';
2906			this.basket();
2907		},
2908
2909		buyBasket: function()
2910		{
2911			this.basketMode = 'BUY';
2912			this.basket();
2913		},
2914
2915		sendToBasket: function()
2916		{
2917			if (!this.canBuy)
2918			{
2919				return;
2920			}
2921
2922			// check recommendation
2923			if (this.product && this.product.id && this.bigData)
2924			{
2925				this.rememberProductRecommendation();
2926			}
2927
2928			this.initBasketUrl();
2929			this.fillBasketProps();
2930			BX.ajax({
2931				method: 'POST',
2932				dataType: 'json',
2933				url: this.basketUrl,
2934				data: this.basketParams,
2935				onsuccess: BX.proxy(this.basketResult, this)
2936			});
2937		},
2938
2939		sendToViewed: function()
2940		{
2941			if (this.viewedParams.skuId > 0 && this.viewedParams.siteId)
2942			{
2943				BX.ajax.runComponentAction(
2944					'bitrix:catalog.item',
2945					'addViewedProduct',
2946					{
2947						mode: 'ajax',
2948						data: this.viewedParams,
2949					}
2950				);
2951			}
2952		},
2953
2954		basket: function()
2955		{
2956			var contentBasketProps = '';
2957			if (!this.canBuy)
2958			{
2959				return;
2960			}
2961			switch (this.productType)
2962			{
2963				case 1: // product
2964				case 2: // set
2965				case 7: // service
2966					if (this.basketData.useProps && !this.basketData.emptyProps)
2967					{
2968						this.initPopupWindow();
2969						this.obPopupWin.setTitleBar(BX.message('TITLE_BASKET_PROPS'));
2970						if (BX(this.visual.BASKET_PROP_DIV))
2971						{
2972							contentBasketProps = BX(this.visual.BASKET_PROP_DIV).innerHTML;
2973						}
2974						this.obPopupWin.setContent(contentBasketProps);
2975						this.obPopupWin.setButtons([
2976							new BasketButton({
2977								text: BX.message('BTN_MESSAGE_SEND_PROPS'),
2978								events: {
2979									click: BX.delegate(this.sendToBasket, this)
2980								}
2981							})
2982						]);
2983						this.obPopupWin.show();
2984					}
2985					else
2986					{
2987						this.sendToBasket();
2988					}
2989					break;
2990				case 3: // sku
2991					this.sendToBasket();
2992					break;
2993			}
2994		},
2995
2996		basketResult: function(arResult)
2997		{
2998			var strContent = '',
2999				strPict = '',
3000				successful,
3001				buttons = [];
3002
3003			if (this.obPopupWin)
3004				this.obPopupWin.close();
3005
3006			if (!BX.type.isPlainObject(arResult))
3007				return;
3008
3009			successful = arResult.STATUS === 'OK';
3010
3011			if (successful)
3012			{
3013				this.setAnalyticsDataLayer('addToCart');
3014			}
3015
3016			if (successful && this.basketMode === 'BUY')
3017			{
3018				// this.prebuyPopupClose();
3019				this.basketRedirect();
3020			}
3021			else
3022			{
3023				this.initPopupWindow();
3024
3025				if (successful)
3026				{
3027					BX.onCustomEvent('OnBasketChange');
3028
3029					if  (BX.findParent(this.obProduct, {className: 'bx_sale_gift_main_products'}, 10))
3030					{
3031						BX.onCustomEvent('onAddToBasketMainProduct', [this]);
3032					}
3033
3034					switch (this.productType)
3035					{
3036						case 1: // product
3037						case 2: // set
3038						case 7: // service
3039							strPict = this.product.pict.SRC;
3040							break;
3041						case 3: // sku
3042							strPict = (this.offers[this.offerNum].PREVIEW_PICTURE ?
3043									this.offers[this.offerNum].PREVIEW_PICTURE.SRC :
3044									this.defaultPict.pict.SRC
3045							);
3046							break;
3047					}
3048
3049					strContent = '<div style="width: 100%; margin: 0; text-align: center;"><img src="'
3050						+ strPict + '" height="130" style="max-height:130px"><p>' + this.product.name + '</p></div>';
3051
3052					if (this.showClosePopup)
3053					{
3054						buttons = [
3055							new BasketButton({
3056								text: BX.message("BTN_MESSAGE_BASKET_REDIRECT"),
3057								events: {
3058									click: BX.delegate(this.basketRedirect, this)
3059								},
3060								style: {marginRight: '10px'}
3061							}),
3062							new BasketButton({
3063								text: BX.message("BTN_MESSAGE_CLOSE_POPUP"),
3064								events: {
3065									click: BX.delegate(this.obPopupWin.close, this.obPopupWin)
3066								}
3067							})
3068						];
3069					}
3070					else
3071					{
3072						buttons = [
3073							new BasketButton({
3074								text: BX.message("BTN_MESSAGE_BASKET_REDIRECT"),
3075								events: {
3076									click: BX.delegate(this.basketRedirect, this)
3077								}
3078							})
3079						];
3080					}
3081				}
3082				else
3083				{
3084					strContent = '<div style="width: 100%; margin: 0; text-align: center;"><p>'
3085						+ (arResult.MESSAGE ? arResult.MESSAGE : BX.message('BASKET_UNKNOWN_ERROR'))
3086						+ '</p></div>';
3087					buttons = [
3088						new BasketButton({
3089							text: BX.message('BTN_MESSAGE_CLOSE'),
3090							events: {
3091								click: BX.delegate(this.obPopupWin.close, this.obPopupWin)
3092							}
3093						})
3094					];
3095				}
3096				// this.obPopupWin.setTitleBar(successful ? BX.message('TITLE_SUCCESSFUL') : BX.message('TITLE_ERROR'));
3097				// this.obPopupWin.setContent(strContent);
3098				// this.obPopupWin.setButtons(buttons);
3099				// this.obPopupWin.show();
3100			}
3101		},
3102
3103		basketRedirect: function()
3104		{
3105			location.href = (this.basketData.basketUrl ? this.basketData.basketUrl : BX.message('BASKET_URL'));
3106		},
3107
3108		initPopupWindow: function()
3109		{
3110			if (this.obPopupWin)
3111				return;
3112
3113			this.obPopupWin = BX.PopupWindowManager.create('CatalogSectionBasket_' + this.visual.ID, null, {
3114				autoHide: true,
3115				offsetLeft: 0,
3116				offsetTop: 0,
3117				overlay : true,
3118				closeByEsc: true,
3119				titleBar: true,
3120				closeIcon: true,
3121				contentColor: 'white',
3122				className: this.templateTheme ? 'bx-' + this.templateTheme : ''
3123			});
3124		}
3125	};
3126})(window);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.