PageSourceSearch

https://sib-lada.ru/bitrix/templates/universe_s2/components/bitrix…/template.1/js/component.js

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

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.