/* * This file is part of Toolkit. * * Toolkit is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * Toolkit is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General * Public License along with this program; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA */ "use strict"; (function (w, TK) { function autoclose_cb(e) { var curr = e.target; while (curr) { // TODO: if a dialog is opened out of a dialog both should avoid // closing any of those on click. former version: //if (curr === this.element) return; // this closes tagger in Cabasa Dante Tagger when interacting // with the colorpicker. // workaround for the moment: // don't close on click on any dialog if (curr.classList.contains("toolkit-dialog")) return; curr = curr.parentElement; } this.close(); } function activate_autoclose() { if (this._autoclose_active) return; document.body.addEventListener("click", this._autoclose_cb); this._autoclose_active = true; } function deactivate_autoclose() { if (!this._autoclose_active) return; document.body.removeEventListener("click", this._autoclose_cb); this._autoclose_active = false; } TK.Dialog = TK.class({ /** * TK.Dialog provides a hovering area which can be closed by clicking/tapping * anywhere on the screen. It can be automatically pushed to the topmost * DOM position as a child of an AWML-ROOT or the BODY element. On close * it can be removed from the DOM. The {@link TK.Anchor}-functionality * makes positioning the dialog window straight forward. * * @class TK.Dialog * * @extends TK.Container * @implments TK.Anchor * * @param {Object} [options={ }] - An object containing initial options. * * @property {Boolean} [options.visible=true] - Hide or show the dialog. * @property {String} [options.anchor="top-left"] - Origin of `x` and `y` coordinates. * @property {Number} [options.x=0] - X-position of the dialog. * @property {Number} [options.y=0] - Y-position of the dialog. * @property {boolean} [options.auto_close=false] - Set dialog to `visible=false` if clicked outside in the document. * @property {boolean} [options.auto_remove=false] - Remove the dialogs DOM node after setting `visible=false`. * @property {boolean} [options.toplevel=false] - Add the dialog DOM node to the topmost position in DOM on `visible=true`. Topmost means either a parenting `AWML-ROOT` or the `BODY` node. * */ _class: "Dialog", Extends: TK.Container, Implements: TK.Anchor, _options: Object.assign(Object.create(TK.Container.prototype._options), { visible: "boolean", anchor: "string", x: "number", y: "number", auto_close: "boolean", auto_remove: "boolean", toplevel: "boolean", }), options: { visible: true, anchor: "top-left", x: 0, y: 0, auto_close: false, auto_remove: false, toplevel: false, }, static_events: { hide: function() { deactivate_autoclose.call(this); if (this.options.auto_remove) this.element.remove(); this.fire_event("close"); }, set_display_state: function(val) { var O = this.options; if (val === "show") { if (O.auto_close) activate_autoclose.call(this); this.trigger_resize(); } else { deactivate_autoclose.call(this); } if (val === "showing") { var C = O.container; if (C) C.appendChild(this.element); this.reposition(); } }, set_auto_close: function(val) { if (val) { if (!this.hidden()) activate_autoclose.call(this); } else { deactivate_autoclose.call(this); } }, set_visible: function (val) { var O = this.options; if (val) { deactivate_autoclose.call(this); if (O.toplevel && O.container.tagName !== "AWML-ROOT" && O.container.tagName !== "BODY") { var p = this.element; while ((p = p.parentElement) && p.tagName !== "AWML-ROOT" && p.tagName !== "BODY") {}; this.set("container", p); } this.show(); } else { O.container = this.element.parentElement; this.hide(); } }, }, initialize: function (options) { TK.Container.prototype.initialize.call(this, options); TK.add_class(this.element, "toolkit-dialog"); var O = this.options; /* This cannot be a default option because document.body * is not defined there */ if (!O.container) O.container = window.document.body; this._autoclose_active = false; this._autoclose_cb = autoclose_cb.bind(this); this.set('visible', O.visible); if (O.visible) this.force_show() else this.force_hide() }, resize: function() { if (this.options.visible) this.reposition(); }, redraw: function () { TK.Container.prototype.redraw.call(this); var I = this.invalid; var O = this.options; var E = this.element; if (I.x || I.y || I.anchor) { var bodybox = document.body.getBoundingClientRect(); var sw = bodybox.width; var sh = bodybox.height; var box = this.element.getBoundingClientRect(); I.x = I.y = I.anchor = false; var box = E.getBoundingClientRect(); var pos = this.translate_anchor(O.anchor, O.x, O.y, -box.width, -box.height); pos.x = Math.min(sw - box.width, Math.max(0, pos.x)); pos.y = Math.min(sh - box.height, Math.max(0, pos.y)); E.style.left = pos.x + "px" E.style.top = pos.y + "px" } }, /** * Open the dialog. Optionally set x and y position regarding `anchor`. * * @method TK.Dialog#open * * @param {Number} [x] - New X-position of the dialog. * @param {Number} [y] - New Y-position of the dialog. */ open: function (x, y) { this.fire_event("open"); this.userset("visible", true); if (typeof x !== "undefined") this.set("x", x); if (typeof y !== "undefined") this.set("y", y); }, /** * Close the dialog. The node is removed from DOM if `auto_remove` is set to `true`. * * @method TK.Dialog#close */ close: function () { this.userset("visible", false); }, /** * Reposition the dialog to the current `x` and `y` position. * * @method TK.Dialog#reposition */ reposition: function () { var O = this.options; this.set("x", O.x); this.set("y", O.y); } }); })(this, this.TK);