PageSourceSearch

https://stanko-trade.ru/bitrix/js/ui/notification/ui.notification.balloon.js

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

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.