PageSourceSearch

https://plintus-hall.ru/bitrix/templates/aspro_next/components/bit…/.default/js/component.js

js plintus-hall.ru collected 2026-10-04 04:51:00 UTC 62,235 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		isMobile: BX.browser.IsMobile(),
8		isTouch: BX.hasClass(document.documentElement, 'bx-touch'),
9		lastAction: 'initialLoad',
10		maxItemsShowCount: 30,
11
12		precisionFactor: Math.pow(10, 6),
13
14		quantityDelay: null,
15		quantityTimer: null,
16
17		stickyHeaderOffset: 0,
18
19		duration: {
20			priceAnimation: 300,
21			filterTimer: 300
22		},
23
24		imagePopup: null,
25		loadingScreen: null,
26
27		templates: {},
28		nodes: {},
29
30		/** Object of all basket items (itemId => itemArray) */
31		items: {},
32
33		/** Array of all basket items to show sorted by field SORT */
34		sortedItems: [],
35
36		/** Array of basket items showed on screen */
37		shownItems: [],
38
39		/** Array of basket items changed since last request */
40		changedItems: [],
41
42		/** Array of basket items postponed by pool to edit */
43		postponedItems: [],
44
45		/** Array of basket items with warnings */
46		warningItems: [],
47
48		ids: {
49			item: 'basket-item-',
50			quantity: 'basket-item-quantity-',
51			price: 'basket-item-price-',
52			sumPrice: 'basket-item-sum-price-',
53			sumPriceOld: 'basket-item-sum-price-old-',
54			sumPriceDiff: 'basket-item-sum-price-difference-',
55			itemHeightAligner: 'basket-item-height-aligner-',
56			total: 'basket-total-price',
57			basketRoot: 'basket-root',
58			itemListWrapper: 'basket-items-list-wrapper',
59			itemListContainer: 'basket-items-list-container',
60			itemList: 'basket-item-list',
61			itemListTable: 'basket-item-table',
62			itemListEmptyResult: 'basket-item-list-empty-result',
63			itemListOverlay: 'basket-items-list-overlay',
64			warning: 'basket-warning'
65		},
66
67		init: function(parameters)
68		{
69			this.params = parameters.params || {};
70			this.template = parameters.template || '';
71			this.signedParamsString = parameters.signedParamsString || '';
72			this.siteId = parameters.siteId || '';
73			this.ajaxUrl = this.params.AJAX_PATH || '';
74			this.templateFolder = parameters.templateFolder || '';
75
76			this.useDynamicScroll = this.params.USE_DYNAMIC_SCROLL === 'Y';
77			this.useItemsFilter = this.params.SHOW_FILTER === 'Y' && !this.isMobile;
78
79			this.initializeFilter();
80			this.applyBasketResult(parameters.result);
81			this.initializeActionPool();
82
83			if (this.useItemsFilter)
84			{
85				this.checkHeaderDisplay();
86				this.bindHeaderEvents();
87			}
88
89			this.initializeBasketItems();
90			this.editTotal();
91			this.editWarnings();
92
93			this.adjustBasketWrapperHeight();
94			this.getCacheNode(this.ids.basketRoot).style.opacity = 1;
95
96			this.bindInitialEvents();
97		},
98
99		getTemplate: function(templateName)
100		{
101			if (!this.templates.hasOwnProperty(templateName))
102			{
103				var template = BX(templateName);
104				this.templates[templateName] = BX.type.isDomNode(template) ? template.innerHTML : '';
105			}
106
107			return this.templates[templateName];
108		},
109
110		getCacheNode: function(id)
111		{
112			if (!this.nodes.hasOwnProperty(id))
113			{
114				this.nodes[id] = BX(id);
115			}
116
117			return this.nodes[id];
118		},
119
120		getEntity: function(parent, entity, additionalFilter)
121		{
122			if (!parent || !entity)
123				return null;
124
125			additionalFilter = additionalFilter || '';
126
127			return parent.querySelector(additionalFilter + '[data-entity="' + entity + '"]');
128		},
129
130		getEntities: function(parent, entity, additionalFilter)
131		{
132			if (!parent || !entity)
133				return {length: 0};
134
135			additionalFilter = additionalFilter || '';
136
137			return parent.querySelectorAll(additionalFilter + '[data-entity="' + entity + '"]');
138		},
139
140		bindInitialEvents: function()
141		{
142			this.bindWarningEvents();
143
144			BX.bind(window, 'scroll', BX.proxy(this.checkStickyHeaders, this));
145			BX.bind(window, 'scroll', BX.proxy(this.lazyLoad, this));
146
147			BX.bind(window, 'resize', BX.throttle(this.checkStickyHeaders, 20, this));
148			BX.bind(window, 'resize', BX.throttle(this.adjustBasketWrapperHeight, 20, this));
149		},
150
151		bindWarningEvents: function()
152		{
153			var showItemsNode = this.getEntity(BX(this.ids.warning), 'basket-items-warning-count');
154
155			if (BX.type.isDomNode(showItemsNode))
156			{
157				showItemsNode.style.display = '';
158				BX.bind(showItemsNode, 'click', BX.delegate(function() {this.toggleFilter('warning');}, this));
159			}
160
161			BX.bind(
162				this.getEntity(BX(this.ids.warning), 'basket-items-warning-notification-close'),
163				'click',
164				BX.proxy(this.removeAllWarnings, this)
165			);
166		},
167
168		toggleFilter: function(event)
169		{
170			var target = BX.type.isNotEmptyString(event) ?
171				this.getEntity(
172					this.getCacheNode(this.ids.itemListWrapper),
173					'basket-items-count',
174					'[data-filter="' + event + '"]'
175				)
176				: BX.getEventTarget(event);
177
178			if (!BX.type.isDomNode(target) || BX.hasClass(target, 'active'))
179				return;
180
181			var entityName = target.getAttribute('data-filter');
182			var entities = target.parentNode.querySelectorAll('[data-filter]');
183
184			for (var i = 0; i < entities.length; i++)
185			{
186				if (entities[i].getAttribute('data-filter') === entityName)
187				{
188					BX.addClass(entities[i], 'active');
189				}
190				else if (BX.hasClass(entities[i], 'active'))
191				{
192					BX.removeClass(entities[i], 'active');
193				}
194			}
195
196			this.filter.showFilterByName(entityName);
197		},
198
199		scrollToFirstItem: function()
200		{
201			var headerNode = this.getEntity(this.getCacheNode(this.ids.itemListWrapper), 'basket-items-list-header');
202
203			if (BX.type.isDomNode(headerNode))
204			{
205				var itemListTopPosition = BX.pos(this.getCacheNode(this.ids.itemListContainer)).top;
206				var headerBottomPosition = BX.pos(headerNode).bottom;
207
208				if (itemListTopPosition < headerBottomPosition)
209				{
210					window.scrollTo(0, itemListTopPosition - this.stickyHeaderOffset);
211				}
212			}
213		},
214
215		showItemsOverlay: function()
216		{
217			var overlay = this.getCacheNode(this.ids.itemListOverlay);
218
219			if (BX.type.isDomNode(overlay))
220			{
221				overlay.style.display = '';
222			}
223		},
224
225		hideItemsOverlay: function()
226		{
227			var overlay = this.getCacheNode(this.ids.itemListOverlay);
228
229			if (BX.type.isDomNode(overlay))
230			{
231				overlay.style.display = 'none';
232			}
233		},
234
235		checkHeaderDisplay: function()
236		{
237			var header = this.getCacheNode(this.ids.itemListWrapper);
238
239			if (BX.type.isDomNode(header))
240			{
241				BX.removeClass(header, 'basket-items-list-wrapper-light');
242			}
243		},
244
245		bindHeaderEvents: function()
246		{
247			var entities = this.getEntities(this.getCacheNode(this.ids.itemListWrapper), 'basket-items-count');
248
249			for (var i = 0; i < entities.length; i++)
250			{
251				BX.bind(entities[i], 'click', BX.proxy(this.toggleFilter, this));
252			}
253		},
254
255		checkStickyHeaders: function()
256		{
257			if (this.isMobile)
258				return;
259
260			var node, position;
261			var border = 2, offset = 0;
262			var scrollTop = this.getDocumentScrollTop();
263			var basketPosition = BX.pos(this.getCacheNode(this.ids.basketRoot));
264			var basketScrolledToEnd = scrollTop + 200 >= basketPosition.bottom;
265
266			if (BX.util.in_array('top', this.params.TOTAL_BLOCK_DISPLAY))
267			{
268				var totalBlockNode = this.getEntity(this.getCacheNode(this.ids.basketRoot), 'basket-total-block');
269				if (BX.type.isDomNode(totalBlockNode))
270				{
271					node = this.getEntity(totalBlockNode, 'basket-checkout-aligner');
272					if (BX.type.isDomNode(node))
273					{
274						position = BX.pos(totalBlockNode);
275
276						if (scrollTop >= position.top)
277						{
278							offset += node.clientHeight;
279
280							if (!BX.hasClass(node, 'basket-checkout-container-fixed'))
281							{
282								totalBlockNode.style.height = position.height + 'px';
283
284								node.style.width = node.clientWidth + border + 'px';
285								BX.addClass(node, 'basket-checkout-container-fixed');
286							}
287						}
288						else if (BX.hasClass(node, 'basket-checkout-container-fixed'))
289						{
290							totalBlockNode.style.height = '';
291
292							node.style.width = '';
293							BX.removeClass(node, 'basket-checkout-container-fixed');
294						}
295
296						if (basketScrolledToEnd)
297						{
298							if (!BX.hasClass(node, 'basket-checkout-container-fixed-hide'))
299							{
300								BX.addClass(node, 'basket-checkout-container-fixed-hide');
301							}
302						}
303						else if (BX.hasClass(node, 'basket-checkout-container-fixed-hide'))
304						{
305							BX.removeClass(node, 'basket-checkout-container-fixed-hide');
306						}
307					}
308				}
309			}
310
311			if (this.useItemsFilter)
312			{
313				var itemWrapperNode = this.getCacheNode(this.ids.itemListWrapper);
314
315				node = this.getEntity(itemWrapperNode, 'basket-items-list-header');
316				if (BX.type.isDomNode(node))
317				{
318					position = BX.pos(itemWrapperNode);
319
320					if ((scrollTop + offset >= position.top) && !basketScrolledToEnd)
321					{
322						if (!BX.hasClass(node, 'basket-items-list-header-fixed'))
323						{
324							node.style.width = node.clientWidth + border + 'px';
325
326							itemWrapperNode.style.paddingTop = node.clientHeight + 'px';
327
328							BX.addClass(node, 'basket-items-list-header-fixed');
329						}
330
331						if (offset)
332						{
333							node.style.top = offset + 'px';
334						}
335
336						offset += node.clientHeight;
337					}
338					else if (BX.hasClass(node, 'basket-items-list-header-fixed'))
339					{
340						itemWrapperNode.style.paddingTop = '';
341
342						node.style.width = '';
343						node.style.top = '';
344
345						BX.removeClass(node, 'basket-items-list-header-fixed');
346					}
347				}
348			}
349
350			var offsetChanged = this.stickyHeaderOffset === offset;
351
352			this.stickyHeaderOffset = offset;
353
354			if (offsetChanged && !basketScrolledToEnd)
355			{
356				this.adjustBasketWrapperHeight();
357			}
358		},
359
360		getDocumentScrollTop: function()
361		{
362			return window.scrollY
363				|| window.pageYOffset
364				|| document.body.scrollTop + (document.documentElement && document.documentElement.scrollTop || 0);
365		},
366
367		lazyLoad: function()
368		{
369			var itemsNodePosition = BX.pos(this.getCacheNode(this.ids.itemListContainer));
370
371			if (this.getDocumentScrollTop() + window.innerHeight >= itemsNodePosition.bottom - 400)
372			{
373				var itemIds = this.getItemsAfter();
374				if (itemIds.length)
375				{
376					this.editBasketItems(itemIds);
377				}
378			}
379		},
380
381		fireCustomEvents: function()
382		{
383			if (this.result.EVENT_ONCHANGE_ON_START === 'Y')
384			{
385				BX.onCustomEvent('OnBasketChange');
386			}
387		},
388
389		adjustBasketWrapperHeight: function()
390		{
391			var itemListContainer = this.getCacheNode(this.ids.itemListContainer),
392				itemList = this.getCacheNode(this.ids.itemList);
393
394			if (BX.type.isDomNode(itemListContainer) && BX.type.isDomNode(itemList))
395			{
396				if (itemListContainer.clientHeight + this.stickyHeaderOffset > window.innerHeight)
397				{
398					itemListContainer.style.minHeight = 'calc(100vh - 15px - ' + this.stickyHeaderOffset + 'px)';
399					itemList.style.minHeight = 'calc(100vh - 15px - ' + this.stickyHeaderOffset + 'px)';
400				}
401				else
402				{
403					itemListContainer.style.minHeight = itemListContainer.clientHeight + 'px';
404					itemList.style.minHeight = itemListContainer.clientHeight + 'px';
405				}
406			}
407		},
408
409		editTotal: function()
410		{
411			this.fillTotalBlocks();
412			this.showItemsCount();
413			this.showWarningItemsCount();
414			this.showNotAvailableItemsCount();
415			this.showDelayedItemsCount();
416		},
417
418		fillTotalBlocks: function()
419		{
420			var totalNodes = this.getEntities(this.getCacheNode(this.ids.basketRoot), 'basket-total-block');
421
422			if (totalNodes && totalNodes.length)
423			{
424				var totalTemplate = this.getTemplate('basket-total-template');
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.adjustBasketWrapperHeight();
681					this.applyPriceAnimation();
682					this.editWarnings();
683
684					this.actionPool.switchTimer();
685
686					if (this.isBasketIntegrated() && this.isBasketChanged())
687					{
688						BX.Sale.OrderAjaxComponent.sendRequest();
689					}
690				}, this),
691				onfailure: BX.delegate(function() {
692					this.actionPool.doProcessing(false);
693				}, this)
694			});
695		},
696
697		isBasketIntegrated: function()
698		{
699			return this.params.BASKET_WITH_ORDER_INTEGRATION === 'Y';
700		},
701
702		isBasketChanged: function()
703		{
704			return this.changedItems.length;
705		},
706
707		addPriceAnimationData: function(nodeId, start, finish, currency)
708		{
709			if (!BX.type.isPlainObject(this.priceAnimationData))
710			{
711				this.clearPriceAnimationData();
712			}
713
714			this.priceAnimationData.start[nodeId] = parseFloat(start);
715			this.priceAnimationData.finish[nodeId] = parseFloat(finish);
716			this.priceAnimationData.currency[nodeId] = currency;
717			this.priceAnimationData.int[nodeId] = (parseFloat(start) === parseInt(start)) && (parseFloat(finish) === parseInt(finish));
718		},
719
720		clearPriceAnimationData: function()
721		{
722			this.priceAnimationData = {
723				start: {},
724				finish: {},
725				currency: {},
726				int: {}
727			};
728		},
729
730		applyBasketResult: function(result)
731		{
732			this.changedItems = [];
733			this.clearPriceAnimationData();
734
735			if (!BX.type.isPlainObject(result))
736			{
737				return;
738			}
739
740			if (result.BASKET_ITEM_RENDER_DATA)
741			{
742				var i, newData;
743
744				for (i in result.BASKET_ITEM_RENDER_DATA)
745				{
746					if (result.BASKET_ITEM_RENDER_DATA.hasOwnProperty(i))
747					{
748						newData = result.BASKET_ITEM_RENDER_DATA[i];
749						newData.WARNINGS = this.checkBasketItemWarnings(newData, result.WARNING_MESSAGE_WITH_CODE);
750
751						if (this.items[newData.ID])
752						{
753							if (JSON.stringify(this.items[newData.ID]) === JSON.stringify(newData))
754							{
755								continue;
756							}
757						}
758						else
759						{
760							this.addSortedItem(newData.ID, true);
761						}
762
763						this.changedItems.push(newData.ID);
764
765						newData = this.checkBasketItemsAnimation(newData);
766						this.items[newData.ID] = newData;
767					}
768				}
769
770				this.changedItems = BX.util.array_unique(this.changedItems.concat(this.getChangedSimilarOffers()));
771
772				if (this.isBasketChanged())
773				{
774					this.sortSortedItems(true);
775				}
776			}
777
778			if (result.TOTAL_RENDER_DATA)
779			{
780				result.TOTAL_RENDER_DATA = this.checkTotalAnimation(result.TOTAL_RENDER_DATA);
781			}
782
783			this.result = result;
784		},
785
786		itemSortFunction: function(a, b)
787		{
788			if (!this.items.hasOwnProperty(a) || !this.items.hasOwnProperty(b))
789			{
790				return 0;
791			}
792
793			return parseFloat(this.items[a].SORT) - parseFloat(this.items[b].SORT);
794		},
795
796		getChangedSimilarOffers: function()
797		{
798			var changedSimilarOffers = [];
799
800			var otherSimilarItemsQuantity, totalSimilarItemsQuantity;
801			var hashMap = this.getHashMap();
802
803			for (var hash in hashMap)
804			{
805				if (hashMap.hasOwnProperty(hash))
806				{
807					if (hashMap[hash].length > 1)
808					{
809						for (var i = 0; i < hashMap[hash].length; i++)
810						{
811							otherSimilarItemsQuantity = 0;
812							totalSimilarItemsQuantity = 0;
813
814							for (var k = 0; k < hashMap[hash].length; k ++)
815							{
816								if (hashMap[hash][k] != hashMap[hash][i])
817								{
818									otherSimilarItemsQuantity += parseFloat(this.items[hashMap[hash][k]].QUANTITY);
819								}
820
821								totalSimilarItemsQuantity += parseFloat(this.items[hashMap[hash][k]].QUANTITY);
822							}
823
824							if (
825								!this.items[hashMap[hash][i]].HAS_SIMILAR_ITEMS
826								|| this.items[hashMap[hash][i]].SIMILAR_ITEMS_QUANTITY != otherSimilarItemsQuantity
827								|| this.items[hashMap[hash][i]].TOTAL_SIMILAR_ITEMS_QUANTITY != totalSimilarItemsQuantity
828							)
829							{
830								changedSimilarOffers.push(hashMap[hash][i]);
831
832								this.items[hashMap[hash][i]].HAS_SIMILAR_ITEMS = true;
833								this.items[hashMap[hash][i]].SIMILAR_ITEMS_QUANTITY = otherSimilarItemsQuantity;
834								this.items[hashMap[hash][i]].TOTAL_SIMILAR_ITEMS_QUANTITY = totalSimilarItemsQuantity;
835
836								this.items[hashMap[hash][i]].ALL_AVAILABLE_QUANTITY = this.items[hashMap[hash][i]].AVAILABLE_QUANTITY;
837								this.items[hashMap[hash][i]].AVAILABLE_QUANTITY = this.items[hashMap[hash][i]].ALL_AVAILABLE_QUANTITY - otherSimilarItemsQuantity;
838							}
839						}
840					}
841					else if (hashMap[hash][0] && this.items[hashMap[hash][0]].HAS_SIMILAR_ITEMS)
842					{
843						changedSimilarOffers.push(hashMap[hash][0]);
844
845						delete this.items[hashMap[hash][0]].HAS_SIMILAR_ITEMS;
846						delete this.items[hashMap[hash][0]].SIMILAR_ITEMS_QUANTITY;
847						delete this.items[hashMap[hash][0]].TOTAL_SIMILAR_ITEMS_QUANTITY;
848
849						this.items[hashMap[hash][0]].AVAILABLE_QUANTITY = this.items[hashMap[hash][0]].ALL_AVAILABLE_QUANTITY;
850						delete this.items[hashMap[hash][0]].ALL_AVAILABLE_QUANTITY;
851					}
852				}
853			}
854
855			return changedSimilarOffers;
856		},
857
858		getHashMap: function()
859		{
860			var hashMap = {};
861
862			for (var id in this.items)
863			{
864				if (this.items.hasOwnProperty(id) && this.isItemAvailable(id))
865				{
866					if (!hashMap.hasOwnProperty(this.items[id].HASH))
867					{
868						hashMap[this.items[id].HASH] = [];
869					}
870
871					hashMap[this.items[id].HASH].push(id);
872				}
873			}
874
875			return hashMap;
876		},
877
878		isItemAvailable: function(itemId)
879		{
880			var sortedItems = this.filter.isActive() ? this.filter.realSortedItems : this.sortedItems;
881
882			return !this.items[itemId].NOT_AVAILABLE
883				&& !this.items[itemId].SHOW_RESTORE
884				&& BX.util.in_array(itemId, sortedItems);
885		},
886
887		checkTotalAnimation: function(totalData)
888		{
889			if (this.result && this.result.TOTAL_RENDER_DATA && parseFloat(this.result.TOTAL_RENDER_DATA.PRICE) > parseFloat(totalData.PRICE))
890			{
891				totalData.PRICE_NEW = totalData.PRICE;
892				totalData.PRICE = this.result.TOTAL_RENDER_DATA.PRICE;
893
894				totalData.PRICE_FORMATED_NEW = totalData.PRICE_FORMATED;
895				totalData.PRICE_FORMATED = this.result.TOTAL_RENDER_DATA.PRICE_FORMATED;
896
897				this.addPriceAnimationData(this.ids.total, totalData.PRICE, totalData.PRICE_NEW, totalData.CURRENCY);
898			}
899
900			return totalData;
901		},
902
903		checkBasketItemsAnimation: function(itemData)
904		{
905			var itemId = itemData.ID;
906
907			if (this.items[itemId])
908			{
909				var quantityNode = BX(this.ids.quantity + itemId);
910				if (
911					BX.type.isDomNode(quantityNode)
912					&& !this.actionPool.isItemInPool(itemId)
913					&& parseFloat(quantityNode.value) !== parseFloat(itemData.QUANTITY)
914				)
915				{
916					itemData.QUANTITY_ANIMATION = true;
917					this.actionPool.clearLastActualQuantityPool(itemId);
918				}
919
920				if (parseFloat(this.items[itemId].PRICE) > parseFloat(itemData.PRICE))
921				{
922					itemData.PRICE_NEW = itemData.PRICE;
923					itemData.PRICE = this.items[itemId].PRICE;
924
925					itemData.PRICE_FORMATED_NEW = itemData.PRICE_FORMATED;
926					itemData.PRICE_FORMATED = this.items[itemId].PRICE_FORMATED;
927
928					this.addPriceAnimationData(this.ids.price + itemId, itemData.PRICE, itemData.PRICE_NEW, itemData.CURRENCY);
929				}
930
931				if (
932					BX.util.in_array('SUM', this.params.COLUMNS_LIST)
933					&& parseFloat(this.items[itemId].SUM_PRICE) > parseFloat(itemData.SUM_PRICE)
934					&& parseFloat(this.items[itemId].QUANTITY) === parseFloat(itemData.QUANTITY)
935				)
936				{
937					itemData.SUM_PRICE_NEW = itemData.SUM_PRICE;
938					itemData.SUM_PRICE = this.items[itemId].SUM_PRICE;
939
940					itemData.SUM_PRICE_FORMATED_NEW = itemData.SUM_PRICE_FORMATED;
941					itemData.SUM_PRICE_FORMATED = this.items[itemId].SUM_PRICE_FORMATED;
942
943					this.addPriceAnimationData(this.ids.sumPrice + itemId, itemData.SUM_PRICE, itemData.SUM_PRICE_NEW, itemData.CURRENCY);
944				}
945			}
946
947			return itemData;
948		},
949
950		getData: function(data)
951		{
952			data = data || {};
953
954			data[this.params.ACTION_VARIABLE] = this.lastAction;
955			data.via_ajax = 'Y';
956			data.site_id = this.siteId;
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].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.