Your IP : 18.116.85.96


Current Path : /var/www/www-root/data/www/www.monolith-realty.ru/bitrix/js/ui/hint/
Upload File :
Current File : /var/www/www-root/data/www/www.monolith-realty.ru/bitrix/js/ui/hint/ui.hint.js

;(function ()
{
	"use strict";

	BX.namespace("BX.UI");
	if (BX.UI.Hint)
	{
		return;
	}

	/**
	 * Check is hovered element or not.
	 * @param {Object} cursorPosition
	 * @param {HTMLElement} element
	 * @returns
	 */
	var isHovered = function(cursorPosition, element) {
		var elementRect = element.getBoundingClientRect();
		var xMarginLeft = elementRect.x;
		var xMarginRight = elementRect.x + elementRect.width;
		var yMarginLeft = elementRect.y;
		var yMarginRight = elementRect.y + elementRect.height;

		return xMarginLeft <= cursorPosition.x && cursorPosition.x <= xMarginRight
			&& yMarginLeft <= cursorPosition.y && cursorPosition.y <= yMarginRight;
	};

	/**
	 * Hint manager.
	 *
	 * @param {object} [parameters] - Parameters.
	 * @param {string} [parameters.id] - Id hint instance and id popup window.
	 * @param {string} [parameters.attributeName] - Name of hint attribute.
	 * @param {string} [parameters.attributeInitName] - Name of init hint attribute.
	 * @param {string} [parameters.classNameIcon]
	 * @param {string} [parameters.className]
	 * @param {Element} [parameters.content] - Content node for text of hint.
	 * @param {BX.PopupWindow} [parameters.popup] - Custom popup instance.
	 * @param {object} [parameters.popupParameters] - Parameters for standard popup.
	 * @constructor
	 */
	function Manager (parameters)
	{
		parameters = parameters || {};
		this.id = 'ui-hint-popup-' + (+new Date());

		if (parameters.id)
		{
			this.id = parameters.id;
		}
		if (parameters.attributeName)
		{
			this.attributeName = parameters.attributeName;
		}
		if (parameters.classNameIcon)
		{
			this.classNameIcon = parameters.classNameIcon;
		}
		if (parameters.className)
		{
			this.className = parameters.className;
		}
		if (parameters.attributeInitName)
		{
			this.attributeInitName = parameters.attributeInitName;
		}
		if (parameters.content)
		{
			if (!BX.type.isDomNode(parameters.content))
			{
				throw new Error('Parameter `content` should be a DOM Node.');
			}
			this.content = parameters.content;
		}
		if (parameters.popup)
		{
			if (!(parameters.popup instanceof BX.PopupWindow))
			{
				throw new Error('Parameter `popup` should be an instance of BX.PopupWindow.');
			}
			this.popup = parameters.popup;
		}
		if (parameters.popupParameters)
		{
			this.popupParameters = parameters.popupParameters;
		}

		this.initByClassName();
		BX.ready(this.initByClassName.bind(this));
	}
	Manager.prototype = {
		attributeName: 'data-hint',
		attributeHtmlName: 'data-hint-html',
		attributeInitName: 'data-hint-init',
		attributeInteractivityName: 'data-hint-interactivity',
		className: 'ui-hint',
		classNameIcon: 'ui-hint-icon',
		classNameContent: 'ui-hint-content',
		classNamePopup: 'ui-hint-popup',
		classNamePopupInteractivity: 'ui-hint-popup-interactivity',
		popup: null,
		content: null,
		popupParameters: null,
		ownerDocument: null,
		cursorPosition: {x:0, y:0},
		anchorNode: null,

		/**
		 * Create instance of manager. Use for customization purposes.
		 *
		 * @param {object} [parameters] - Parameters.
		 * @returns {Manager}
		 */
		createInstance: function (parameters)
		{
			return new Manager(parameters);
		},

		/**
		 * Init all hints founded by class name on document.
		 */
		initByClassName: function ()
		{
			var nodes = document.getElementsByClassName(this.className);
			nodes = BX.convert.nodeListToArray(nodes);
			nodes.forEach(this.initNode, this);
		},

		/**
		 * Init hints that was found in context. Use for mass initialization of hints.
		 *
		 * @param {HTMLElement} [context] - Context.
		 */
		init: function (context)
		{
			context = context || document.body;
			var nodes = context.querySelectorAll('[' + this.attributeName + ']');
			nodes = BX.convert.nodeListToArray(nodes);
			nodes.forEach(this.initNode, this);

			this.initOwnerDocument(context);
		},

		/**
		 * Init the owner document to track the cursor position.
		 * @param {HTMLElement} element
		 * @returns
		 */
		initOwnerDocument: function (element)
		{
			if (element.ownerDocument === this.ownerDocument)
			{
				return;
			}

			this.ownerDocument = element.ownerDocument;

			BX.bind(this.ownerDocument, 'mousemove', (e) => {
				this.cursorPosition.x = e.x;
				this.cursorPosition.y = e.y;
			});
		},

		/**
		 * Create hint node. Use in js-generated layout.
		 * Return node with styles, icon and bound event handlers.
		 *
		 * @param {string} text - Text of hint.
		 * @returns {Element|*}
		 */
		createNode: function (text)
		{
			var node = document.createElement('span');
			node.setAttribute(this.attributeName, text);
			this.initNode(node);
			return node;
		},

		/**
		 * Init hind node. Add styles, icon, event handlers.
		 *
		 * @param {Element} node - Element node of hint.
		 */
		initNode: function (node)
		{
			if (node.getAttribute(this.attributeInitName))
			{
				return;
			}

			node.setAttribute(this.attributeInitName, 'y');

			var text = node.getAttribute(this.attributeName);
			if (!BX.type.isNotEmptyString(text))
			{
				return;
			}

			if (!node.hasAttribute(this.attributeHtmlName))
			{
				text = BX.util.htmlspecialchars(text);
			}

			if (!node.hasAttribute('data-hint-no-icon'))
			{
				BX.addClass(node, this.className);
				node.innerHTML = '';

				var iconNode = document.createElement('span');
				BX.addClass(iconNode, this.classNameIcon);
				node.appendChild(iconNode);
			}

			if (node.hasAttribute('data-hint-center'))
			{
				BX.bind(node, 'mouseenter', this.show.bind(this, node, text, true));
			}
			else
			{
				BX.bind(node, 'mouseenter', this.show.bind(this, node, text, false));
			}

			BX.bind(node, 'mouseleave', this.hide.bind(this, node));
		},

		/**
		 * Show hint window. Automatically calls on `mouseenter` event.
		 *
		 * @param {Element} anchorNode - Anchor node for popup with text.
		 * @param {string } html - Html of hint.
		 * @param {boolean } centerPos - Position center for hint.
		 */
		show: function (anchorNode, html, centerPos)
		{
			this.anchorNode = anchorNode;
			if (!this.content)
			{
				this.content= document.createElement('div');
				BX.addClass(this.content, this.classNameContent);
			}

			if (!this.popup)
			{
				var parameters = this.popupParameters || {};
				if (typeof parameters.zIndex  === "undefined")
				{
					parameters.zIndex = 1000;
				}
				if (typeof parameters.darkMode  === "undefined")
				{
					parameters.darkMode = true;
				}
				if (typeof parameters.animationOptions  === "undefined")
				{
					/*
					// bug with fast hide/show
					parameters.animationOptions = {
						show: {
							type: "opacity"
						},
						close: {
							type: "opacity"
						}
					};
					*/
				}

				if (typeof parameters.animation  === "undefined")
				{
					parameters.animation = "fading-slide";
				}

				if (typeof parameters.content === "undefined")
				{
					parameters.content = this.content;
				}

				if (typeof parameters.className === "undefined")
				{
					parameters.className = this.classNamePopup;
				}

				if (centerPos === true)
				{
					if (typeof parameters.offsetLeft === "undefined")
					{
						parameters.offsetLeft = 0;
					}

					if (typeof parameters.angle === "undefined")
					{
						parameters.angle = {
							offset: 0,
						};
					}

					if (typeof parameters.events === "undefined")
					{
						parameters.events = {
							onPopupShow: function ()
							{
								this.offsetLeft = this.getPopupContainer().offsetWidth ? 23 + (anchorNode.offsetWidth - this.getPopupContainer().offsetWidth) / 2 : false;
								setTimeout(function() {
									this.angle.offset = this.getPopupContainer().offsetWidth ? (this.getPopupContainer().offsetWidth / 2) - 16 : false;
									this.angle.element.style.left = this.angle.offset ? this.angle.offset + 'px': false;
								}.bind(this), 0);
							},
						}
					}
				}
				else
				{
					if (typeof parameters.angle === "undefined")
					{
						parameters.angle = {
							offset: anchorNode.offsetWidth ? (23 + anchorNode.offsetWidth / 2) : false,
						};
					}
				}

				this.popup = new BX.PopupWindow(this.id, anchorNode, parameters);

				BX.bind(this.popup.getPopupContainer(), 'mouseleave', this.hide.bind(this, this.popup.getPopupContainer()));
			}

			if (anchorNode.hasAttribute(this.attributeInteractivityName))
			{
				BX.Dom.addClass(this.popup.getPopupContainer(), this.classNamePopupInteractivity);
			}
			else
			{
				BX.Dom.removeClass(this.popup.getPopupContainer(), this.classNamePopupInteractivity);
			}
			this.content.innerHTML = html;
			this.popup.setBindElement(anchorNode);
			this.popup.show();
			if (centerPos === true)
			{
				this.popup.getPopupContainer().style.visibility = 'hidden';
				setTimeout(function() {
					this.popup.getPopupContainer().style.visibility = '';
				}.bind(this), 10);
			}
			this.timer = null;
		},

		/**
		 * Hide hint window. Automatically calls on `mouseleave` event.
		 */
		hide: function (anchorNode)
		{
			if (!this.popup)
			{
				return;
			}

			if (anchorNode && anchorNode.hasAttribute(this.attributeInteractivityName))
			{
				// exit from flow with a short pause, so that when the mouse is moved diagonally, the popup does not disappear.
				setTimeout(() => {
					if (this.popup && !isHovered(this.cursorPosition, this.popup.getPopupContainer()))
					{
						this.popup.close();
					}
				}, 100);
			}
			else
			{
				this.popup.close();
			}
		}
	};

	BX.UI.Hint = new Manager();

})();