220 lines
7.1 KiB
JavaScript
220 lines
7.1 KiB
JavaScript
|
/*
|
||
|
* 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);
|