PageSourceSearch

https://chuvashcable.ru/local/templates/chuvkabel/components/bitri…/.default/js/component.js?165266831762070

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

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.