var Widget = require ('./widget'); /** * Class to handle popups. */ module.exports = new Class ({ Extends: Widget ,Tag: 'htk-popup' ,Properties: { /** * The popup child. */ child: { type: Widget ,set: function (x) { this._child = x; this._setChildNode (x.node); } ,get: function () { return this._child; } } /** * The popup child Node. */ ,childNode: { type: Object ,set: function (x) { this._child = null; this._setChildNode (x); } ,get: function () { return this.node.firstChild; } } /** * Indicates how the dialog must be displayed. */ ,modal: { type: Boolean ,set: function (x) { this._modal = x; } ,get: function () { return this._modal; } } } ,_parent: null ,_fitParent: false ,_modal: false ,_isOpen: false ,_child: null ,initialize: function (props) { this._bgMouseDownHandler = this._bgMouseDown.bind (this); this._bgKeyPressHandler = this._bgKeyPress.bind (this); this.parent (props); } ,render: function () { var div = this.createRoot ('div'); div.className = 'htk-popup'; } ,_setChildNode: function (childNode) { Vn.Node.removeChilds (this.node); this.node.appendChild (childNode); } /** * Shows the popup relative to another element. * * @param {Node} parent The relative element * @param {boolean} fitParent Wether to set the width same to the parent * @param {HTMLEvent} ignoreEvent An optional event object to ignore */ ,show: function (parent, fitParent, ignoreEvent) { this._parent = parent; this._fitParent = fitParent; this.open (ignoreEvent); } /** * Opens the popup. * * @param {Node} parent The relative element * @param {boolean} fitParent Wether to set the width same to the parent */ ,open: function (ignoreEvent) { if (ignoreEvent) this._ignoreEvent = ignoreEvent; if (this._isOpen) { this.resetAsync (); return; } var node = this.node; node.addEventListener ('mousedown', this._onPopupMouseDown.bind (this)); this.doc.addEventListener ('keypress', this._bgKeyPressHandler); if (this.isModal ()) { var bg = this._bg = this.createElement ('div'); bg.className = 'htk-background'; bg.addEventListener ('mousedown', this._bgMouseDownHandler); Htk.Toast.pushTop (bg); Vn.Node.addClass (node, 'modal'); bg.appendChild (node); this.doc.body.appendChild (bg); } else { this.doc.addEventListener ('mousedown', this._bgMouseDownHandler); this.doc.body.appendChild (node); Vn.Node.addClass (node, 'fixed'); this.resetAsync (); } setTimeout (this._onOpacityTimeout.bind (this), 10); this._isOpen = true; } /** * Returns if the popup window shoud be modal based on the modal property * and if the device is mobile. * * @return {boolean} %true if it's modal, %false otherwise */ ,isModal: function () { return this._modal || Vn.isMobile () || !this._parent; } ,_onOpacityTimeout: function () { if (!this._isOpen) return; if (this._bg) this._bg.style.opacity = 1; this._node.style.opacity = 1; } ,_onResetTimeout: function () { this.reset (); } ,resetAsync: function () { setTimeout (this._onResetTimeout.bind (this)); } /** * Repositions the popup. */ ,reset: function () { if (!this._isOpen || this.isModal ()) return; var node = this._node; var style = node.style; style.visibility = 'hidden'; style.height = ''; style.width = ''; var margin = 20; var dblMargin = margin * 2; var iniWidth; var iniHeight; var width = iniWidth = node.offsetWidth; var height = iniHeight = node.offsetHeight; var windowWidth = window.innerWidth; var windowHeight = window.innerHeight; if (width + dblMargin > windowWidth) width = windowWidth - dblMargin; if (height + dblMargin > windowHeight) height = windowHeight - dblMargin; var rect = this._parent.getBoundingClientRect (); var left = rect.left; var top = rect.top + this._parent.offsetHeight; if (this._fitParent) width = this._parent.offsetWidth; if (left + width + margin > windowWidth) left -= (left + width) - window.windowWidth + margin; if (top + height + margin > windowHeight) top -= height + this._parent.offsetHeight; if (left < margin) left = margin; if (top < margin) top = margin; style.top = (top) +'px'; style.left = (left) +'px'; if (width != iniWidth) style.width = (width) +'px'; if (height != iniHeight) style.height = (height) +'px'; style.visibility = 'initial'; } /** * Hides the popup. */ ,hide: function () { if (!this._isOpen) return; var node = this._node; if (this._bg) { Htk.Toast.popTop (); Vn.Node.remove (this._bg); Vn.Node.removeClass (node, 'modal'); this._bg = null; } else { this.doc.removeEventListener ('mousedown', this._bgMouseDownHandler); Vn.Node.removeClass (node, 'fixed'); } Vn.Node.remove (node); this._parent = null; this._isOpen = false; this.emit ('closed'); } ,_bgMouseDown: function (e) { if (e !== this._ignoreEvent) this.hide (); this._ignoreEvent = null; } ,_bgKeyPress: function (e) { if (e.keyCode == 27) // Escape this.hide (); } ,_onPopupMouseDown: function (e) { this._ignoreEvent = e; } });