PageSourceSearch

https://ek-territory.ru/bitrix/js/ui/notification/ui.notification.balloon.js?176986456618234

js ek-territory.ru collected 2026-09-24 12:26:50 UTC 18,234 bytes, 998 lines download raw bytes

1(function() {
2
3	"use strict";
4
5	BX.namespace("BX.UI.Notification");
6
7	/**
8	 *
9	 * @enum {number}
10	 */
11	var State = {
12		INIT: 0,
13		OPENING: 1,
14		OPEN: 2,
15		CLOSING: 3,
16		CLOSED: 4,
17		PAUSED: 5,
18		QUEUED: 6
19	};
20
21	BX.UI.Notification.State = State;
22
23	/**
24	 * @typedef {object} BX.UI.Notification.BalloonOptions
25	 * @property {BX.UI.Notification.Stack} stack
26	 * @property {string|Element} [content]
27	 * @property {boolean} [autoHide=true]
28	 * @property {boolean} [showOnTopWindow=false]
29	 * @property {number} [autoHideDelay=8000]
30	 * @property {boolean} [closeButton=true]
31	 * @property {string} [category]
32	 * @property {string} [id]
33	 * @property {BX.UI.Notification.Action[]} [actions]
34	 * @property {function} [render]
35	 * @property {number} [width=400]
36	 * @property {object} [data]
37	 * @property {?object.<string, function>} [events]
38	 * @property {boolean} [useAirDesign=false]
39	 */
40
41	/**
42	 *
43	 * @param {BX.UI.Notification.BalloonOptions} options
44	 * @constructor
45	 */
46	BX.UI.Notification.Balloon = function(options)
47	{
48		options = BX.type.isPlainObject(options) ? options : Object.create(null);
49
50		if (!(options.stack instanceof BX.UI.Notification.Stack))
51		{
52			throw new Error("BX.UI.Notification.Balloon: 'stack' parameter is required.");
53		}
54
55		this.id = BX.type.isNotEmptyString(options.id) ? options.id : BX.util.getRandomString(8).toLowerCase();
56		this.stack = options.stack;
57		this.state = State.INIT;
58
59		this.showOnTopWindow = options.showOnTopWindow === true;
60		this.container = null;
61		this.content = null;
62		this.actions = [];
63		this.animationClassName = "ui-notification-balloon-animate";
64		this.customRender = null;
65		this.category = null;
66
67		this.autoHide = true;
68		this.autoHideDelay = 8000;
69		this.autoHideTimeout = null;
70
71		this.useAirDesign = options.useAirDesign === true;
72
73		this.data = {};
74		this.width = 400;
75		if (this.useAirDesign === true)
76		{
77			this.width = 339;
78		}
79
80		this.closeButton = null;
81		this.closeButtonVisibility = true;
82
83		if (BX.type.isPlainObject(options.events))
84		{
85			for (var eventName in options.events)
86			{
87				this.addEvent(eventName, options.events[eventName]);
88			}
89		}
90
91		this.setOptions(options);
92	};
93
94	BX.UI.Notification.Balloon.prototype =
95		{
96			/**
97			 * @public
98			 */
99			show: function()
100			{
101				if (this.getState() === State.OPENING)
102				{
103					return;
104				}
105
106				if (this.getState() === State.OPEN)
107				{
108					this.activateAutoHide();
109					return;
110				}
111
112				var firstLaunch = false;
113				if (!this.getContainer().parentNode)
114				{
115					firstLaunch = true;
116					if (this.showOnTopWindow)
117					{
118						window.top.document.body.appendChild(this.getContainer());
119					}
120					else
121					{
122						document.body.appendChild(this.getContainer());
123					}
124					BX.ZIndexManager.register(this.getContainer(), { alwaysOnTop: true });
125
126					this.getStack().add(this);
127					if (this.getState() === State.QUEUED)
128					{
129						return;
130					}
131				}
132
133				var paused = this.getState() === State.PAUSED;
134				this.setState(State.OPENING);
135				this.adjustPosition();
136				BX.ZIndexManager.bringToFront(this.getContainer());
137
138				this.animateIn(function() {
139
140					if (this.getState() !== State.OPENING)
141					{
142						return;
143					}
144
145					this.setState(State.OPEN);
146
147					if (firstLaunch)
148					{
149						this.fireEvent("onOpen");
150					}
151
152					if (!paused)
153					{
154						this.activateAutoHide();
155					}
156
157				}.bind(this));
158
159			},
160
161			/**
162			 *
163			 * @param {BX.UI.Notification.BalloonOptions} options
164			 */
165			setOptions: function(options)
166			{
167				if (!BX.type.isPlainObject(options))
168				{
169					return;
170				}
171
172				this.setContent(options.content);
173				this.setWidth(options.width);
174				this.setData(options.data);
175				this.setCloseButtonVisibility(options.closeButton);
176				this.setActions(options.actions);
177				this.setCategory(options.category);
178				this.setAutoHide(options.autoHide);
179				this.setCustomRender(options.render);
180				this.setAutoHideDelay(options.autoHideDelay);
181			},
182
183			/**
184			 *
185			 * @param {BX.UI.Notification.BalloonOptions|null} options
186			 */
187			update: function(options)
188			{
189				this.setOptions(options);
190
191				BX.cleanNode(this.getContainer());
192
193				this.getContainer().appendChild(this.render());
194
195				this.deactivateAutoHide();
196				this.activateAutoHide();
197			},
198
199			/**
200			 * @public
201			 */
202			close: function()
203			{
204				const startState = this.getState();
205				if (startState === State.CLOSING || startState === State.CLOSED)
206				{
207					return;
208				}
209
210				this.setState(State.CLOSING);
211				this.deactivateAutoHide();
212
213				const finalize = () => {
214					if (this.getState() !== State.CLOSING)
215					{
216						return;
217					}
218
219					this.setState(State.CLOSED);
220
221					BX.ZIndexManager.unregister(this.getContainer());
222					BX.remove(this.getContainer());
223					this.container = null;
224
225					this.fireEvent("onClose");
226				};
227
228				if (startState === State.OPENING)
229				{
230					finalize();
231				}
232				else
233				{
234					this.animateOut(() => {
235						finalize();
236					});
237				}
238			},
239
240			/**
241			 * @private
242			 */
243			blink: function()
244			{
245				var self = this;
246				this.animateOut(function() {
247					setTimeout(function() {
248						self.update(null);
249						self.animateIn(function() {});
250					}, 200);
251				});
252			},
253
254			/**
255			 * @public
256			 */
257			adjustPosition: function()
258			{
259				if (this.getStack().isNewestOnTop())
260				{
261					this.getStack().adjustPosition();
262				}
263				else
264				{
265					this.getStack().adjustPosition(this);
266				}
267			},
268
269			/**
270			 * @public
271			 * @return {string}
272			 */
273			getId: function()
274			{
275				return this.id;
276			},
277
278			/**
279			 * @package
280			 * @return {Element}
281			 */
282			getCloseButton: function()
283			{
284				if (this.closeButton !== null)
285				{
286					return this.closeButton;
287				}
288
289				this.closeButton = BX.create("div", {
290					props: {
291						className: "ui-notification-balloon-close-btn"
292					},
293					events: {
294						click: this.handleCloseBtnClick.bind(this)
295					}
296				});
297
298				return this.closeButton;
299			},
300
301			/**
302			 *
303			 * @param {boolean} visibility
304			 */
305			setCloseButtonVisibility: function(visibility)
306			{
307				this.closeButtonVisibility = visibility !== false;
308			},
309
310			/**
311			 * @public
312			 * @return {boolean}
313			 */
314			isCloseButtonVisible: function()
315			{
316				return this.closeButtonVisibility;
317			},
318
319			/**
320			 * @public
321			 * @return {Element|string}
322			 */
323			getContent: function()
324			{
325				return this.content;
326			},
327
328			/**
329			 * @public
330			 * @param {Element|string} content
331			 */
332			setContent: function(content)
333			{
334				if (BX.type.isString(content) || BX.type.isDomNode(content))
335				{
336					this.content = content;
337				}
338			},
339
340			/**
341			 * @public
342			 * @return {number|"auto"}
343			 */
344			getWidth: function()
345			{
346				return this.width;
347			},
348
349			/**
350			 * @public
351			 * @param {number|"auto"} width
352			 */
353			setWidth: function(width)
354			{
355				if (BX.type.isNumber(width) || width === "auto")
356				{
357					this.width = width;
358				}
359			},
360
361			/**
362			 * @return {number}
363			 */
364			getZIndex: function()
365			{
366				var component = BX.ZIndexManager.getComponent(this.getContainer());
367
368				return component ? component.getZIndex() : 0;
369			},
370
371			/**
372			 * @deprecated
373			 * @param {number} zIndex
374			 */
375			setZIndex: function(zIndex)
376			{
377
378			},
379
380			/**
381			 * @package
382			 * @return {number}
383			 */
384			getHeight: function()
385			{
386				return this.getContainer().offsetHeight;
387			},
388
389			/**
390			 * @public
391			 * @return {string|null}
392			 */
393			getCategory: function()
394			{
395				return this.category;
396			},
397
398			/**
399			 * @public
400			 * @param {string|null} category
401			 */
402			setCategory: function(category)
403			{
404				if (BX.type.isNotEmptyString(category) || category === null)
405				{
406					this.category = category;
407				}
408			},
409
410			/**
411			 * @public
412			 * @param {BX.UI.Notification.Action[]|null} actions
413			 */
414			setActions: function(actions)
415			{
416				if (BX.type.isArray(actions))
417				{
418					this.actions = [];
419					actions.forEach(function(action) {
420						this.actions.push(new BX.UI.Notification.Action(this, action));
421					}, this);
422				}
423				else if (actions === null)
424				{
425					this.actions = [];
426				}
427			},
428
429			/**
430			 * @public
431			 * @return {BX.UI.Notification.Action[]}
432			 */
433			getActions: function()
434			{
435				return this.actions;
436			},
437
438			/**
439			 * @public
440			 * @param {string} id
441			 * @return {BX.UI.Notification.Action}
442			 */
443			getAction: function(id)
444			{
445				for (var i = 0; i < this.actions.length; i++)
446				{
447					var action = this.actions[i];
448					if (action.getId() === id)
449					{
450						return action;
451					}
452				}
453
454				return null;
455			},
456
457			/**
458			 * @public
459			 * @return {Element}
460			 */
461			getContainer: function()
462			{
463				if (this.container !== null)
464				{
465					return this.container;
466				}
467
468				this.container = BX.create("div", {
469					props: {
470						className: "ui-notification-balloon"
471					},
472					children: [
473						this.render()
474					],
475					events: {
476						mouseenter: this.handleMouseEnter.bind(this),
477						mouseleave: this.handleMouseLeave.bind(this)
478					}
479				});
480
481				return this.container;
482			},
483
484			/**
485			 * @protected
486			 * @return {Element}
487			 */
488			render: function()
489			{
490				if (this.getCustomRender() !== null)
491				{
492					return this.getCustomRender().apply(this, [this]);
493				}
494
495				var actions = this.getActions().map(function(action) {
496					return action.getContainer();
497				});
498
499				var content = this.getContent();
500				var width = this.getWidth();
501
502				return BX.create("div", {
503					props: {
504						className: "ui-notification-balloon-content"
505					},
506					style: {
507						width: BX.type.isNumber(width) ? (width + "px") : width
508					},
509					children: [
510						BX.create("div", {
511							props: {
512								className: "ui-notification-balloon-message"
513							},
514							html: BX.type.isDomNode(content) ? null : content,
515							children: BX.type.isDomNode(content) ? [content] : []
516						}),
517						BX.create("div", {
518							props: {
519								className: "ui-notification-balloon-actions"
520							},
521							children: actions
522						}),
523						this.isCloseButtonVisible() ?  this.getCloseButton(): null
524					]
525				});
526			},
527
528			/**
529			 * @public
530			 * @param {function} render
531			 */
532			setCustomRender: function(render)
533			{
534				if (BX.type.isFunction(render))
535				{
536					this.customRender = render;
537				}
538			},
539
540			/**
541			 * @public
542			 * @return {Function}
543			 */
544			getCustomRender: function()
545			{
546				return this.customRender;
547			},
548
549			/**
550			 * @public
551			 * @return {BX.UI.Notification.Stack}
552			 */
553			getStack: function()
554			{
555				return this.stack;
556			},
557
558			/**
559			 * @package
560			 * @param {State} state
561			 */
562			setState: function(state)
563			{
564				var code = this.getStateCode(state);
565				if (code !== null)
566				{
567					this.state = state;
568				}
569			},
570
571			/**
572			 * @public
573			 * @return {State}
574			 */
575			getState: function()
576			{
577				return this.state;
578			},
579
580			/**
581			 * @public
582			 * @param mode
583			 * @return {?string}
584			 */
585			getStateCode: function(mode)
586			{
587				for (var code in State)
588				{
589					if (State[code] === mode)
590					{
591						return code;
592					}
593				}
594
595				return null;
596			},
597
598			/**
599			 * @public
600			 */
601			activateAutoHide: function()
602			{
603				if (!this.getAutoHide())
604				{
605					return;
606				}
607
608				this.deactivateAutoHide();
609
610				this.autoHideTimeout = setTimeout(function() {
611					this.close();
612				}.bind(this), this.getAutoHideDelay());
613			},
614
615			/**
616			 * @public
617			 */
618			deactivateAutoHide: function()
619			{
620				clearTimeout(this.autoHideTimeout);
621				this.autoHideTimeout = null;
622			},
623
624			/**
625			 * @public
626			 * @param {boolean} autoHide
627			 */
628			setAutoHide: function(autoHide)
629			{
630				this.autoHide = autoHide !== false;
631			},
632
633			/**
634			 * @public
635			 * @return {boolean}
636			 */
637			getAutoHide: function()
638			{
639				return this.autoHide;
640			},
641
642			/**
643			 * @public
644			 * @param {number} delay
645			 */
646			setAutoHideDelay: function(delay)
647			{
648				if (BX.type.isNumber(delay) && delay > 0)
649				{
650					this.autoHideDelay = delay;
651				}
652			},
653
654			/**
655			 * @public
656			 * @return {number}
657			 */
658			getAutoHideDelay: function()
659			{
660				return this.autoHideDelay;
661			},
662
663			/**
664			 * @private
665			 * @param {function} callback
666			 */
667			animateIn: function(callback)
668			{
669				if (!this.getContainer().classList.contains(this.getAnimationClassName()))
670				{
671					this.getContainer().addEventListener("transitionend", function handleTransitionEnd() {
672						this.removeEventListener("transitionend", handleTransitionEnd);
673						callback();
674					});
675
676					this.getContainer().classList.add(this.getAnimationClassName());
677
678					if (this.useAirDesign === true)
679					{
680						BX.Dom.addClass(this.getContainer(), ['--air', '—ui-context-content-dark']);
681					}
682				}
683				else
684				{
685					callback();
686				}
687			},
688
689			/**
690			 * @private
691			 * @param {function} callback
692			 */
693			animateOut: function(callback)
694			{
695				if (this.getContainer().classList.contains(this.getAnimationClassName()))
696				{
697					this.getContainer().addEventListener("transitionend", function handleTransitionEnd() {
698						this.removeEventListener("transitionend", handleTransitionEnd);
699						callback();
700					});
701
702					this.getContainer().classList.remove(this.getAnimationClassName());
703				}
704				else
705				{
706					callback();
707				}
708			},
709
710			/**
711			 * @private
712			 * @return {string}
713			 */
714			getAnimationClassName: function()
715			{
716				return this.animationClassName;
717			},
718
719			/**
720			 * @private
721			 */
722			handleCloseBtnClick: function()
723			{
724				this.close();
725			},
726
727			/**
728			 * @private
729			 */
730			handleMouseEnter: function()
731			{
732				this.fireEvent("onMouseEnter");
733				this.deactivateAutoHide();
734				this.setState(State.PAUSED);
735				this.show();
736			},
737
738			/**
739			 * @private
740			 */
741			handleMouseLeave: function()
742			{
743				this.fireEvent("onMouseLeave");
744				this.activateAutoHide();
745			},
746
747			/**
748			 * @package
749			 * @param {string} eventName
750			 * @returns {BX.UI.Notification.Event}
751			 */
752			fireEvent: function(eventName)
753			{
754				var event = this.getEvent(eventName);
755				BX.onCustomEvent(this, event.getFullName(), [event]);
756
757				return event;
758			},
759
760			/**
761			 * @public
762			 * @param {string} eventName
763			 * @param {function} fn
764			 */
765			addEvent: function(eventName, fn)
766			{
767				if (BX.type.isFunction(fn))
768				{
769					BX.addCustomEvent(this, BX.UI.Notification.Event.getFullName(eventName), fn);
770				}
771			},
772
773			/**
774			 * @public
775			 * @param {string} eventName
776			 * @param {function} fn
777			 */
778			removeEvent: function(eventName, fn)
779			{
780				if (BX.type.isFunction(fn))
781				{
782					BX.removeCustomEvent(this, BX.UI.Notification.Event.getFullName(eventName), fn);
783				}
784			},
785
786			/**
787			 * @private
788			 * @param {string} eventName
789			 * @returns {BX.UI.Notification.Event}
790			 */
791			getEvent: function(eventName)
792			{
793				var event = new BX.UI.Notification.Event();
794				event.setBalloon(this);
795				event.setName(eventName);
796
797				return event;
798			},
799
800			/**
801			 * @public
802			 * @return {object}
803			 */
804			getData: function()
805			{
806				return this.data;
807			},
808
809			/**
810			 * @public
811			 * @param {object} data
812			 */
813			setData: function(data)
814			{
815				if (BX.type.isPlainObject(data))
816				{
817					this.data = data;
818				}
819			}
820		};
821
822	BX.UI.Notification.Event = function()
823	{
824		this.balloon = null;
825		this.name = null;
826	};
827
828	/**
829	 *
830	 * @param {string} eventName
831	 * @return {string}
832	 */
833	BX.UI.Notification.Event.getFullName = function(eventName)
834	{
835		return "UI.Notification.Balloon:" + eventName;
836	};
837
838	BX.UI.Notification.Event.prototype =
839		{
840			/**
841			 * @public
842			 * @return {BX.UI.Notification.Balloon}
843			 */
844			getBalloon: function()
845			{
846				return this.balloon;
847			},
848
849			/**
850			 * @public
851			 * @param {BX.UI.Notification.Balloon} balloon
852			 */
853			setBalloon: function(balloon)
854			{
855				if (balloon instanceof BX.UI.Notification.Balloon)
856				{
857					this.balloon = balloon;
858				}
859			},
860
861			/**
862			 * @public
863			 * @returns {string}
864			 */
865			getName: function()
866			{
867				return this.name;
868			},
869
870			/**
871			 * @public
872			 * @param {string} name
873			 */
874			setName: function(name)
875			{
876				if (BX.type.isNotEmptyString(name))
877				{
878					this.name = name;
879				}
880			},
881
882			/**
883			 * @public
884			 * @returns {string}
885			 */
886			getFullName: function()
887			{
888				return BX.UI.Notification.Event.getFullName(this.getName());
889			}
890		};
891
892	/**
893	 *
894	 * @param {BX.UI.Notification.Balloon} balloon
895	 * @param {object} [options]
896	 * @param {string} [options.id]
897	 * @param {string} [options.href]
898	 * @param {string} [options.title]
899	 * @param {?object.<string, function>} [options.events]
900	 * @constructor
901	 */
902	BX.UI.Notification.Action = function(balloon, options)
903	{
904		options = BX.type.isPlainObject(options) ? options : Object.create(null);
905
906		this.balloon = balloon;
907		this.id = BX.type.isNotEmptyString(options.id) ? options.id : BX.util.getRandomString(8).toLowerCase();
908		this.container = null;
909		this.href = BX.type.isNotEmptyString(options.href) ? options.href : null;
910		this.title = BX.type.isNotEmptyString(options.title) ? options.title : null;
911		this.window = window;
912
913		this.events = {};
914		if (BX.type.isPlainObject(options.events))
915		{
916			for (var eventName in options.events)
917			{
918				var fn = options.events[eventName];
919				if (!BX.type.isFunction(fn))
920				{
921					continue;
922				}
923
924				this.events[eventName] = (function(fn, action) {
925					return function(event) {
926						fn.call(event.target, event, action.getBalloon(), action);
927					};
928				})(fn, this);
929			}
930		}
931	};
932
933	BX.UI.Notification.Action.prototype =
934		{
935			/**
936			 * @public
937			 * @return {BX.UI.Notification.Balloon}
938			 */
939			getBalloon: function()
940			{
941				return this.balloon;
942			},
943
944			/**
945			 * @public
946			 * @return {string}
947			 */
948			getId: function()
949			{
950				return this.id;
951			},
952
953			/**
954			 * @public
955			 * @return {string}
956			 */
957			getTitle: function()
958			{
959				return this.title;
960			},
961
962			/**
963			 * @public
964			 * @return {string}
965			 */
966			getHref: function()
967			{
968				return this.href;
969			},
970
971			/**
972			 * @public
973			 * @return {Element}
974			 */
975			getContainer: function()
976			{
977				if (this.container === null)
978				{
979					this.container = BX.create(this.getHref() ?  "a" : "span", {
980						props: {
981							href: this.getHref(),
982							className: "ui-notification-balloon-action"
983						},
984						events: this.events,
985						text: this.getTitle()
986					})
987				}
988
989				return this.container;
990			},
991
992			getWindow: function()
993			{
994				return this.window;
995			}
996		};
997
998})();

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.