PageSourceSearch

https://zagustitelvolos.com/bitrix/templates/tpl-zagustitelvolos/c…/js/component.js?178373147661919

js zagustitelvolos.com collected 2026-09-30 08:33:14 UTC 61,919 bytes, 2,436 lines download raw bytes

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

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.