PageSourceSearch

https://copierts.ru/bitrix/components/bitrix/sale.basket.basket/te…/.default/js/component.js?161249295661750

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

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.