/* * 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 after_hiding() { this.__hide_id = false; if (this.options.display_state === "hiding") this.set("display_state", "hide"); } function after_showing() { this.__hide_id = false; if (this.options.display_state === "showing") this.set("display_state", "show"); } function enable_draw_self() { if (this._drawn) return; this._drawn = true; if (this.needs_redraw) { TK.S.add(this._redraw, 1); } /** * Is fired when the container is shown. * * @event TK.Container#show */ this.fire_event("show"); } function enable_draw_children() { var C = this.children; var H = this.hidden_children; if (C) for (var i = 0; i < C.length; i++) if (!H[i]) C[i].enable_draw(); } function disable_draw_self() { if (!this._drawn) return; this._drawn = false; if (this.needs_redraw) { TK.S.remove(this._redraw, 1); TK.S.remove_next(this._redraw, 1); } /** * Is fired when the container is hidden. * * @event TK.Container#hide */ this.fire_event("hide"); } function disable_draw_children() { var C = this.children; var H = this.hidden_children; if (C) for (var i = 0; i < C.length; i++) if (!H[i]) C[i].disable_draw(); } TK.Container = TK.class({ /** * TK.Container represents a <DIV> element contining various * other widgets or DOMNodes. * * Containers have four different display states: show, hide, * showing and hiding. Each of these states has a corresponding * CSS class called toolkit-show, toolkit-hide, toolkit-showing * and toolkit-hiding, respectively. The display state can be controlled using * the methods {@link TK.Container#show}, {@link TK.Container#hide} and {@link TK.Widget#toggle_hidden}. * * A container can keep track of the display states of its child widgets. * The display state of a child can be changed using {@link TK.Container#hide_child}, * {@link TK.Container#show_child} and {@link TK.Container#toggle_child}. * * @class TK.Container * * @extends TK.Widget * * @param {Object} [options={ }] - An object containing initial options. * * @property {String|HTMLElement} [options.content] - The content of the container. It can either be * a string which is interpreted as Text or a DOM node. Note that this option will remove all * child nodes from the container element including those added via append_child. * @property {Number} [options.hiding_duration] - The duration in ms of the hiding CSS * transition/animation of this container. If this option is not set, the transition duration * will be determined by the computed style, which can be rather * expensive. Setting this option explicitly can therefore be an optimization. * @property {Number} [options.showing_duration] - The duration in ms of the showing CSS * transition/animation of this container. * @property {String} [options.display_state="show"] - The current display state of this container. * Do not modify, manually. * @property {Array} [options.children=[]] - Add child widgets on init. Will not be maintained on runtime! Just for convenience purposes on init. */ _class: "Container", Extends: TK.Widget, _options: Object.assign(Object.create(TK.Widget.prototype._options), { content: "string|DOMNode", display_state: "string", hiding_duration: "number", showing_duration: "number", children: "array", }), options: { display_state : "show", children: [], }, initialize: function (options) { var E; TK.Widget.prototype.initialize.call(this, options); this.hidden_children = []; /** * @member {HTMLDivElement} TK.Container#element - The main DIV element. Has class toolkit-container */ if (!(E = this.element)) this.element = E = TK.element("div"); TK.add_class(E, "toolkit-container"); this.widgetize(E, true, true, true); this.__after_hiding = after_hiding.bind(this); this.__after_showing = after_showing.bind(this); this.__hide_id = false; TK.add_class(E, "toolkit-show"); if (this.options.children.length) this.append_children(this.options.children); }, /** * Calls {@link TK.Container#append_child} for an array of widgets. * * @method TK.Container#append_children * * @param {Array.} children - The child widgets to append. */ append_children : function (a) { a.map(this.append_child, this); }, /** * Appends child.element to the container element and * registers child as a child widget. * * @method TK.Container#append_child * * @param {TK.Widget} child - The child widget to append. */ append_child : function(child) { child.set("container", this.element); this.add_child(child); }, set_parent : function(parent) { if (parent && !(parent instanceof TK.Container)) { TK.warn("Container %o should not be child of non-container %o", this, parent); } TK.Widget.prototype.set_parent.call(this, parent); }, add_child : function(child) { TK.Widget.prototype.add_child.call(this, child); var H = this.hidden_children; if (!H) this.hidden_children = H = []; H.push(false); }, remove_child : function(child) { if (!child) return; child.disable_draw(); child.parent = null; var C = this.children; if (C === null) return; var H = this.hidden_children; var i = C.indexOf(child); if (i !== -1) { C.splice(i, 1); H.splice(i, 1); } }, enable_draw: function () { if (this._drawn) return; enable_draw_self.call(this); enable_draw_children.call(this); }, disable_draw: function () { if (!this._drawn) return; disable_draw_self.call(this); disable_draw_children.call(this); }, /** * Starts the transition of the display_state to hide. * * @method TK.Container#hide * */ hide: function () { var O = this.options; if (O.display_state === "hide") return; disable_draw_children.call(this); enable_draw_self.call(this); if (O.display_state === "hiding") return; this.set("display_state", "hiding"); }, /** * Immediately switches the display state of this container to hide. * Unlike {@link TK.Container#hide} this method does not perform the hiding transition * and immediately modifies the DOM by setting the toolkit-hide class. * * @method TK.Container#force_hide * */ force_hide: function () { var O = this.options; if (O.display_state === "hide") return; this.disable_draw(); var E = this.element; O.display_state = "hide"; TK.add_class(E, "toolkit-hide"); TK.remove_class(E, "toolkit-hiding", "toolkit-showing", "toolkit-show"); }, /** * Starts the transition of the display_state to show. * * @method TK.Container#show * */ show: function() { var O = this.options; enable_draw_self.call(this); if (O.display_state === "show" || O.display_state === "showing") return; this.set("display_state", "showing"); }, /** * Immediately switches the display state of this container to show. * Unlike {@link TK.Container#hide} this method does not perform the hiding transition * and immediately modifies the DOM by setting the toolkit-show class. * * @method TK.Container#force_show * */ force_show: function() { var O = this.options; if (O.display_state === "show") return; this.enable_draw(); var E = this.element; O.display_state = "show"; TK.add_class(E, "toolkit-show"); TK.remove_class(E, "toolkit-hiding", "toolkit-showing", "toolkit-hide"); }, show_nodraw: function() { var O = this.options; if (O.display_state === "show") return; this.set("display_state", "show"); var C = this.children; var H = this.hidden_children; if (C) for (var i = 0; i < C.length; i++) if (!H[i]) C[i].show_nodraw(); }, hide_nodraw: function() { var O = this.options; if (O.display_state === "hide") return; this.set("display_state", "hide"); var C = this.children; var H = this.hidden_children; if (C) for (i = 0; i < C.length; i++) if (!H[i]) C[i].hide_nodraw(); }, /** * Switches the hidden state of a child to hidden. * The argument is either the child index or the child itself. * * @method TK.Container#hide_child * @param {Object|integer} child - Child or its index. * */ hide_child: function(i) { var C = this.children; var H = this.hidden_children; if (typeof i !== "number") { i = C.indexOf(i); if (i === -1) throw("Cannot find child."); } H[i] = true; C[i].hide(); }, /** * Switches the hidden state of a child to shown. * The argument is either the child index or the child itself. * * @method TK.Container#show_child * @param {Object|integer} child - Child or its index. * */ show_child: function(i) { var C = this.children; var H = this.hidden_children; if (typeof i !== "number") { i = C.indexOf(i); if (i === -1) throw("Cannot find child."); } if (H[i]) { H[i] = false; if (this.is_drawn()) C[i].show(); else C[i].show_nodraw(); } }, /** * Toggles the hidden state of a child. * The argument is either the child index or the child itself. * * @method TK.Container#toggle_child * @param {Object|integer} child - Child or its index. * */ toggle_child: function(i) { var C = this.children; var H = this.hidden_children; if (typeof i !== "number") { i = C.indexOf(i); if (i === -1) throw("Cannot find child."); } if (H[i]) this.show_child(i); else this.hide_child(i); }, visible_children: function(a) { if (!a) a = []; var C = this.children; var H = this.hidden_children; if (C) for (var i = 0; i < C.length; i++) { if (H[i]) continue; a.push(C[i]); C[i].visible_children(a); } return a; }, hidden: function() { var state = this.options.display_state; return TK.Widget.prototype.hidden.call(this) || state === "hiding" || state === "hide"; }, redraw: function() { var O = this.options; var I = this.invalid; var E = this.element; TK.Widget.prototype.redraw.call(this); if (I.display_state) { I.display_state = false; var time; TK.remove_class(E, "toolkit-hiding", "toolkit-hide", "toolkit-showing", "toolkit-show"); if (this.__hide_id) { window.clearTimeout(this.__hide_id); this.__hide_id = false; } switch (O.display_state) { case "hiding": TK.add_class(E, "toolkit-hiding"); time = O.hiding_duration || TK.get_duration(E); if (time > 0) { this.__hide_id = window.setTimeout(this.__after_hiding, time); break; } this.set("display_state", "hide"); TK.remove_class(E, "toolkit-hiding"); /* FALL THROUGH */ case "hide": TK.add_class(E, "toolkit-hide"); disable_draw_self.call(this); break; case "showing": TK.add_class(E, "toolkit-showing"); time = O.showing_duration || TK.get_duration(E); if (time > 0) { this.__hide_id = window.setTimeout(this.__after_showing, time); enable_draw_children.call(this); break; } this.set("display_state", "show"); TK.remove_class(E, "toolkit-showing"); /* FALL THROUGH */ case "show": TK.add_class(E, "toolkit-show"); enable_draw_children.call(this); break; } } if (I.content) { I.content = false; TK.empty(E); if (typeof O.content === "string") TK.set_content(E, O.content); else E.appendChild(O.content); } }, }); })(this, this.TK);