PageSourceSearch

https://book24.ua/local/templates/.default/components/bitrix/sale.basket.basket/basket-page/js/component.js

js book24.ua collected 2026-09-24 21:09:35 UTC 67,154 bytes, 2,554 lines download raw bytes

1;(function() {
2	'use strict';
3
4	BX.namespace('BX.Sale.BasketComponent');
5
6	BX.Sale.BasketComponent = {
7		maxItemsShowCount: 30,
8		precisionFactor: Math.pow(10, 6),
9		stickyHeaderOffset: 0,
10
11		duration: {
12			priceAnimation: 300,
13			filterTimer: 300
14		},
15
16		ids: {
17			item: 'basket-item-',
18			quantity: 'basket-item-quantity-',
19			price: 'basket-item-price-',
20			sumPrice: 'basket-item-sum-price-',
21			sumPriceOld: 'basket-item-sum-price-old-',
22			sumPriceDiff: 'basket-item-sum-price-difference-',
23			itemHeightAligner: 'basket-item-height-aligner-',
24			total: 'basket-total-price',
25			basketRoot: 'basket-root',
26			itemListWrapper: 'basket-items-list-wrapper',
27			itemListContainer: 'basket-items-list-container',
28			itemList: 'basket-item-list',
29			itemListTable: 'basket-item-table',
30			itemListEmptyResult: 'basket-item-list-empty-result',
31			itemListOverlay: 'basket-items-list-overlay',
32			warning: 'basket-warning'
33		},
34
35		initializePrimaryFields: function()
36		{
37			this.templates = {};
38			this.nodes = {};
39
40			/** Object of all basket items (itemId => itemArray) */
41			this.items = {};
42			
43			this.itemsSets = {};
44
45			/** Array of all basket items to show sorted by field SORT */
46			this.sortedItems = [];
47
48			/** Array of basket items showed on screen */
49			this.shownItems = [];
50
51			/** Array of basket items changed since last request */
52			this.changedItems = [];
53
54			/** Array of basket items postponed by pool to edit */
55			this.postponedItems = [];
56
57			/** Array of basket items with warnings */
58			this.warningItems = [];
59
60			this.isMobile = BX.browser.IsMobile();
61			this.isTouch = BX.hasClass(document.documentElement, 'bx-touch');
62
63			this.lastAction = 'initialLoad';
64			this.coupons = null;
65
66			this.imagePopup = null;
67			this.loadingScreen = null;
68
69			this.quantityDelay = null;
70			this.quantityTimer = null;
71		},
72
73		init: function(parameters)
74		{
75			this.initializePrimaryFields();
76
77			this.params = parameters.params || {};
78			this.template = parameters.template || '';
79			this.signedParamsString = parameters.signedParamsString || '';
80			this.siteId = parameters.siteId || '';
81			this.siteTemplateId = parameters.siteTemplateId || '';
82			this.ajaxUrl = this.params.AJAX_PATH || '';
83			this.templateFolder = parameters.templateFolder || '';
84
85			this.useDynamicScroll = this.params.USE_DYNAMIC_SCROLL === 'Y';
86			this.useItemsFilter = this.params.SHOW_FILTER === 'Y' && !this.isMobile;
87
88			this.initializeFilter();
89			this.applyBasketResult(parameters.result);
90			this.initializeActionPool();
91
92			if (this.useItemsFilter)
93			{
94				this.checkHeaderDisplay();
95				this.bindHeaderEvents();
96			}
97
98			this.initializeBasketItems();
99			this.editTotal();
100			this.editWarnings();
101
102			this.adjustBasketWrapperHeight();
103			this.getCacheNode(this.ids.basketRoot).style.opacity = 1;
104
105			this.bindInitialEvents();
106		},
107
108		getTemplate: function(templateName)
109		{
110			if (!this.templates.hasOwnProperty(templateName))
111			{
112				var template = BX(templateName);
113				this.templates[templateName] = BX.type.isDomNode(template) ? template.innerHTML : '';
114			}
115
116			return this.templates[templateName];
117		},
118
119		getCacheNode: function(id)
120		{
121			if (!this.nodes.hasOwnProperty(id))
122			{
123				this.nodes[id] = BX(id);
124			}
125
126			return this.nodes[id];
127		},
128
129		getEntity: function(parent, entity, additionalFilter)
130		{
131			if (!parent || !entity)
132				return null;
133
134			additionalFilter = additionalFilter || '';
135
136			return parent.querySelector(additionalFilter + '[data-entity="' + entity + '"]');
137		},
138
139		getEntities: function(parent, entity, additionalFilter)
140		{
141			if (!parent || !entity)
142				return {length: 0};
143
144			additionalFilter = additionalFilter || '';
145
146			return parent.querySelectorAll(additionalFilter + '[data-entity="' + entity + '"]');
147		},
148
149		bindInitialEvents: function()
150		{
151			this.bindWarningEvents();
152
153			BX.bind(window, 'scroll', BX.proxy(this.checkStickyHeaders, this));
154			BX.bind(window, 'scroll', BX.proxy(this.lazyLoad, this));
155
156			BX.bind(window, 'resize', BX.throttle(this.checkStickyHeaders, 20, this));
157			BX.bind(window, 'resize', BX.throttle(this.adjustBasketWrapperHeight, 20, this));
158		},
159
160		bindWarningEvents: function()
161		{
162			var showItemsNode = this.getEntity(BX(this.ids.warning), 'basket-items-warning-count');
163
164			if (BX.type.isDomNode(showItemsNode))
165			{
166				showItemsNode.style.display = '';
167				BX.bind(showItemsNode, 'click', BX.delegate(function() {this.toggleFilter('warning');}, this));
168			}
169
170			BX.bind(
171				this.getEntity(BX(this.ids.warning), 'basket-items-warning-notification-close'),
172				'click',
173				BX.proxy(this.removeAllWarnings, this)
174			);
175		},
176
177		toggleFilter: function(event)
178		{
179			var target = BX.type.isNotEmptyString(event) ?
180				this.getEntity(
181					this.getCacheNode(this.ids.itemListWrapper),
182					'basket-items-count',
183					'[data-filter="' + event + '"]'
184				)
185				: BX.getEventTarget(event);
186
187			if (!BX.type.isDomNode(target) || BX.hasClass(target, 'active'))
188				return;
189
190			var entityName = target.getAttribute('data-filter');
191			var entities = target.parentNode.querySelectorAll('[data-filter]');
192
193			for (var i = 0; i < entities.length; i++)
194			{
195				if (entities[i].getAttribute('data-filter') === entityName)
196				{
197					BX.addClass(entities[i], 'active');
198				}
199				else if (BX.hasClass(entities[i], 'active'))
200				{
201					BX.removeClass(entities[i], 'active');
202				}
203			}
204
205			this.filter.showFilterByName(entityName);
206		},
207
208		scrollToFirstItem: function()
209		{
210			var headerNode = this.getEntity(this.getCacheNode(this.ids.itemListWrapper), 'basket-items-list-header');
211
212			if (BX.type.isDomNode(headerNode))
213			{
214				var itemListTopPosition = BX.pos(this.getCacheNode(this.ids.itemListContainer)).top;
215				var headerBottomPosition = BX.pos(headerNode).bottom;
216
217				if (itemListTopPosition < headerBottomPosition)
218				{
219					window.scrollTo(0, itemListTopPosition - this.stickyHeaderOffset);
220				}
221			}
222		},
223
224		showItemsOverlay: function()
225		{
226			var overlay = this.getCacheNode(this.ids.itemListOverlay);
227
228			if (BX.type.isDomNode(overlay))
229			{
230				overlay.style.display = '';
231			}
232		},
233
234		hideItemsOverlay: function()
235		{
236			var overlay = this.getCacheNode(this.ids.itemListOverlay);
237
238			if (BX.type.isDomNode(overlay))
239			{
240				overlay.style.display = 'none';
241			}
242		},
243
244		checkHeaderDisplay: function()
245		{
246			var header = this.getCacheNode(this.ids.itemListWrapper);
247
248			if (BX.type.isDomNode(header))
249			{
250				BX.removeClass(header, 'basket-items-list-wrapper-light');
251			}
252		},
253
254		bindHeaderEvents: function()
255		{
256			var entities = this.getEntities(this.getCacheNode(this.ids.itemListWrapper), 'basket-items-count');
257
258			for (var i = 0; i < entities.length; i++)
259			{
260				BX.bind(entities[i], 'click', BX.proxy(this.toggleFilter, this));
261			}
262		},
263
264		checkStickyHeaders: function()
265		{
266			if (this.isMobile)
267				return;
268
269			var node, position;
270			var border = 2, offset = 0;
271			var scrollTop = this.getDocumentScrollTop();
272			var basketPosition = BX.pos(this.getCacheNode(this.ids.basketRoot));
273			var basketScrolledToEnd = scrollTop + 200 >= basketPosition.bottom;
274
275			if (BX.util.in_array('top', this.params.TOTAL_BLOCK_DISPLAY))
276			{
277				var totalBlockNode = this.getEntity(this.getCacheNode(this.ids.basketRoot), 'basket-total-block');
278				if (BX.type.isDomNode(totalBlockNode))
279				{
280					node = this.getEntity(totalBlockNode, 'basket-checkout-aligner');
281					if (BX.type.isDomNode(node))
282					{
283						position = BX.pos(totalBlockNode);
284
285						if (scrollTop >= position.top)
286						{
287							offset += node.clientHeight;
288
289							if (!BX.hasClass(node, 'basket-checkout-container-fixed'))
290							{
291								totalBlockNode.style.height = position.height + 'px';
292
293								node.style.width = node.clientWidth + border + 'px';
294								BX.addClass(node, 'basket-checkout-container-fixed');
295							}
296						}
297						else if (BX.hasClass(node, 'basket-checkout-container-fixed'))
298						{
299							totalBlockNode.style.height = '';
300
301							node.style.width = '';
302							BX.removeClass(node, 'basket-checkout-container-fixed');
303						}
304
305						if (basketScrolledToEnd)
306						{
307							if (!BX.hasClass(node, 'basket-checkout-container-fixed-hide'))
308							{
309								BX.addClass(node, 'basket-checkout-container-fixed-hide');
310							}
311						}
312						else if (BX.hasClass(node, 'basket-checkout-container-fixed-hide'))
313						{
314							BX.removeClass(node, 'basket-checkout-container-fixed-hide');
315						}
316					}
317				}
318			}
319
320			if (this.useItemsFilter)
321			{
322				var itemWrapperNode = this.getCacheNode(this.ids.itemListWrapper);
323
324				node = this.getEntity(itemWrapperNode, 'basket-items-list-header');
325				if (BX.type.isDomNode(node))
326				{
327					position = BX.pos(itemWrapperNode);
328
329					if ((scrollTop + offset >= position.top) && !basketScrolledToEnd)
330					{
331						if (!BX.hasClass(node, 'basket-items-list-header-fixed'))
332						{
333							node.style.width = node.clientWidth + border + 'px';
334
335							itemWrapperNode.style.paddingTop = node.clientHeight + 'px';
336
337							BX.addClass(node, 'basket-items-list-header-fixed');
338						}
339
340						if (offset)
341						{
342							node.style.top = offset + 'px';
343						}
344
345						offset += node.clientHeight;
346					}
347					else if (BX.hasClass(node, 'basket-items-list-header-fixed'))
348					{
349						itemWrapperNode.style.paddingTop = '';
350
351						node.style.width = '';
352						node.style.top = '';
353
354						BX.removeClass(node, 'basket-items-list-header-fixed');
355					}
356				}
357			}
358
359			var offsetChanged = this.stickyHeaderOffset === offset;
360
361			this.stickyHeaderOffset = offset;
362
363			if (offsetChanged && !basketScrolledToEnd)
364			{
365				this.adjustBasketWrapperHeight();
366			}
367		},
368
369		getDocumentScrollTop: function()
370		{
371			return window.scrollY
372				|| window.pageYOffset
373				|| document.body.scrollTop + (document.documentElement && document.documentElement.scrollTop || 0);
374		},
375
376		lazyLoad: function()
377		{
378			var itemsNodePosition = BX.pos(this.getCacheNode(this.ids.itemListContainer));
379
380			if (this.getDocumentScrollTop() + window.innerHeight >= itemsNodePosition.bottom - 400)
381			{
382				var itemIds = this.getItemsAfter();
383				if (itemIds.length)
384				{
385					this.editBasketItems(itemIds);
386				}
387			}
388		},
389
390		fireCustomEvents: function()
391		{
392			if (this.result.EVENT_ONCHANGE_ON_START === 'Y')
393			{
394				BX.onCustomEvent('OnBasketChange');
395			}
396
397			if (this.params.HIDE_COUPON !== 'Y')
398			{
399				if (this.coupons !== null && this.coupons !== this.result.COUPON_LIST)
400				{
401					BX.onCustomEvent('OnCouponApply');
402				}
403
404				this.coupons = this.result.COUPON_LIST;
405			}
406		},
407
408		adjustBasketWrapperHeight: function()
409		{
410			var itemListContainer = this.getCacheNode(this.ids.itemListContainer),
411				itemList = this.getCacheNode(this.ids.itemList);
412
413			if (BX.type.isDomNode(itemListContainer) && BX.type.isDomNode(itemList))
414			{
415				if (itemListContainer.clientHeight + this.stickyHeaderOffset > window.innerHeight)
416				{
417					itemListContainer.style.minHeight = 'calc(100vh - 15px - ' + this.stickyHeaderOffset + 'px)';
418					itemList.style.minHeight = 'calc(100vh - 15px - ' + this.stickyHeaderOffset + 'px)';
419				}
420				else
421				{
422					itemListContainer.style.minHeight = itemListContainer.clientHeight + 'px';
423					itemList.style.minHeight = itemListContainer.clientHeight + 'px';
424				}
425			}
426		},
427
428		editTotal: function()
429		{
430			this.fillTotalBlocks();
431			this.showItemsCount();
432			this.showWarningItemsCount();
433			this.showNotAvailableItemsCount();
434			this.showDelayedItemsCount();
435		},
436
437		fillTotalBlocks: function()
438		{
439			var totalNodes = this.getEntities(this.getCacheNode(this.ids.basketRoot), 'basket-total-block');
440
441			if (totalNodes && totalNodes.length)
442			{
443				var totalTemplate = this.getTemplate('basket-total-template');
444				if (totalTemplate)
445				{
446					var totalRender = this.render(totalTemplate, this.result.TOTAL_RENDER_DATA);
447
448					for (var i in totalNodes)
449					{
450						if (totalNodes.hasOwnProperty(i) && BX.type.isDomNode(totalNodes[i])
450)
451						{
452							totalNodes[i].innerHTML = totalRender;
453
454							this.bindTotalEvents(totalNodes[i]);
455						}
456					}
457				}
458			}
459
460			this.checkStickyHeaders();
461		},
462
463		showItemsCount: function()
464		{
465			var itemCountNode = this.getEntity(
466				this.getCacheNode(this.ids.itemListWrapper),
467				'basket-items-count',
468				'[data-filter="all"]'
469			);
470
471			if (BX.type.isDomNode(itemCountNode))
472			{
473				itemCountNode.innerHTML = BX.message('SBB_IN_BASKET') + ' ' + this.result.BASKET_ITEMS_COUNT + ' ' + this.getGoodsMessage(this.result.BASKET_ITEMS_COUNT);
474				itemCountNode.style.display = '';
475			}
476		},
477
478		showSimilarCount: function(state)
479		{
480			var itemCountNode = this.getEntity(
481				this.getCacheNode(this.ids.itemListWrapper),
482				'basket-items-count',
483				'[data-filter="similar"]'
484			);
485
486			if (BX.type.isDomNode(itemCountNode))
487			{
488				if (state)
489				{
490					itemCountNode.innerHTML = this.sortedItems.length + ' '
491						+ this.getGoodsMessage(this.result.BASKET_ITEMS_COUNT, 'SBB_SIMILAR_ITEM');
492					itemCountNode.style.display = '';
493				}
494				else
495				{
496					itemCountNode.style.display = 'none';
497				}
498			}
499		},
500
501		showWarningItemsCount: function()
502		{
503			var itemCountNode = this.getEntity(
504				this.getCacheNode(this.ids.itemListWrapper),
505				'basket-items-count',
506				'[data-filter="warning"]'
507			);
508
509			if (BX.type.isDomNode(itemCountNode))
510			{
511				if (this.warningItems.length)
512				{
513					itemCountNode.innerHTML = this.warningItems.length + ' ' + BX.message('SBB_BASKET_ITEMS_WARNING');
514					itemCountNode.style.display = '';
515				}
516				else
517				{
518					itemCountNode.style.display = 'none';
519				}
520			}
521		},
522
523		showNotAvailableItemsCount: function()
524		{
525			var itemCountNode = this.getEntity(
526				this.getCacheNode(this.ids.itemListWrapper),
527				'basket-items-count',
528				'[data-filter="not-available"]'
529			);
530
531			if (BX.type.isDomNode(itemCountNode))
532			{
533				if (parseInt(this.result.NOT_AVAILABLE_BASKET_ITEMS_COUNT))
534				{
535					itemCountNode.innerHTML = this.result.NOT_AVAILABLE_BASKET_ITEMS_COUNT + ' '
536						+ this.getGoodsMessage(this.result.NOT_AVAILABLE_BASKET_ITEMS_COUNT, 'SBB_NOT_AVAILABLE_ITEM');
537					itemCountNode.style.display = '';
538				}
539				else
540				{
541					itemCountNode.style.display = 'none';
542				}
543			}
544		},
545
546		showDelayedItemsCount: function()
547		{
548			var itemCountNode = this.getEntity(
549				this.getCacheNode(this.ids.itemListWrapper),
550				'basket-items-count',
551				'[data-filter="delayed"]'
552			);
553
554			if (BX.type.isDomNode(itemCountNode))
555			{
556				if (parseInt(this.result.DELAYED_BASKET_ITEMS_COUNT))
557				{
558					itemCountNode.innerHTML = this.result.DELAYED_BASKET_ITEMS_COUNT + ' '
559						+ this.getGoodsMessage(this.result.DELAYED_BASKET_ITEMS_COUNT, 'SBB_DELAYED_ITEM');
560					itemCountNode.style.display = '';
561				}
562				else
563				{
564					itemCountNode.style.display = 'none';
565				}
566			}
567		},
568
569		getGoodsMessage: function(count, customMessage)
570		{
571			var mesCode;
572			var countReminder = (count > 10 && count < 20) ? 0 : count % 10;
573
574			if (countReminder === 1)
575			{
576				mesCode = customMessage || 'SBB_GOOD';
577			}
578			else if (countReminder >= 2 && countReminder <= 4)
579			{
580				mesCode = customMessage ? customMessage + '_2' : 'SBB_GOOD_2';
581			}
582			else
583			{
584				mesCode = customMessage ? customMessage + 'S' : 'SBB_GOODS';
585			}
586
587			return BX.message(mesCode);
588		},
589
590		bindTotalEvents: function(node)
591		{
592			if (!this.result.TOTAL_RENDER_DATA.DISABLE_CHECKOUT)
593			{
594				BX.bind(this.getEntity(node, 'basket-checkout-button'), 'click', BX.proxy(this.checkOutAction, this));
595			}
596
597			BX.bind(this.getEntity(node, 'basket-coupon-input'), 'change', BX.proxy(this.addCouponAction, this));
598			BX.bind(this.getEntity(node, 'basket-coupon-input'), 'paste', BX.proxy(this.pasteCouponAction, this));
599
600			var couponNodes = this.getEntities(node, 'basket-coupon-delete');
601			for (var i = 0, l = couponNodes.length; i < l; i++)
602			{
603				BX.bind(couponNodes[i], 'click', BX.proxy(this.removeCouponAction, this));
604			}
605
606		},
607
608		checkOutAction: function()
609		{
610			document.location.href = this.params.PATH_TO_ORDER;
611		},
612
613		addCouponAction: function(event)
614		{
615			var target = BX.getEventTarget(event);
616			if (target && target.value)
617			{
618				this.actionPool.addCoupon(target.value);
619				target.disabled = true;
620			}
621		},
622
623		pasteCouponAction: function(event)
624		{
625			setTimeout(BX.delegate(function() {
626				this.addCouponAction(event);
627			}, this), 10);
628		},
629
630		removeCouponAction: function()
631		{
632			var value = BX.proxy_context && BX.util.trim(BX.proxy_context.getAttribute('data-coupon'));
633			if (value)
634			{
635				this.actionPool.removeCoupon(value);
636			}
637		},
638
639		initializeActionPool: function()
640		{
641			this.actionPool = new BX.Sale.BasketActionPool(this);
642		},
643
644		initializeFilter: function()
645		{
646			this.filter = new BX.Sale.BasketFilter(this);
647		},
648
649		/**
650		 * Send ajax request with basket data and executes callback by action
651		 */
652		sendRequest: function(action, data)
653		{
654			this.lastAction = action;
655
656			if (this.lastAction === 'recalculateAjax')
657			{
658				// we use it to reload all items if applied discounts changed
659				data.lastAppliedDiscounts = BX.util.array_keys(this.result.FULL_DISCOUNT_LIST).join(',');
660
661				if (this.params.USE_ENHANCED_ECOMMERCE === 'Y')
662				{
663					this.checkAnalytics(data);
664				}
665			}
666
667			BX.ajax({
668				method: 'POST',
669				dataType: 'json',
670				url: this.ajaxUrl,
671				data: this.getData(data),
672				onsuccess: BX.delegate(function(result) {
673					this.actionPool.doProcessing(false);
674
675					if (!BX.type.isPlainObject(result))
676						return;
677
678					this.actionPool.setRefreshStatus(result.BASKET_REFRESHED);
679
680					if (result.RESTORED_BASKET_ITEMS)
681					{
682						this.restoreBasketItems(result.RESTORED_BASKET_ITEMS);
683					}
684					
685					if (result.DELETED_BASKET_ITEMS)
686					{
687						this.deleteBasketItems(result.DELETED_BASKET_ITEMS, this.params.SHOW_RESTORE === 'Y');
688					}
689					
690					if (result.MERGED_BASKET_ITEMS)
691					{
692						this.deleteBasketItems(result.MERGED_BASKET_ITEMS, false, true);
693					}
694					
695					this.applyBasketResult(result.BASKET_DATA);
696					this.editBasketItems(this.getItemsToEdit());
697					this.editTotal();
698					
699					this.adjustBasketWrapperHeight();
700					this.applyPriceAnimation();
701					this.editWarnings();
702					
703					this.actionPool.switchTimer();
704
705					if (this.isBasketIntegrated() && this.isBasketChanged())
706					{
707						BX.Sale.OrderAjaxComponent.sendRequest();
708					}
709				}, this),
710				onfailure: BX.delegate(function() {
711					this.actionPool.doProcessing(false);
712				}, this)
713			});
714		},
715		
716		isBasketIntegrated: function()
717		{
718			return this.params.BASKET_WITH_ORDER_INTEGRATION === 'Y';
719		},
720
721		isBasketChanged: function()
722		{
723			return this.changedItems.length;
724		},
725
726		addPriceAnimationData: function(nodeId, start, finish, currency)
727		{
728			if (!BX.type.isPlainObject(this.priceAnimationData))
729			{
730				this.clearPriceAnimationData();
731			}
732
733			this.priceAnimationData.start[nodeId] = parseFloat(start);
734			this.priceAnimationData.finish[nodeId] = parseFloat(finish);
735			this.priceAnimationData.currency[nodeId] = currency;
736			this.priceAnimationData.int[nodeId] = (parseFloat(start) === parseInt(start)) && (parseFloat(finish) === parseInt(finish));
737		},
738
739		clearPriceAnimationData: function()
740		{
741			this.priceAnimationData = {
742				start: {},
743				finish: {},
744				currency: {},
745				int: {}
746			};
747		},
748
749		applyBasketResult: function(result)
750		{
751			this.changedItems = [];
752			this.clearPriceAnimationData();
753
754			if (!BX.type.isPlainObject(result))
755			{
756				return;
757			}
758
759			if (result.BASKET_ITEM_RENDER_DATA)
760			{
761				var i, newData;
762
763				for (i in result.BASKET_ITEM_RENDER_DATA)
764				{
765					if (result.BASKET_ITEM_RENDER_DATA.hasOwnProperty(i))
766					{
767						newData = result.BASKET_ITEM_RENDER_DATA[i];
768						newData.WARNINGS = this.checkBasketItemWarnings(newData, result.WARNING_MESSAGE_WITH_CODE);
769                        
770                        var setId = parseInt(this.itemsSets[newData.ID]);
771
772                        if(setId > 0)
773                        {
774                            continue;
775                        }
776						else if (this.items[newData.ID])
777						{
778							if (JSON.stringify(this.items[newData.ID]) === JSON.stringify(newData))
779							{
780								continue;
781							}
782						}
783						else
784						{
785							this.addSortedItem(newData.ID, true);
786						}
787
788						this.changedItems.push(newData.ID);
789
790						newData = this.checkBasketItemsAnimation(newData);
791						this.items[newData.ID] = newData;
792					}
793				}
794
795				this.changedItems = BX.util.array_unique(this.changedItems.concat(this.getChangedSimilarOffers()));
796
797				if (this.isBasketChanged())
798				{
799					this.sortSortedItems(true);
800				}
801			}
802
803			if (result.TOTAL_RENDER_DATA)
804			{
805				result.TOTAL_RENDER_DATA = this.checkTotalAnimation(result.TOTAL_RENDER_DATA);
806			}
807
808			this.result = result;
809		},
810
811		itemSortFunction: function(a, b)
812		{
813			if (!this.items.hasOwnProperty(a) || !this.items.hasOwnProperty(b))
814			{
815				return 0;
816			}
817
818			return parseFloat(this.items[a].SORT) - parseFloat(this.items[b].SORT);
819		},
820
821		getChangedSimilarOffers: function()
822		{
823			var changedSimilarOffers = [];
824
825			var otherSimilarItemsQuantity, totalSimilarItemsQuantity;
826			var hashMap = this.getHashMap();
827
828			for (var hash in hashMap)
829			{
830				if (hashMap.hasOwnProperty(hash))
831				{
832					if (hashMap[hash].length > 1)
833					{
834						for (var i = 0; i < hashMap[hash].length; i++)
835						{
836							otherSimilarItemsQuantity = 0;
837							totalSimilarItemsQuantity = 0;
838
839							for (var k = 0; k < hashMap[hash].length; k ++)
840							{
841								if (hashMap[hash][k] != hashMap[hash][i])
842								{
843									otherSimilarItemsQuantity += parseFloat(this.items[hashMap[hash][k]].QUANTITY);
844								}
845
846								totalSimilarItemsQuantity += parseFloat(this.items[hashMap[hash][k]].QUANTITY);
847							}
848
849							if (
850								!this.items[hashMap[hash][i]].HAS_SIMILAR_ITEMS
851								|| this.items[hashMap[hash][i]].SIMILAR_ITEMS_QUANTITY != otherSimilarItemsQuantity
852								|| this.items[hashMap[hash][i]].TOTAL_SIMILAR_ITEMS_QUANTITY != totalSimilarItemsQuantity
853							)
854							{
855								changedSimilarOffers.push(hashMap[hash][i]);
856
857								this.items[hashMap[hash][i]].HAS_SIMILAR_ITEMS = true;
858								this.items[hashMap[hash][i]].SIMILAR_ITEMS_QUANTITY = otherSimilarItemsQuantity;
859								this.items[hashMap[hash][i]].TOTAL_SIMILAR_ITEMS_QUANTITY = totalSimilarItemsQuantity;
860
861								this.items[hashMap[hash][i]].ALL_AVAILABLE_QUANTITY = this.items[hashMap[hash][i]].AVAILABLE_QUANTITY;
862								this.items[hashMap[hash][i]].AVAILABLE_QUANTITY = this.items[hashMap[hash][i]].ALL_AVAILABLE_QUANTITY - otherSimilarItemsQuantity;
863							}
864						}
865					}
866					else if (hashMap[hash][0] && this.items[hashMap[hash][0]].HAS_SIMILAR_ITEMS)
867					{
868						changedSimilarOffers.push(hashMap[hash][0]);
869
870						delete this.items[hashMap[hash][0]].HAS_SIMILAR_ITEMS;
871						delete this.items[hashMap[hash][0]].SIMILAR_ITEMS_QUANTITY;
872						delete this.items[hashMap[hash][0]].TOTAL_SIMILAR_ITEMS_QUANTITY;
873
874						this.items[hashMap[hash][0]].AVAILABLE_QUANTITY = this.items[hashMap[hash][0]].ALL_AVAILABLE_QUANTITY;
875						delete this.items[hashMap[hash][0]].ALL_AVAILABLE_QUANTITY;
876					}
877				}
878			}
879
880			return changedSimilarOffers;
881		},
882
883		getHashMap: function()
884		{
885			var hashMap = {};
886
887			for (var id in this.items)
888			{
889				if (this.items.hasOwnProperty(id) && this.isItemAvailable(id))
890				{
891					if (!hashMap.hasOwnProperty(this.items[id].HASH))
892					{
893						hashMap[this.items[id].HASH] = [];
894					}
895
896					hashMap[this.items[id].HASH].push(id);
897				}
898			}
899
900			return hashMap;
901		},
902
903		isItemAvailable: function(itemId)
904		{
905			var sortedItems = this.filter.isActive() ? this.filter.realSortedItems : this.sortedItems;
906
907			return !this.items[itemId].NOT_AVAILABLE
908				&& !this.items[itemId].SHOW_RESTORE
909				&& BX.util.in_array(itemId, sortedItems);
910		},
911		
912		isSetItem: function(itemId)
913		{
914		    var sortedItems = this.filter.isActive() ? this.filter.realSortedItems : this.sortedItems;
915		    
916		    return this.items[itemId].IS_SET
917		        && BX.util.in_array(itemId, sortedItems);
918		},
919
920		checkTotalAnimation: function(totalData)
921		{
922			if (this.result && this.result.TOTAL_RENDER_DATA && parseFloat(this.result.TOTAL_RENDER_DATA.PRICE) > parseFloat(totalData.PRICE))
923			{
924				totalData.PRICE_NEW = totalData.PRICE;
925				totalData.PRICE = this.result.TOTAL_RENDER_DATA.PRICE;
926
927				totalData.PRICE_FORMATED_NEW = totalData.PRICE_FORMATED;
928				totalData.PRICE_FORMATED = this.result.TOTAL_RENDER_DATA.PRICE_FORMATED;
929
930				this.addPriceAnimationData(this.ids.total, totalData.PRICE, totalData.PRICE_NEW, totalData.CURRENCY);
931			}
932
933			return totalData;
934		},
935
936		checkBasketItemsAnimation: function(itemData)
937		{
938			var itemId = itemData.ID;
939
940			if (this.items[itemId])
941			{
942				var quantityNode = BX(this.ids.quantity + itemId);
943				if (
944					BX.type.isDomNode(quantityNode)
945					&& !this.actionPool.isItemInPool(itemId)
946					&& parseFloat(quantityNode.value) !== parseFloat(itemData.QUANTITY)
947				)
948				{
949					itemData.QUANTITY_ANIMATION = true;
950					this.actionPool.clearLastActualQuantityPool(itemId);
951				}
952
953				if (parseFloat(this.items[itemId].PRICE) > parseFloat(itemData.PRICE))
954				{
955					itemData.PRICE_NEW = itemData.PRICE;
956					itemData.PRICE = this.items[itemId].PRICE;
957
958					itemData.PRICE_FORMATED_NEW = itemData.PRICE_FORMATED;
959					itemData.PRICE_FORMATED = this.items[itemId].PRICE_FORMATED;
960
961					this.addPriceAnimationData(this.ids.price + itemId, itemData.PRICE, itemData.PRICE_NEW, itemData.CURRENCY);
962				}
963
964				if (
965					BX.util.in_array('SUM', this.params.COLUMNS_LIST)
966					&& parseFloat(this.items[itemId].SUM_PRICE) > parseFloat(itemData.SUM_PRICE)
967					&& parseFloat(this.items[itemId].QUANTITY) === parseFloat(itemData.QUANTITY)
968				)
969				{
970					itemData.SUM_PRICE_NEW = itemData.SUM_PRICE;
971					itemData.SUM_PRICE = this.items[itemId].SUM_PRICE;
972
973					itemData.SUM_PRICE_FORMATED_NEW = itemData.SUM_PRICE_FORMATED;
974					itemData.SUM_PRICE_FORMATED = this.items[itemId].SUM_PRICE_FORMATED;
975
976					this.addPriceAnimationData(this.ids.sumPrice + itemId, itemData.SUM_PRICE, itemData.SUM_PRICE_NEW, itemData.CURRENCY);
977				}
978			}
979
980			return itemData;
981		},
982
983		getData: function(data)
984		{
985			data = data || {};
986
987			data[this.params.ACTION_VARIABLE] = this.lastAction;
988			data.via_ajax = 'Y';
989			data.site_id = this.siteId;
990			data.site_template_id = this.siteTemplateId;
991			data.sessid = BX.bitrix_sessid();
992			data.template = this.template;
993			data.signedParamsString = this.signedParamsString;
994
995			return data;
996		},
997
998		startLoader: function()
999		{
1000			// if (!this.loadingScreen)
1001			// {
1002			// 	this.loadingScreen = new BX.PopupWindow('loading_screen', null, {
1003			// 		events: {
1004			// 			onAfterPopupShow: BX.delegate(function() {
1005			// 				BX.cleanNode(this.loadingScreen.popupContainer);
1006			// 				BX.removeClass(this.loadingScreen.popupContainer, 'popup-window');
1007			// 				this.loadingScreen.popupContainer.appendChild(
1008			// 					BX.create('IMG', {props: {src: this.templateFolder + '/images/loader.gif'}})
1009			// 				);
1010			// 				this.loadingScreen.popupContainer.removeAttribute('style');
1011			// 				this.loadingScreen.popupContainer.style.display = 'block';
1012			// 			}, this)
1013			// 		}
1014			// 	});
1015			// 	BX.addClass(this.loadingScreen.popupContainer, 'bx-step-opacity');
1016			// }
1017			//
1018			// this.loadingScreen.show();
1019		},
1020
1021		/**
1022		 * Hiding loader image with overlay.
1023		 */
1024		endLoader: function()
1025		{
1026			// if (this.loadingScreen && this.loadingScreen.isShown())
1027			// {
1028			// 	this.loadingScreen.close();
1029			// }
1030		},
1031
1032		editWarnings: function()
1033		{
1034			this.editGeneralWarnings();
1035			this.editBasketItemWarnings();
1036			this.toggleWarningBlock();
1037			this.showWarningItemsCount();
1038		},
1039
1040		editGeneralWarnings: function()
1041		{
1042			var warningsNode = this.getEntity(this.getCacheNode(this.ids.warning), 'basket-general-warnings');
1043
1044			if (BX.type.isDomNode(warningsNode))
1045			{
1046				var generalWarningText = warningsNode.innerHTML;
1047
1048				if (this.result.WARNING_MESSAGE_WITH_CODE)
1049				{
1050					for (var code in this.result.WARNING_MESSAGE_WITH_CODE)
1051					{
1052						if (this.result.WARNING_MESSAGE_WITH_CODE.hasOwnProperty(code))
1053						{
1054							if (
1055								!this.items[code]
1056								&& generalWarningText.indexOf(this.result.WARNING_MESSAGE_WITH_CODE[code]) === -1
1057							)
1058							{
1059								generalWarningText += this.result.WARNING_MESSAGE_WITH_CODE[code] + '<br/>';
1060							}
1061						}
1062					}
1063				}
1064
1065				if (generalWarningText)
1066				{
1067					warningsNode.innerHTML = generalWarningText;
1068					warningsNode.style.display = '';
1069				}
1070				else
1071				{
1072					warningsNode.style.display = 'none';
1073					warningsNode.innerHTML = '';
1074				}
1075			}
1076		},
1077
1078		editBasketItemWarnings: function()
1079		{
1080			var itemsWarningsNode = this.getEntity(this.getCacheNode(this.ids.warning), 'basket-item-warnings');
1081
1082			if (BX.type.isDomNode(itemsWarningsNode))
1083			{
1084				if (this.warningItems.length)
1085				{
1086					var warningCount = this.getEntity(itemsWarningsNode, 'basket-items-warning-count');
1087					if (BX.type.isDomNode(warningCount))
1088					{
1089						warningCount.innerHTML = this.warningItems.length + ' ' + this.getGoodsMessage(this.warningItems.length);
1090					}
1091
1092					itemsWarningsNode.style.display = '';
1093				}
1094				else if (itemsWarningsNode.style.display !== 'none')
1095				{
1096					itemsWarningsNode.style.display = 'none';
1097
1098					if (this.filter.isActive())
1099					{
1100						this.toggleFilter('all');
1101					}
1102				}
1103			}
1104		},
1105
1106		toggleWarningBlock: function()
1107		{
1108			var warningNode = this.getCacheNode(this.ids.warning);
1109
1110			if (BX.type.isDomNode(warningNode))
1111			{
1112				var generalWarningNode = this.getEntity(warningNode, 'basket-general-warnings');
1113				var itemsWarningsNode = this.getEntity(warningNode, 'basket-item-warnings');
1114
1115				if (
1116					(!BX.type.isDomNode(generalWarningNode) || generalWarningNode.style.display === 'none')
1117					&& (!BX.type.isDomNode(itemsWarningsNode) || itemsWarningsNode.style.display === 'none')
1118				)
1119				{
1120					warningNode.style.display = 'none';
1121				}
1122				else
1123				{
1124					warningNode.style.display = '';
1125				}
1126			}
1127		},
1128
1129		checkBasketItemWarnings: function(itemData, warnings)
1130		{
1131			if (!itemData)
1132				return;
1133
1134			var itemWarnings;
1135
1136			if (this.items[itemData.ID] && this.lastAction === 'refreshAjax')
1137			{
1138				itemWarnings = this.items[itemData.ID].WARNINGS;
1139			}
1140			else
1141			{
1142				itemWarnings = [];
1143			}
1144
1145			if (BX.type.isArray(warnings[itemData.ID]) && warnings[itemData.ID].length)
1146			{
1147				for (var i in warnings[itemData.ID])
1148				{
1149					if (warnings[itemData.ID].hasOwnProperty(i) && !BX.util.in_array(warnings[itemData.ID][i], itemWarnings))
1150					{
1151						itemWarnings.push(warnings[itemData.ID][i]);
1152					}
1153				}
1154			}
1155
1156			if (itemWarnings.length)
1157			{
1158				if (!BX.util.in_array(itemData.ID, this.warningItems))
1159				{
1160					this.warningItems.push(itemData.ID);
1161				}
1162			}
1163			else if (BX.util.in_array(itemData.ID, this.warningItems))
1164			{
1165				this.warningItems.splice(BX.util.array_search(itemData.ID, this.warningItems), 1);
1166			}
1167
1168			return itemWarnings;
1169		},
1170
1171		removeAllWarnings: function(event)
1172		{
1173			this.clearGeneralWarnings();
1174			this.clearBasketItemsWarnings();
1175
1176			this.editWarnings();
1177
1178			event && event.preventDefault();
1179		},
1180
1181		clearGeneralWarnings: function()
1182		{
1183			this.result.WARNING_MESSAGE_WITH_CODE = {};
1184
1185			var generalWarningNode = this.getEntity(this.getCacheNode(this.ids.warning), 'basket-general-warnings');
1186
1187			if (BX.type.isDomNode(generalWarningNode))
1188			{
1189				generalWarningNode.innerHTML = '';
1190			}
1191		},
1192
1193		clearBasketItemsWarnings: function()
1194		{
1195			var itemsToEdit = [];
1196
1197			for (var i in this.warningItems)
1198			{
1199				if (this.warningItems.hasOwnProperty(i))
1200				{
1201					this.items[this.warningItems[i]].WARNINGS = [];
1202
1203					if (this.isItemShown(this.warningItems[i]))
1204					{
1205						itemsToEdit.push(this.warningItems[i]);
1206					}
1207				}
1208			}
1209
1210			this.warningItems = [];
1211			this.editBasketItems(itemsToEdit);
1212		},
1213
1214		isItemShown: function(itemId)
1215		{
1216			return BX.util.in_array(itemId, this.shownItems);
1217		},
1218
1219		initializeBasketItems: function()
1220		{
1221			if (Object.keys(this.items).length === 0)
1222				return;
1223
1224			for (var i = 0; i < this.sortedItems.length; i++)
1225			{
1226				if (this.useDynamicScroll && this.shownItems.length >= this.maxItemsShowCount)
1227				{
1228					break;
1229				}
1230
1231				this.createBasketItem(this.sortedItems[i]);
1232			}
1233		},
1234
1235		createBasketItem: function(itemId)
1236		{
1237			if (!this.items[itemId])
1238			{
1239				return;
1240			}
1241			
1242			var basketItemTemplate;
1243
1244            if(this.isSetItem(itemId) && this.items[itemId].ITEMS.length)
1245            {
1246                var basketItemsSetTemplate = this.getTemplate('basket-set-item-template');
1247                
1248                basketItemTemplate = this.getTemplate('basket-set-template');
1249                this.items[itemId].ITEMS_HTML = '';
1250                
1251                for (let i = 0; i < this.items[itemId].ITEMS.length; i++)
1252                {
1253                    this.items[itemId].ITEMS_HTML += this.renderBasketItem(basketItemsSetTemplate, this.items[itemId].ITEMS[i]);
1254                    this.sortedItems.push(this.items[itemId].ITEMS[i].ID);
1255                    this.itemsSets[this.items[itemId].ITEMS[i].ID] = itemId;
1256                }
1257            }
1258            else
1259            {
1260                basketItemTemplate = this.getTemplate('basket-item-template');
1261            }
1262            
1263            if (basketItemTemplate)
1264            {
1265                var basketItemHtml = this.renderBasketItem(basketItemTemplate, this.items[itemId]);
1266                var sortIndex = BX.util.array_search(itemId, this.sortedItems);
1267
1268                if (this.shownItems.length && sortIndex >= 0)
1269                {
1270                    if (sortIndex < BX.util.array_search(this.shownItems[0], this.sortedItems))
1271                    {
1272                        // insert before
1273                        BX(this.ids.item + this.shownItems[0]).insertAdjacentHTML('beforebegin', basketItemHtml);
1274                        this.shownItems.unshift(itemId);
1275                    }
1276                    else if (sortIndex > BX.util.array_search(this.shownItems[this.shownItems.length - 1], this.sortedItems))
1277                    {
1278                        // insert after
1279                        BX(this.ids.item + this.shownItems[this.shownItems.length - 1]).insertAdjacentHTML('afterend', basketItemHtml);
1280                        this.shownItems.push(itemId);
1281                    }
1282                    else
1283                    {
1284                        // insert between
1285                        BX(this.ids.item + this.sortedItems[sortIndex + 1]).insertAdjacentHTML('beforebegin', basketItemHtml);
1286                        this.shownItems.splice(sortIndex + 1, 0, itemId);
1287                    }
1288                }
1289                else
1290                {
1291                    this.getCacheNode(this.ids.itemListTable).insertAdjacentHTML('beforeend', basketItemHtml);
1292                    this.shownItems.push(itemId);
1293                }
1294
1295                this.bindBasketItemEvents(this.items[itemId]);
1296
1297                if (this.filter.isActive())
1298                {
1299                    if(this.isSetItem(itemId))
1300                    {
1301                        for (let i = 0; i < this.items[itemId].ITEMS.length; i++)
1302                        {
1303                            this.filter.highlightSearchMatch(this.items[itemId].ITEMS[i]);
1304                        }
1305                    }
1306                    else
1307                    {
1308                        this.filter.highlightSearchMatch(this.items[itemId]);
1309                    }
1310                }
1311            }
1312		},
1313
1314		getItemsToEdit: function()
1315		{
1316			var itemIds = [];
1317
1318			if (this.isBasketChanged())
1319			{
1320				for (var i in this.changedItems)
1321				{
1322					if (this.changedItems.hasOwnProperty(i) && this.isItemShown(this.changedItems[i]))
1323					{
1324						itemIds.push(this.changedItems[i]);
1325					}
1326				}
1327			}
1328
1329			return itemIds;
1330		},
1331
1332		getItemsAfter: function()
1333		{
1334			var itemIdsAfter = [];
1335
1336			if (this.useDynamicScroll)
1337			{
1338				var lastShownItemId = this.shownItems[this.shownItems.length - 1] || false;
1339
1340				if (lastShownItemId)
1341				{
1342					var i = 0;
1343					var index = BX.util.array_search(lastShownItemId, this.sortedItems);
1344
1345					while (this.sortedItems[++index] && i++ < this.maxItemsShowCount)
1346					{
1347						itemIdsAfter.push(this.sortedItems[index]);
1348					}
1349				}
1350			}
1351
1352			return itemIdsAfter;
1353		},
1354
1355		editBasketItems: function(itemIds)
1356		{
1357			if (!itemIds || itemIds.length === 0)
1358			{
1359				return;
1360			}
1361
1362			var i, item;
1363
1364			for (i in itemIds)
1365			{
1366				if (!itemIds.hasOwnProperty(i) || !BX.type.isPlainObject(this.items[itemIds[i]]))
1367				{
1368					continue;
1369				}
1370
1371				item = this.items[itemIds[i]];
1372
1373				if (this.actionPool.isItemInPool(item.ID))
1374				{
1375					if (!BX.util.in_array(item.ID, this.postponedItems))
1376					{
1377						this.postponedItems.push(item.ID);
1378					}
1379
1380					continue;
1381				}
1382
1383				if (BX.type.isDomNode(BX(this.ids.item + item.ID)))
1384				{
1385					this.redrawBasketItemNode(item.ID);
1386					this.applyQuantityAnimation(item.ID);
1387				}
1388				else
1389				{
1390					this.createBasketItem(item.ID);
1391				}
1392			}
1393		},
1394
1395		editPostponedBasketItems: function()
1396		{
1397			if (!this.postponedItems.length)
1398				return;
1399
1400			var itemsToEdit = [];
1401
1402			for (var i in this.postponedItems)
1403			{
1404				if (this.postponedItems.hasOwnProperty(i) && this.isItemShown(this.postponedItems[i]))
1405				{
1406					itemsToEdit.push(this.postponedItems[i]);
1407				}
1408			}
1409
1410			this.postponedItems = [];
1411			this.editBasketItems(itemsToEdit);
1412		},
1413
1414		applyQuantityAnimation: function(itemId)
1415		{
1416			var basketItemNode = BX(this.ids.item + itemId);
1417
1418			if (BX.type.isDomNode(basketItemNode) && this.items[itemId])
1419			{
1420				if (this.items[itemId].QUANTITY_ANIMATION)
1421				{
1422					BX.addClass(BX(this.ids.quantity + itemId), 'basket-updated');
1423				}
1424			}
1425		},
1426
1427		applyPriceAnimation: function()
1428		{
1429			if (!this.priceAnimationData || Object.keys(this.priceAnimationData.start).length === 0)
1430				return;
1431
1432			var animationData = this.priceAnimationData,
1433				nodeCache = {};
1434
1435			new BX.easing({
1436				duration: this.params.USE_PRICE_ANIMATION === 'Y' ? this.duration.priceAnimation : 1,
1437				start: animationData.start,
1438				finish: animationData.finish,
1439				transition: BX.easing.makeEaseOut(BX.easing.transitions.quad),
1440				step: BX.delegate(function(state){
1441					for (var nodeId in animationData.start)
1442					{
1443						if (animationData.start.hasOwnProperty(nodeId))
1444						{
1445							if (!nodeCache[nodeId])
1446							{
1447								if (nodeId === this.ids.total)
1448								{
1449									nodeCache[nodeId] = this.getEntities(this.getCacheNode(this.ids.basketRoot), this.ids.total);
1450								}
1451								else
1452								{
1453									var node = BX(nodeId);
1454									nodeCache[nodeId] = node ? [node] : [];
1455								}
1456							}
1457
1458							if (!animationData.int[nodeId])
1459							{
1460								// fix price blinking
1461								state[nodeId] = (state[nodeId] + (state[nodeId] % 1000) / 1000).toFixed(5);
1462							}
1463
1464							for (var i = 0; i < nodeCache[nodeId].length; i++)
1465							{
1466								nodeCache[nodeId][i].innerHTML = this.getFormatPrice(state[nodeId], animationData.currency[nodeId]);
1467							}
1468						}
1469					}
1470				}, this),
1471				complete: BX.delegate(function() {
1472					var nodeId, formattedPrice, itemId, type;
1473
1474					for (nodeId in animationData.start)
1475					{
1476						if (animationData.start.hasOwnProperty(nodeId))
1477						{
1478							formattedPrice = this.getFormatPrice(animationData.finish[nodeId], animationData.currency[nodeId]);
1479
1480							for (var i = 0; i < nodeCache[nodeId].length; i++)
1481							{
1482								nodeCache[nodeId][i].innerHTML = formattedPrice;
1483							}
1484
1485							if (nodeId.indexOf(this.ids.sumPrice) !== -1)
1486							{
1487								type = 'SUM_PRICE';
1488								itemId = nodeId.substr(this.ids.sumPrice.length);
1489							}
1490							else if (nodeId.indexOf(this.ids.price) !== -1)
1491							{
1492								type = 'PRICE';
1493								itemId = nodeId.substr(this.ids.price.length);
1494							}
1495							else if (nodeId.indexOf(this.ids.total) !== -1)
1496							{
1497								type = 'TOTAL';
1498								itemId = '';
1499							}
1500							else
1501							{
1502								itemId = '';
1503								type = '';
1504							}
1505
1506							if (BX.type.isNotEmptyString(type))
1507							{
1508								if (itemId)
1509								{
1510									this.items[itemId][type] = animationData.finish[nodeId];
1511									delete this.items[itemId][type + '_NEW'];
1512									this.items[itemId][type + '_FORMATED'] = formattedPrice;
1513									delete this.items[itemId][type + '_FORMATED_NEW'];
1514								}
1515								else if (type === 'TOTAL')
1516								{
1517									this.result.TOTAL_RENDER_DATA.PRICE = animationData.finish[nodeId];
1518									delete this.result.TOTAL_RENDER_DATA.PRICE_NEW;
1519									this.result.TOTAL_RENDER_DATA.PRICE_FORMATED = formattedPrice;
1520									delete this.result.TOTAL_RENDER_DATA.PRICE_FORMATED_NEW;
1521								}
1522							}
1523						}
1524					}
1525
1526					this.filter.highlightFoundItems();
1527				}, this)
1528			}).animate();
1529		},
1530
1531		getFormatPrice: function(price, currency)
1532		{
1533			return BX.Currency.currencyFormat(price, currency, true);
1534		},
1535
1536		deleteBasketItems: function(items, restore, final)
1537		{
1538			if (!items || !items.length)
1539			{
1540				return;
1541			}
1542
1543			for (var i in items)
1544			{
1545				if (items.hasOwnProperty(i))
1546				{
1547					this.deleteBasketItem(items[i], restore, final);
1548				}
1549			}
1550		},
1551
1552		deleteBasketItem: function(itemId, restore, final)
1553		{
1554			// delete not available item with no chance to restore
1555			if (this.items[itemId].NOT_AVAILABLE && restore)
1556			{
1557				restore = false;
1558				final = true;
1559			}
1560
1561			if (restore)
1562			{
1563				this.items[itemId].SHOW_RESTORE = true;
1564				this.items[itemId].SHOW_LOADING = false;
1565				this.redrawBasketItemNode(itemId);
1566			}
1567			else
1568			{
1569				this.changeShownItem(itemId);
1570				BX.remove(BX(this.ids.item + itemId));
1571			}
1572
1573			if (final)
1574			{
1575				this.changeSortedItem(itemId, false, true);
1576				this.changeShownItem(itemId, false, true);
1577			}
1578		},
1579
1580		addSortedItem: function(itemId, all)
1581		{
1582			this.sortedItems.push(itemId.toString());
1583
1584			if (all && this.filter.isActive())
1585			{
1586				this.filter.realSortedItems.push(itemId.toString());
1587			}
1588		},
1589
1590		changeSortedItem: function(itemId, newItemId, all)
1591		{
1592			var index = BX.util.array_search(itemId, this.sortedItems);
1593
1594			if (index >= 0)
1595			{
1596				if (newItemId)
1597				{
1598					this.sortedItems.splice(index, 1, newItemId.toString());
1599				}
1600				else
1601				{
1602					this.sortedItems.splice(index, 1);
1603				}
1604			}
1605
1606			if (all && this.filter.isActive())
1607			{
1608				index = BX.util.array_search(itemId, this.filter.realSortedItems);
1609
1610				if (index >= 0)
1611				{
1612					if (newItemId)
1613					{
1614						this.filter.realSortedItems.splice(index, 1, newItemId.toString());
1615					}
1616					else
1617					{
1618						this.filter.realSortedItems.splice(index, 1);
1619					}
1620				}
1621			}
1622		},
1623
1624		sortSortedItems: function(all)
1625		{
1626			this.sortedItems.sort(BX.proxy(this.itemSortFunction, this));
1627
1628console.log(this);
1629			if (all && this.filter.isActive())
1630			{
1631				this.filter.realSortedItems.sort(BX.proxy(this.itemSortFunction, this));
1632			}
1633		},
1634
1635		changeShownItem: function(itemId, newItemId, all)
1636		{
1637			var index = BX.util.array_search(itemId, this.shownItems);
1638
1639			if (index >= 0)
1640			{
1641				if (newItemId)
1642				{
1643					this.shownItems.splice(index, 1, newItemId.toString());
1644				}
1645				else
1646				{
1647					this.shownItems.splice(index, 1);
1648				}
1649			}
1650
1651			if (all && this.filter.isActive())
1652			{
1653				index = BX.util.array_search(itemId, this.filter.realShownItems);
1654
1655				if (index >= 0)
1656				{
1657					if (newItemId)
1658					{
1659						this.filter.realShownItems.splice(index, 1, newItemId.toString());
1660					}
1661					else
1662					{
1663						this.filter.realShownItems.splice(index, 1);
1664					}
1665				}
1666			}
1667		},
1668
1669		redrawBasketItemNode: function(itemId)
1670		{
1671			var basketItemNode = BX(this.ids.item + itemId);
1672
1673			if (!this.items[itemId] || !BX.type.isDomNode(basketItemNode))
1674				return;
1675
1676			var basketItemTemplate;
1677
1678            if(this.isSetItem(itemId) && this.items[itemId].ITEMS.length)
1679            {
1680                var basketItemsSetTemplate = this.getTemplate('basket-set-item-template');
1681                
1682                basketItemTemplate = this.getTemplate('basket-set-template');
1683                this.items[itemId].ITEMS_HTML = '';
1684                
1685                for (let i = 0; i < this.items[itemId].ITEMS.length; i++)
1686                {
1687                    this.items[itemId].ITEMS_HTML += this.renderBasketItem(basketItemsSetTemplate, this.items[itemId].ITEMS[i]);
1688                }
1689            }
1690            else
1691            {
1692                basketItemTemplate = this.getTemplate('basket-item-template');
1693            }
1694            
1695			if (basketItemTemplate)
1696			{
1697				var nodeAligner = BX(this.ids.itemHeightAligner + itemId),
1698					oldHeight;
1699
1700				if (BX.type.isDomNode(nodeAligner))
1701				{
1702					oldHeight = nodeAligner.clientHeight;
1703				}
1704
1705				var basketItemHtml = this.renderBasketItem(basketItemTemplate, this.items[itemId]);
1706				basketItemNode.insertAdjacentHTML('beforebegin', basketItemHtml);
1707				BX.remove(basketItemNode);
1708
1709				if (oldHeight)
1710				{
1711					nodeAligner = BX(this.ids.itemHeightAligner + itemId);
1712
1713					if (BX.type.isDomNode(nodeAligner) && nodeAligner.clientHeight < oldHeight)
1714					{
1715						nodeAligner.style.minHeight = oldHeight + 'px';
1716						setTimeout(function(){nodeAligner.style.minHeight = '0px';}, 1);
1717					}
1718				}
1719
1720				this.bindBasketItemEvents(this.items[itemId]);
1721
1722				if (this.filter.isActive())
1723				{
1724					this.filter.highlightSearchMatch(this.items[itemId]);
1725				}
1726			}
1727		},
1728
1729		restoreBasketItems: function(items)
1730		{
1731			if (!items || Object.keys(items).length === 0)
1732			{
1733				return;
1734			}
1735
1736			var oldItemId, newItemId, basketItemNode;
1737
1738			for (oldItemId in items)
1739			{
1740				if (items.hasOwnProperty(oldItemId))
1741				{
1742					newItemId = items[oldItemId];
1743
1744					if (this.isItemShown(oldItemId))
1745					{
1746						this.changeShownItem(oldItemId, newItemId, true);
1747
1748						basketItemNode = BX(this.ids.item + oldItemId);
1749						if (BX.type.isDomNode(basketItemNode))
1750						{
1751							basketItemNode.id = this.ids.item + newItemId;
1752							basketItemNode.setAttribute('data-id', newItemId);
1753						}
1754					}
1755
1756					this.changeSortedItem(oldItemId, false, true);
1757				}
1758			}
1759		},
1760
1761		bindBasketItemEvents: function(itemData)
1762		{
1763			if (!itemData)
1764				return;
1765
1766			var itemNode = BX(this.ids.item + itemData.ID);
1767			if (BX.type.isDomNode(itemNode))
1768			{
1769				this.bindQuantityEvents(itemNode, itemData);
1770				this.bindSkuEvents(itemNode, itemData);
1771				this.bindImageEvents(itemNode, itemData);
1772				this.bindActionEvents(itemNode, itemData);
1773				this.bindRestoreAction(itemNode, itemData);
1774				this.bindItemWarningEvents(itemNode, itemData);
1775			}
1776		},
1777
1778		bindQuantityEvents: function(node, data)
1779		{
1780			if (!node || !data || !this.isItemAvailable(data.ID))
1781				return;
1782
1783			var entity;
1784
1785			var block = this.getEntity(node, 'basket-item-quantity-block');
1786			if (block)
1787			{
1788				var startEventName = this.isTouch ? 'touchstart' : 'mousedown';
1789				var endEventName = this.isTouch ? 'touchend' : 'mouseup';
1790
1791				entity = this.getEntity(block, 'basket-item-quantity-minus');
1792				BX.bind(entity, startEventName, BX.proxy(this.startQuantityInterval, this));
1793				BX.bind(entity, endEventName, BX.proxy(this.clearQuantityInterval, this));
1794				BX.bind(entity, 'mouseout', BX.proxy(this.clearQuantityInterval, this));
1795				BX.bind(entity, 'click', BX.proxy(this.quantityMinus, this));
1796
1797				entity = this.getEntity(block, 'basket-item-quantity-plus');
1798				BX.bind(entity, startEventName, BX.proxy(this.startQuantityInterval, this));
1799				BX.bind(entity, endEventName, BX.proxy(this.clearQuantityInterval, this));
1800				BX.bind(entity, 'mouseout', BX.proxy(this.clearQuantityInterval, this));
1801				BX.bind(entity, 'click', BX.proxy(this.quantityPlus, this));
1802
1803				entity = this.getEntity(block, 'basket-item-quantity-field');
1804				BX.bind(entity, 'change', BX.proxy(this.quantityChange, this));
1805			}
1806		},
1807
1808		startQuantityInterval: function()
1809		{
1810			var target = BX.proxy_context;
1811			var func = target.getAttribute('data-entity') === 'basket-item-quantity-minus'
1812				? BX.proxy(this.quantityMinus, this)
1813				: BX.proxy(this.quantityPlus, this);
1814
1815			this.quantityDelay = setTimeout(
1816				BX.delegate(function() {
1817					this.quantityTimer = setInterval(function(){func(target);}, 150);
1818				}, this),
1819				300
1820			);
1821		},
1822
1823		clearQuantityInterval: function()
1824		{
1825			clearTimeout(this.quantityDelay);
1826			clearInterval(this.quantityTimer);
1827		},
1828
1829		quantityPlus: function(target)
1830		{
1831			if (!BX.type.isDomNode(target))
1832			{
1833				target = BX.proxy_context;
1834				this.clearQuantityInterval();
1835			}
1836
1837			var itemData = this.getItemDataByTarget(target);
1838			if (itemData)
1839			{
1840				var quantityField = BX(this.ids.quantity + itemData.ID);
1841				var isQuantityFloat = this.isQuantityFloat(itemData);
1842
1843				var currentQuantity = isQuantityFloat ? parseFloat(quantityField.value) : Math.round(quantityField.value);
1844				var measureRatio = isQuantityFloat ? parseFloat(itemData.MEASURE_RATIO) : parseInt(itemData.MEASURE_RATIO);
1845
1846				var quantity = parseFloat((currentQuantity + measureRatio).toFixed(5));
1847				quantity = this.getCorrectQuantity(itemData, quantity);
1848
1849				this.setQuantity(itemData, quantity);
1850			}
1851		},
1852
1853		quantityMinus: function(target)
1854		{
1855			target = BX.type.isDomNode(target) ? target : BX.proxy_context;
1856
1857			var itemData = this.getItemDataByTarget(target);
1858			if (itemData)
1859			{
1860				var quantityField = BX(this.ids.quantity + itemData.ID);
1861				var isQuantityFloat = this.isQuantityFloat(itemData);
1862
1863				var currentQuantity = isQuantityFloat ? parseFloat(quantityField.value) : Math.round(quantityField.value);
1864				var measureRatio = isQuantityFloat ? parseFloat(itemData.MEASURE_RATIO) : parseInt(itemData.MEASURE_RATIO);
1865
1866				var quantity = parseFloat((currentQuantity - measureRatio).toFixed(5));
1867				quantity = this.getCorrectQuantity(itemData, quantity);
1868
1869				this.setQuantity(itemData, quantity);
1870			}
1871		},
1872
1873		quantityChange: function()
1874		{
1875			var itemData = this.getItemDataByTarget(BX.proxy_context);
1876			if (itemData)
1877			{
1878				var quantityField, quantity;
1879
1880				quantityField = BX(this.ids.quantity + itemData.ID);
1881				quantity = this.getCorrectQuantity(itemData, quantityField.value);
1882
1883				this.setQuantity(itemData, quantity);
1884			}
1885		},
1886
1887		isQuantityFloat: function(item)
1888		{
1889			return this.params.QUANTITY_FLOAT === 'Y' || (parseInt(item.MEASURE_RATIO) !== parseFloat(item.MEASURE_RATIO));
1890		},
1891
1892		getCorrectQuantity: function(itemData, quantity)
1893		{
1894			var isQuantityFloat = this.isQuantityFloat(itemData),
1895				measureRatio = isQuantityFloat ? parseFloat(itemData.MEASURE_RATIO) : parseInt(itemData.MEASURE_RATIO),
1896				availableQuantity = 0;
1897
1898			quantity = (isQuantityFloat ? parseFloat(quantity) : parseInt(quantity, 10)) || 0;
1899			if (quantity < 0)
1900			{
1901				quantity = 0;
1902			}
1903
1904			if (measureRatio > 0 && quantity < measureRatio)
1905			{
1906				quantity = measureRatio;
1907			}
1908
1909			if (itemData.CHECK_MAX_QUANTITY === 'Y')
1910			{
1911				availableQuantity = isQuantityFloat ? parseFloat(itemData.AVAILABLE_QUANTITY) : parseInt(itemData.AVAILABLE_QUANTITY);
1912				if (availableQuantity > 0 && quantity > availableQuantity)
1913				{
1914					quantity = availableQuantity;
1915				}
1916			}
1917
1918			var reminder = (quantity / measureRatio - ((quantity / measureRatio).toFixed(0))).toFixed(5),
1919				remain;
1920
1921			if (parseFloat(reminder) === 0)
1922			{
1923				return quantity;
1924			}
1925
1926			if (measureRatio !== 0 && measureRatio !== 1)
1927			{
1928				remain = (quantity * this.precisionFactor) % (measureRatio * this.precisionFactor) / this.precisionFactor;
1929
1930				if (measureRatio > 0 && remain > 0)
1931				{
1932					if (
1933						remain >= measureRatio / 2
1934						&& (
1935							availableQuantity === 0
1936							|| (quantity + measureRatio - remain) <= availableQuantity
1937						)
1938					)
1939					{
1940						quantity += (measureRatio - remain);
1941					}
1942					else
1943					{
1944						quantity -= remain;
1945					}
1946				}
1947			}
1948
1949			quantity = isQuantityFloat ? parseFloat(quantity) : parseInt(quantity, 10);
1950
1951			return quantity;
1952		},
1953
1954		setQuantity: function(itemData, quantity)
1955		{
1956			var quantityField = BX(this.ids.quantity + itemData.ID),
1957				currentQuantity;
1958
1959			if (quantityField)
1960			{
1961				quantity = parseFloat(quantity);
1962				currentQuantity = parseFloat(quantityField.getAttribute('data-value'));
1963
1964				quantityField.value = quantity;
1965
1966				if (parseFloat(itemData.QUANTITY) !== parseFloat(quantity))
1967				{
1968					this.animatePriceByQuantity(itemData, quantity);
1969					this.actionPool.changeQuantity(itemData.ID, quantity, currentQuantity);
1970				}
1971			}
1972		},
1973
1974		animatePriceByQuantity: function(itemData, quantity)
1975		{
1976			var priceNode = BX(this.ids.sumPrice + itemData.ID);
1977			if (!BX.type.isDomNode(priceNode))
1978				return;
1979
1980			var quantityMultiplier = quantity / parseFloat(itemData.MEASURE_RATIO);
1981
1982			var startPrice = parseFloat(itemData.SUM_PRICE),
1983				finalPrice = parseFloat(itemData.PRICE) * quantityMultiplier,
1984				isInt = parseInt(startPrice) === parseFloat(startPrice)
1985					&& parseInt(finalPrice) === parseFloat(finalPrice);
1986
1987			if (startPrice !== finalPrice)
1988			{
1989				this.items[itemData.ID].QUANTITY = quantity;
1990				this.items[itemData.ID].SUM_PRICE = finalPrice;
1991
1992				new BX.easing({
1993					duration: this.params.USE_PRICE_ANIMATION === 'Y' ? this.duration.priceAnimation : 1,
1994					start: {price: startPrice},
1995					finish: {price: finalPrice},
1996					transition: BX.easing.makeEaseOut(BX.easing.transitions.quad),
1997					step: BX.delegate(function(state){
1998						if (!isInt)
1999						{
2000							// fix price blinking
2001							state.price = (state.price + (state.price % 1000) / 1000).toFixed(5);
2002						}
2003
2004						priceNode.innerHTML = this.getFormatPrice(state.price, itemData.CURRENCY);
2005					}, this),
2006					complete: BX.delegate(function() {
2007						var node, price;
2008
2009						priceNode.innerHTML = this.getFormatPrice(finalPrice, itemData.CURRENCY);
2010
2011						node = BX(this.ids.sumPriceOld + itemData.ID);
2012						if (BX.type.isDomNode(node))
2013						{
2014							price = parseFloat(itemData.FULL_PRICE) * quantityMultiplier;
2015							node.innerHTML = this.getFormatPrice(price, itemData.CURRENCY);
2016						}
2017
2018						node = BX(this.ids.sumPriceDiff + itemData.ID);
2019						if (BX.type.isDomNode(node))
2020						{
2021							price = parseFloat(itemData.DISCOUNT_PRICE) * quantityMultiplier;
2022							node.innerHTML = this.getFormatPrice(price, itemData.CURRENCY);
2023						}
2024					}, this)
2025				}).animate();
2026			}
2027		},
2028
2029		getItemDataByTarget: function(target)
2030		{
2031			var data = false;
2032			var id;
2033
2034			var itemNode = BX.findParent(target, {attrs: {'data-entity': 'basket-item'}});
2035			if (itemNode)
2036			{
2037				id = itemNode.getAttribute('data-id');
2038				data = this.items[id];
2039			}
2040
2041			return data;
2042		},
2043
2044		bindSkuEvents: function(node, data)
2045		{
2046			if (!node || !data)
2047				return;
2048
2049			var blocks = this.getEntities(node, 'basket-item-sku-block');
2050			var blockEntities, i, l, ii, ll;
2051
2052			for (i = 0, l = blocks.length; i < l; i++)
2053			{
2054				blockEntities = this.getEntities(blocks[i], 'basket-item-sku-field');
2055
2056				for (ii = 0, ll = blockEntities.length; ii < ll; ii++)
2057				{
2058					BX.bind(blockEntities[ii], 'click', BX.proxy(this.changeSku, this));
2059				}
2060			}
2061		},
2062
2063		changeSku: function()
2064		{
2065			var i, l;
2066
2067			var target = BX.proxy_context;
2068
2069			if (BX.hasClass(target, 'selected'))
2070				return;
2071
2072			var itemData = this.getItemDataByTarget(target);
2073			if (itemData)
2074			{
2075				var basketItemNode = BX(this.ids.item + itemData.ID);
2076				if (basketItemNode)
2077				{
2078					var currentSkuListNodes = this.getEntities(target.parentNode, 'basket-item-sku-field');
2079					for (i = 0, l = currentSkuListNodes.length; i < l; i++)
2080					{
2081						if (currentSkuListNodes[i].isEqualNode(target))
2082						{
2083							BX.addClass(currentSkuListNodes[i], 'selected');
2084						}
2085						else
2086						{
2087							BX.removeClass(currentSkuListNodes[i], 'selected');
2088						}
2089					}
2090
2091					this.actionPool.changeSku(
2092						itemData.ID,
2093						this.getSkuPropertyValues(basketItemNode),
2094						this.getInitialSkuPropertyValues(basketItemNode)
2095					);
2096				}
2097			}
2098		},
2099
2100		getSkuPropertyValues: function(basketItemNode)
2101		{
2102			var propertyValues = {};
2103
2104			var propNodes = this.getEntities(basketItemNode, 'basket-item-sku-field', '.selected');
2105			for (var i = 0, l = propNodes.length; i < l; i++)
2106			{
2107				propertyValues[propNodes[i].getAttribute('data-property')] = BX.util.htmlspecialcharsback(propNodes[i].getAttribute('data-value-id'));
2108			}
2109
2110			return propertyValues;
2111		},
2112
2113		getInitialSkuPropertyValues: function(basketItemNode)
2114		{
2115			var propertyValues = {};
2116
2117			var propNodes = this.getEntities(basketItemNode, 'basket-item-sku-field', '[data-initial="true"]');
2118			for (var i = 0, l = propNodes.length; i < l; i++)
2119			{
2120				propertyValues[propNodes[i].getAttribute('data-property')] = BX.util.htmlspecialcharsback(propNodes[i].getAttribute('data-value-id'));
2121			}
2122
2123			return propertyValues;
2124		},
2125
2126		bindImageEvents: function(node, data)
2127		{
2128			if (!node || !data)
2129				return;
2130
2131			var images = node.querySelectorAll('.basket-item-custom-block-photo-item');
2132			for (var i = 0, l = images.length; i < l; i++)
2133			{
2134				BX.bind(images[i], 'click', BX.proxy(this.showPropertyImagePopup, this));
2135			}
2136		},
2137
2138		showPropertyImagePopup: function()
2139		{
2140			var target, propertyCode, imageIndex, item, imageSrc, i;
2141
2142			target = BX.proxy_context;
2143			item = this.getItemDataByTarget(target);
2144
2145			propertyCode = target.getAttribute('data-column-property-code');
2146			imageIndex = target.getAttribute('data-image-index');
2147
2148			if (item && item.COLUMN_LIST)
2149			{
2150				for (i in item.COLUMN_LIST)
2151				{
2152					if (
2153						item.COLUMN_LIST.hasOwnProperty(i)
2154						&& item.COLUMN_LIST[i].CODE === propertyCode
2155						&& item.COLUMN_LIST[i].VALUE[imageIndex]
2156					)
2157					{
2158						imageSrc = item.COLUMN_LIST[i].VALUE[imageIndex].IMAGE_SRC_ORIGINAL;
2159						break;
2160					}
2161				}
2162			}
2163
2164			if (!imageSrc)
2165			{
2166				return;
2167			}
2168
2169			if (this.imagePopup)
2170			{
2171				this.imagePopup.destroy();
2172			}
2173
2174			var imageId = 'bx-soa-image-popup-content';
2175			var that = this;
2176
2177			this.imagePopup = new BX.PopupWindow('bx-soa-image-popup', null, {
2178				lightShadow: true,
2179				offsetTop: 0,
2180				offsetLeft: 0,
2181				closeIcon: {top: '3px', right: '10px'},
2182				autoHide: true,
2183				bindOptions: {position: 'bottom'},
2184				closeByEsc: true,
2185				zIndex: 100,
2186				events: {
2187					onPopupShow: function() {
2188						BX.create('IMG', {
2189							props: {src: imageSrc},
2190							events: {
2191								load: function() {
2192									var content = BX(imageId);
2193									if (content)
2194									{
2195										var windowSize = BX.GetWindowInnerSize(),
2196											ratio = that.isMobile ? 0.5 : 0.9,
2197											contentHeight, contentWidth;
2198
2199										BX.cleanNode(content);
2200										content.appendChild(this);
2201
2202										contentHeight = content.offsetHeight;
2203										contentWidth = content.offsetWidth;
2204
2205										if (contentHeight >
2205 windowSize.innerHeight * ratio)
2206										{
2207											content.style.height = windowSize.innerHeight * ratio + 'px';
2208											content.style.width = contentWidth * (windowSize.innerHeight * ratio / contentHeight) + 'px';
2209											contentHeight = content.offsetHeight;
2210											contentWidth = content.offsetWidth;
2211										}
2212
2213										if (contentWidth > windowSize.innerWidth * ratio)
2214										{
2215											content.style.width = windowSize.innerWidth * ratio + 'px';
2216											content.style.height = contentHeight * (windowSize.innerWidth * ratio / contentWidth) + 'px';
2217										}
2218
2219										content.style.height = content.offsetHeight + 'px';
2220										content.style.width = content.offsetWidth + 'px';
2221
2222										that.imagePopup.adjustPosition();
2223									}
2224								}
2225							}
2226						});
2227					},
2228					onPopupClose: function() {
2229						this.destroy();
2230					}
2231				},
2232				content: BX.create('DIV', {props: {id: imageId}})
2233			});
2234			this.imagePopup.show();
2235		},
2236
2237		bindActionEvents: function(node, data)
2238		{
2239			if (!node || !data)
2240				return;
2241
2242			var entity;
2243
2244			if (BX.util.in_array('DELETE', this.params.COLUMNS_LIST))
2245			{
2246				entity = this.getEntities(node, 'basket-item-delete');
2247				for (var i = 0, l = entity.length; i < l; i++)
2248				{
2249					BX.bind(entity[i], 'click', BX.proxy(this.deleteAction, this));
2250				}
2251				
2252				entity = this.getEntities(node, 'basket-set-item-delete');
2253                for (var i = 0, l = entity.length; i < l; i++)
2254                {
2255                    BX.bind(entity[i], 'click', BX.proxy(this.deleteSetAction, this));
2256                }
2257			}
2258
2259			if (BX.util.in_array('DELAY', this.params.COLUMNS_LIST))
2260			{
2261				entity = this.getEntity(node, 'basket-item-add-delayed');
2262				BX.bind(entity, 'click', BX.proxy(this.addDelayedAction, this));
2263			}
2264
2265			entity = this.getEntity(node, 'basket-item-remove-delayed');
2266			BX.bind(entity, 'click', BX.proxy(this.removeDelayedAction, this));
2267
2268			entity = this.getEntity(node, 'basket-item-merge-sku-link');
2269			BX.bind(entity, 'click', BX.proxy(this.mergeAction, this));
2270
2271			entity = this.getEntity(node, 'basket-item-show-similar-link');
2272			BX.bind(entity, 'click', BX.delegate(function() {this.toggleFilter('similar');}, this));
2273		},
2274
2275		deleteAction: function()
2276		{
2277			var itemData = this.getItemDataByTarget(BX.proxy_context);
2278			if (itemData)
2279			{
2280				this.actionPool.deleteItem(itemData.ID);
2281
2282				this.items[itemData.ID].SHOW_LOADING = true;
2283
2284				if (this.params.SHOW_RESTORE === 'Y' && this.isItemAvailable(itemData.ID))
2285				{
2286					this.items[itemData.ID].SHOW_RESTORE = true;
2287				}
2288
2289				this.redrawBasketItemNode(itemData.ID);
2290			}
2291		},
2292		
2293		deleteSetAction: function()
2294        {
2295            var itemData = this.getItemDataByTarget(BX.proxy_context);
2296            if (itemData)
2297            {
2298                this.items[itemData.ID].SHOW_LOADING = true;
2299                
2300                BX.ajax({
2301                    method: 'POST',
2302                    dataType: 'html',
2303                    url: BX.message('SITE_DIR') + 'ajax/basket_product_list.php',
2304                    data: {
2305                        deleteSet: itemData.ID 
2306                    },
2307                    onsuccess: BX.delegate(function(result)
2308                    {
2309                        if(result.length)
2310                        {
2311                            if (this.params.SHOW_RESTORE === 'Y' && this.isItemAvailable(itemData.ID))
2312                            {
2313                                this.items[itemData.ID].SHOW_RESTORE = true;
2314                            }
2315                            
2316                            this.redrawBasketItemNode(itemData.ID);
2317                        }
2318                    }, this)
2319                });
2320            }
2321        },
2322
2323		addDelayedAction: function()
2324		{
2325			var itemData = this.getItemDataByTarget(BX.proxy_context);
2326			if (itemData)
2327			{
2328				this.actionPool.addDelayed(itemData.ID);
2329
2330				this.items[itemData.ID].SHOW_LOADING = true;
2331				this.redrawBasketItemNode(itemData.ID);
2332			}
2333		},
2334
2335		removeDelayedAction: function()
2336		{
2337			var itemData = this.getItemDataByTarget(BX.proxy_context);
2338			if (itemData)
2339			{
2340				this.actionPool.removeDelayed(itemData.ID);
2341
2342				this.items[itemData.ID].SHOW_LOADING = true;
2343				this.redrawBasketItemNode(itemData.ID);
2344			}
2345		},
2346
2347		mergeAction: function()
2348		{
2349			var itemData = this.getItemDataByTarget(BX.proxy_context);
2350			if (itemData)
2351			{
2352				this.actionPool.mergeSku(itemData.ID);
2353			}
2354		},
2355
2356		bindRestoreAction: function(node, itemData)
2357		{
2358			if (!node || !itemData || this.params.SHOW_RESTORE !== 'Y')
2359				return;
2360
2361			BX.bind(
2362				this.getEntity(node, 'basket-item-restore-button'),
2363				'click',
2364				BX.delegate(function() {
2365					this.actionPool.restoreItem(itemData.ID, {
2366						PRODUCT_ID: itemData.PRODUCT_ID,
2367						QUANTITY: itemData.QUANTITY,
2368						PROPS: itemData.PROPS_ALL,
2369						SORT: itemData.SORT,
2370						MODULE: itemData.MODULE,
2371						PRODUCT_PROVIDER_CLASS: itemData.PRODUCT_PROVIDER_CLASS
2372					});
2373
2374					this.items[itemData.ID].SHOW_RESTORE = false;
2375					this.items[itemData.ID].SHOW_LOADING = true;
2376					this.redrawBasketItemNode(itemData.ID);
2377				}, this)
2378			);
2379			BX.bind(
2380				this.getEntity(node, 'basket-item-close-restore-button'),
2381				'click',
2382				BX.delegate(function() {
2383					this.deleteBasketItem(itemData.ID, false, true);
2384				}, this)
2385			);
2386		},
2387
2388		bindItemWarningEvents: function(node, data)
2389		{
2390			if (!node || !data)
2391				return;
2392
2393			BX.bind(
2394				this.getEntity(BX(this.ids.item + data.ID), 'basket-item-warning-close'),
2395				'click',
2396				BX.proxy(this.closeItemWarnings, this)
2397			);
2398		},
2399
2400		closeItemWarnings: function()
2401		{
2402			var target = BX.proxy_context;
2403
2404			if (BX.type.isDomNode(target))
2405			{
2406				var itemData = this.getItemDataByTarget(target);
2407
2408				this.items[itemData.ID].WARNINGS = [];
2409				this.warningItems.splice(BX.util.array_search(itemData.ID, this.warningItems), 1);
2410
2411				this.redrawBasketItemNode(itemData.ID);
2412				this.editWarnings();
2413			}
2414		},
2415
2416		renderBasketItem: function(template, data)
2417		{
2418			var clonedData = BX.clone(data);
2419			console.log(clonedData);
2420
2421			if (BX.type.isPlainObject(clonedData))
2422			{
2423				clonedData.USE_FILTER = this.useItemsFilter
2424					&& !this.filter.currentFilter.similarHash.length;
2425			}
2426
2427			return Mustache.render(template, clonedData);
2428		},
2429
2430		render: function(template, data)
2431		{
2432			return Mustache.render(template, data);
2433		},
2434
2435		checkAnalytics: function(data)
2436		{
2437			if (!data || !data.basket)
2438				return;
2439
2440			var itemId, itemsDiff = {};
2441
2442			for (var i in data.basket)
2443			{
2444				if (data.basket.hasOwnProperty(i))
2445				{
2446					if (i.indexOf('QUANTITY_') >= 0)
2447					{
2448						itemId = i.substr(9);
2449
2450						if (this.items[itemId])
2451						{
2452							itemsDiff[itemId] = parseFloat(data.basket[i]) - parseFloat(BX(this.ids.quantity + itemId).getAttribute('data-value'));
2453						}
2454					}
2455					else if (i.indexOf('DELETE_') >= 0)
2456					{
2457						itemId = i.substr(7);
2458
2459						if (this.items[itemId])
2460						{
2461							itemsDiff[itemId] = -parseFloat(this.items[itemId].QUANTITY);
2462						}
2463					}
2464					else if (i.indexOf('RESTORE_') >= 0)
2465					{
2466						itemId = i.substr(8);
2467
2468						if (this.items[itemId])
2469						{
2470							itemsDiff[itemId] = parseFloat(this.items[itemId].QUANTITY);
2471						}
2472					}
2473				}
2474			}
2475
2476			this.setAnalyticsDataLayer(itemsDiff);
2477		},
2478
2479		setAnalyticsDataLayer: function(itemsDiff)
2480		{
2481			if (!itemsDiff || Object.keys(itemsDiff).length === 0)
2482				return;
2483
2484			window[this.params.DATA_LAYER_NAME] = window[this.params.DATA_LAYER_NAME] || [];
2485
2486			var plus = [], minus = [];
2487
2488			for (var itemId in itemsDiff)
2489			{
2490				if (itemsDiff.hasOwnProperty(itemId))
2491				{
2492					if (itemsDiff[itemId] > 0)
2493					{
2494						plus.push(this.getItemAnalyticsInfo(itemId, itemsDiff[itemId]));
2495					}
2496					else if (itemsDiff[itemId] < 0)
2497					{
2498						minus.push(this.getItemAnalyticsInfo(itemId, itemsDiff[itemId]));
2499					}
2500				}
2501			}
2502
2503			if (plus.length)
2504			{
2505				window[this.params.DATA_LAYER_NAME].push({
2506					event: 'addToCart',
2507					ecommerce: {
2508						currencyCode: this.items[itemId].CURRENCY || '',
2509						add: {
2510							products: plus
2511						}
2512					}
2513				});
2514			}
2515
2516			if (minus.length)
2517			{
2518				window[this.params.DATA_LAYER_NAME].push({
2519					event: 'removeFromCart',
2520					ecommerce: {
2521						currencyCode: this.items[itemId].CURRENCY || '',
2522						remove: {
2523							products: minus
2524						}
2525					}
2526				});
2527			}
2528		},
2529
2530		getItemAnalyticsInfo: function(itemId, diff)
2531		{
2532			if (!this.items[itemId])
2533				return {};
2534
2535			var brand = (this.items[itemId].BRAND || '').split(',  ').join('/');
2536			var variants = [];
2537
2538			var selectedSku = this.getEntities(BX(this.ids.item + itemId), 'basket-item-sku-field', '.selected');
2539			for (var i = 0, l = selectedSku.length; i < l; i++)
2540			{
2541				variants.push(selectedSku[i].getAttribute('data-sku-name'));
2542			}
2543
2544			return {
2545				'name': this.items[itemId].NAME || '',
2546				'id': this.items[itemId].PRODUCT_ID || '',
2547				'price': this.items[itemId].PRICE || 0,
2548				'brand': brand,
2549				'variant': variants.join('/'),
2550				'quantity': Math.abs(diff)
2551			};
2552		}
2553	};
2554})();

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.