13
0
livetrax/share/web_surfaces/builtin/mixer/toolkit/implements/base.js

922 lines
31 KiB
JavaScript
Raw Normal View History

2020-06-21 17:29:27 -04:00
/*
* 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) {
var merge = function(dst) {
var key, i, src;
for (i = 1; i < arguments.length; i++) {
src = arguments[i];
for (key in src) {
dst[key] = src[key];
}
}
return dst;
};
var mixin = function(dst, src) {
var fun, key;
for (key in src) if (!dst[key]) {
if (key === "constructor" ||
key === "_class" ||
key === "Extends" ||
key === "Implements" ||
key === "options") continue;
if (!src.hasOwnProperty(key)) continue;
fun = src[key];
dst[key] = fun;
}
return dst;
};
function call_handler(self, fun, args) {
try {
return fun.apply(self, args);
} catch (e) {
TK.warn("event handler", fun, "threw", e);
}
}
function dispatch_events(self, handlers, args) {
var v;
if (Array.isArray(handlers)) {
for (var i = 0; i < handlers.length; i++) {
v = call_handler(self, handlers[i], args);
if (v !== void(0)) return v;
}
} else return call_handler(self, handlers, args);
}
function add_event(to, event, fun) {
var tmp = to[event];
if (!tmp) {
to[event] = fun;
} else if (Array.isArray(tmp)) {
tmp.push(fun);
} else {
to[event] = [ tmp, fun ];
}
}
function remove_event(from, event, fun) {
var tmp = from[event];
if (!tmp) return;
if (Array.isArray(tmp)) {
for (var i = 0; i < tmp.length; i++) {
if (tmp[i] === fun) {
tmp[i] = tmp[tmp.length-1];
tmp.length --;
}
}
if (tmp.length === 1) from[event] = tmp[0];
else if (tmp.length === 0) delete from[event];
} else if (tmp === fun) {
delete from[event];
}
}
function add_static_event(w, event, fun) {
var p = w.prototype, e;
if (!p.hasOwnProperty('static_events')) {
if (p.static_events) {
p.static_events = e = Object.assign({}, p.static_events);
} else {
p.static_events = e = {};
}
} else e = p.static_events;
add_event(e, event, fun);
}
function arrayify(x) {
if (!Array.isArray(x)) x = [ x ];
return x;
}
function merge_static_events(a, b) {
var event;
if (!a) return b;
if (!b) return Object.assign({}, a);
for (event in a) {
var tmp = a[event];
if (b.hasOwnProperty(event)) {
b[event] = arrayify(tmp).concat(arrayify(b[event]));
} else {
b[event] = Array.isArray(tmp) ? tmp.slice(0) : tmp;
}
}
return Object.assign({}, a, b);
}
TK.class = function(o) {
var constructor;
var methods;
var tmp, i, c, key;
if (tmp = o.Extends) {
if (typeof(tmp) === "function") {
tmp = tmp.prototype;
}
if (typeof(o.options) === "object" &&
typeof(tmp.options) === "object") {
o.options = Object.assign(Object.create(tmp.options), o.options);
}
if (o.static_events) o.static_events = merge_static_events(tmp.static_events, o.static_events);
methods = Object.assign(Object.create(tmp), o);
} else {
methods = o;
}
tmp = o.Implements;
// mixins
if (tmp !== void(0)) {
tmp = arrayify(tmp);
for (i = 0; i < tmp.length; i++) {
if (typeof(tmp[i]) === "function") {
c = tmp[i].prototype;
} else c = tmp[i];
if (typeof(c.options) === "object") {
if (!methods.hasOwnProperty("options")) {
methods.options = Object.create(methods.options || null);
}
methods.options = merge({}, c.options, methods.options);
}
if (c.static_events) {
if (methods.static_events) {
methods.static_events = merge_static_events(c.static_events,
Object.assign({}, methods.static_events));
} else {
methods.static_events = c.static_events;
}
}
methods = mixin(methods, c, true);
}
}
var init = methods.initialize;
var post_init = methods.initialized;
if (post_init) {
constructor = function() {
init.apply(this, arguments);
post_init.call(this);
};
} else constructor = init || (function() {});
constructor.prototype = methods;
methods.constructor = constructor;
return constructor;
};
var __native_events = {
// mouse
mouseenter : true,
mouseleave : true,
mousedown : true,
mouseup : true,
mousemove : true,
mouseover : true,
click : true,
dblclick : true,
startdrag : true,
stopdrag : true,
drag : true,
dragenter : true,
dragleave : true,
dragover : true,
drop : true,
dragend : true,
// touch
touchstart : true,
touchend : true,
touchmove : true,
touchenter : true,
touchleave : true,
touchcancel: true,
keydown : true,
keypress : true,
keyup : true,
scroll : true,
focus : true,
blur : true,
// mousewheel
mousewheel : true,
DOMMouseScroll : true,
wheel : true,
submit : true,
contextmenu: true,
};
function is_native_event(type) {
return __native_events[type];
}
function remove_native_events(element) {
var type;
var s = this.static_events;
var d = this.__events;
var handler = this.__native_handler;
for (type in s) if (is_native_event(type))
TK.remove_event_listener(element, type, handler);
for (type in d) if (is_native_event(type) && (!s || !s.hasOwnProperty(type)))
TK.remove_event_listener(element, type, handler);
}
function add_native_events(element) {
var type;
var s = this.static_events;
var d = this.__events;
var handler = this.__native_handler;
for (type in s) if (is_native_event(type))
TK.add_event_listener(element, type, handler);
for (type in d) if (is_native_event(type) && (!s || !s.hasOwnProperty(type)))
TK.add_event_listener(element, type, handler);
}
function native_handler(ev) {
/* FIXME:
* * mouseover and error are cancelled with true
* * beforeunload is cancelled with null
*/
if (this.fire_event(ev.type, ev) === false) return false;
}
function has_event_listeners(event) {
var ev = this.__events;
if (ev.hasOwnProperty(event)) return true;
ev = this.static_events;
return ev && ev.hasOwnProperty(event);
}
/**
* This is the base class for all widgets in toolkit.
* It provides an API for event handling and other basic implementations.
*
* @class TK.Base
*/
TK.Base = TK.class({
initialize : function(options) {
this.__events = {};
this.__event_target = null;
this.__native_handler = native_handler.bind(this);
this.set_options(options);
this.fire_event("initialize");
},
initialized : function() {
/**
* Is fired when an instance is initialized.
*
* @event TK.Base#initialized
*/
this.fire_event("initialized");
},
/**
* Destroys all event handlers and the options object.
*
* @method TK.Base#destroy
*/
destroy : function() {
if (this.__event_target) {
remove_native_events.call(this, this.__event_target, this.__events);
}
this.__events = null;
this.__event_target = null;
this.__native_handler = null;
this.options = null;
},
/**
* Merges a new options object into the existing one,
* including deep copies of objects. If an option key begins with
* the string "on" it is considered as event handler. In this case
* the value should be the handler function for the event with
* the corresponding name without the first "on" characters.
*
* @method TK.Base#set_options(options)
*
* @param {Object} [options={ }] - An object containing initial options.
*/
set_options : function(o) {
var opt = this.options;
var key, a, b;
if (typeof(o) !== "object") {
delete this.options;
o = {};
} else if (typeof(opt) === "object") for (key in o) if (o.hasOwnProperty(key)) {
a = o[key];
b = opt[key];
if (typeof a === "object" && a &&
Object.getPrototypeOf(Object.getPrototypeOf(a)) === null &&
typeof b === "object" && b &&
Object.getPrototypeOf(Object.getPrototypeOf(b)) === null
) {
o[key] = merge({}, b, a);
}
}
if (this.hasOwnProperty("options")) {
this.options = merge(opt, o);
} else if (opt) {
this.options = Object.assign(Object.create(opt), o);
} else {
this.options = Object.assign({}, o);
}
for (key in this.options) if (key.startsWith("on")) {
this.add_event(key.substr(2).toLowerCase(), this.options[key]);
delete this.options[key];
}
},
/**
* Get the value of an option.
*
* @method TK.Base#get
*
* @param {string} key - The option name.
*/
get: function (key) {
return this.options[key];
},
/**
* Sets an option. Fires both the events <code>set</code> with arguments <code>key</code>
* and <code>value</code>; and the event <code>'set_'+key</code> with arguments <code>value</code>
* and <code>key</code>.
*
* @method TK.Base#set
*
* @param {string} key - The name of the option.
* @param {mixed} value - The value of the option.
*
* @emits TK.Base#set
* @emits TK.Base#set_[option]
*/
set: function (key, value) {
var e;
this.options[key] = value;
/**
* Is fired when an option is set.
*
* @event TK.Base#set
*
* @param {string} name - The name of the option.
* @param {mixed} value - The value of the option.
*/
if (this.has_event_listeners("set"))
this.fire_event("set", key, value);
/**
* Is fired when an option is set.
*
* @event TK.Base#set_[option]
*
* @param {mixed} value - The value of the option.
*/
e = "set_"+key;
if (this.has_event_listeners(e))
this.fire_event(e, value, key);
return value;
},
/**
* Sets an option by user interaction. Emits the <code>userset</code>
* event. The <code>userset</code> event can be cancelled (if an event handler
* returns <code>false</code>), in which case the option is not set.
* Returns <code>true</code> if the option was set, <code>false</code>
* otherwise. If the option was set, it will emit a <code>useraction</code> event.
*
* @method TK.Base#userset
*
* @param {string} key - The name of the option.
* @param {mixed} value - The value of the option.
*
* @emits TK.Base#userset
* @emits TK.Base#useraction
*/
userset: function(key, value) {
if (false === this.fire_event("userset", key, value)) return false;
value = this.set(key, value);
this.fire_event("useraction", key, value);
return true;
},
/**
* Delegates all occuring DOM events of a specific DOM node to the widget.
* This way the widget fires e.g. a click event if someone clicks on the
* given DOM node.
*
* @method TK.Base#delegate_events
*
* @param {HTMLElement} element - The element all native events of the widget should be bound to.
*
* @returns {HTMLElement} The element
*
* @emits TK.Base#delegated
*/
delegate_events: function (element) {
var old_target = this.__event_target;
/**
* Is fired when an element is delegated.
*
* @event TK.Base#delegated
*
* @param {HTMLElement|Array} element - The element which receives all
* native DOM events.
* @param {HTMLElement|Array} old_element - The element which previously
* received all native DOM events.
*/
this.fire_event("delegated", element, old_target);
if (old_target) remove_native_events.call(this, old_target);
if (element) add_native_events.call(this, element);
this.__event_target = element;
return element;
},
/**
* Register an event handler.
*
* @method TK.Base#add_event
*
* @param {string} event - The event descriptor.
* @param {Function} func - The function to call when the event happens.
* @param {boolean} prevent - Set to true if the event should prevent the default behavior.
* @param {boolean} stop - Set to true if the event should stop bubbling up the tree.
*/
add_event: function (event, func) {
var ev, tmp;
if (typeof event !== "string")
throw new TypeError("Expected string.");
if (typeof func !== "function")
throw new TypeError("Expected function.");
if (arguments.length !== 2)
throw new Error("Bad number of arguments.");
if (is_native_event(event) && (ev = this.__event_target) && !this.has_event_listeners(event))
TK.add_event_listener(ev, event, this.__native_handler);
ev = this.__events;
add_event(ev, event, func);
},
/**
* Removes the given function from the event queue.
* If it is a native DOM event, it removes the DOM event listener
* as well.
*
* @method TK.Base#remove_event
*
* @param {string} event - The event descriptor.
* @param {Function} fun - The function to remove.
*/
remove_event: function (event, fun) {
remove_event(this.__events, event, fun);
// remove native DOM event listener from __event_target
if (is_native_event(event) && !this.has_event_listeners(event)) {
var ev = this.__event_target;
if (ev) TK.remove_event_listener(ev, event, this.__native_handler);
}
},
/**
* Fires an event.
*
* @method TK.Base#fire_event
*
* @param {string} event - The event descriptor.
* @param {...*} args - Event arguments.
*/
fire_event: function (event) {
var ev;
var args;
var v;
ev = this.__events;
if (ev !== void(0) && (event in ev)) {
ev = ev[event];
args = Array.prototype.slice.call(arguments, 1);
v = dispatch_events(this, ev, args);
if (v !== void(0)) return v;
}
ev = this.static_events;
if (ev !== void(0) && (event in ev)) {
ev = ev[event];
if (args === void(0)) args = Array.prototype.slice.call(arguments, 1);
v = dispatch_events(this, ev, args);
if (v !== void(0)) return v;
}
},
/**
* Test if the event descriptor has some handler functions in the queue.
*
* @method TK.Base#has_event_listeners
*
* @param {string} event - The event desriptor.
*
* @returns {boolean} True if the event has some handler functions in the queue, false if not.
*/
has_event_listeners: has_event_listeners,
/**
* Add multiple event handlers at once, either as dedicated event handlers or a list of event
* descriptors with a single handler function.
*
* @method TK.Base#add_events
*
* @param {Object | Array} events - Object with event descriptors as keys and functions as
* values or Array of event descriptors. The latter requires a handler function as the
* second argument.
* @param {Function} func - A function to add as event handler if the first argument is an
* array of event desriptors.
*/
add_events: function (events, func) {
var i;
if (Array.isArray(events)) {
for (i = 0; i < events.length; i++)
this.add_event(events[i], func);
} else {
for (i in events)
if (events.hasOwnProperty(i))
this.add_event(i, events[i]);
}
},
/**
* Remove multiple event handlers at once, either as dedicated event handlers or a list of
* event descriptors with a single handler function.
*
* @method TK.Base#remove_events
*
* @param {Object | Array} events - Object with event descriptors as keys and functions as
* values or Array of event descriptors. The latter requires the handler function as the
* second argument.
* @param {Function} func - A function to remove from event handler queue if the first
* argument is an array of event desriptors.
*/
remove_events: function (events, func) {
var i;
if (Array.isArray(events)) {
for (i = 0; i < events.length; i++)
this.remove_event(events[i], func);
} else {
for (i in events)
if (events.hasOwnProperty(i))
this.remove_event(i, events[i]);
}
},
/**
* Fires several events.
*
* @method TK.Base#fire_events
*
* @param {Array.<string>} events - A list of event names to fire.
*/
fire_events: function (events) {
for (var i in events) {
if (events.hasOwnProperty(i))
this.fire_event(i, events[i]);
}
}
});
function get_child_options(parent, name, options, config) {
var ret = {};
var key, pref = name+".";
var tmp;
var inherit_options = !!config.inherit_options;
var blacklist_options = config.blacklist_options || [];
if (tmp = config.default_options)
Object.assign(ret, (typeof(tmp) === "function") ? tmp.call(parent) : tmp);
for (key in options) {
if (key.startsWith(pref)) {
ret[key.substr(pref.length)] = options[key];
}
if (inherit_options && blacklist_options.indexOf(tmp) < 0) {
if (key in config.create.prototype._options && !(key in TK["Widget"].prototype._options)) {
ret[key] = options[key];
}
}
}
var map_options = config.map_options;
if (map_options) for (key in map_options) {
if (options[key]) {
ret[map_options[key]] = options[key];
}
}
return ret;
}
function ChildWidget(widget, name, config) {
/**
* Defines a {@link TK.Widget} as a child for another widget. This function
* is used internally to simplify widget definitions. E.g. the {@link TK.Icon} of a
* {@link TK.Button} is defined as a TK.ChildWidget. TK.ChildWidgets
* are created/added after the initialization of the parent widget.
* If not configured explicitly, all options of the child widget can
* be accessed via <code>TK.Widget.options[config.name + "." + option]</code>
* on the parent widget.
*
* @param {TK.Widget} widget - The {@link TK.Widget} to add the TK.ChildWidget to.
* @param {string} name - The identifier of the element inside the parent Element, <code>TK.Widget[config.name]</code>.
* @param {object} config - The configuration of the child element.
*
* @property {TK.Widget} config.create - A TK.Widget class derivate to be used as child widget.
* @property {boolean} [config.fixed] - A fixed child widget cannot be removed after initialization.
* @property {boolean} [config.show=false] - Show/hide a non-fixed child widget on initialization.
* @property {string} [config.option="show_"+config.name] - A custom option of the parent widget
* to determine the visibility of the child element. If this is
* <code>null</code>, <code>TK.Widget.options["show_"+ config.name]</code>
* is used to toggle its visibility. The child element is visible, if
* this options is <code>!== false</code>.
* @property {function} [config.append] - A function overriding the generic
* append mechanism. If not <code>null</code>, this function is
* supposed to take care of adding the child widget to the parent
* widgets DOM. Otherwise the element of the child widget is added
* to the element of the parent widget.
* @property {boolean} [config.inherit_options=false] - Defines if both widgets share the
* same set of options. If <code>true</code>, Setting an option on the
* parent widget also sets the same option on the child widget. If <code>false</code>,
* the options of the child widget can be accessed via <code>options[config.name + "." + option]</code>
* in the parent widget.
* @property {array} [config.map_options=[<Object>]] - A list of options to be mapped between
* parent and child. Keys are the options to be added to the parent, values the options
* names in the child widget.
* names in the parent to which the childrens options (defined as values) are mapped to.
* If one of these options is set
* on the parent widget, it also gets set on the child widget. This is
* a fine-grained version of <code>config.inherit-options</code>.
* @property {boolean} [config.userset_ignore=false] - Do not care about the <code>userset</code>
* event of the parent widget, only keep track of <code>set</code>.
* @property {boolean} [config.userset_delegate=false] - Delegates all user interaction from
* the child to the parent element. If the user triggers an event on
* the child widget, the <code>userset</code> function of the parent
* element is called.
* @property {array} [config.static_events=[]] - An array of static events to be
* added to the parent widget. Each entry is a mapping between
* the name of the event and the callback function.
* @property {boolean} [config.toggle_class=false] - Defines if the parent widget
* receives the class <code>toolkit-has-[name]</code> as soon as
* the child element is shown.
* @property {array<string>} [config.blacklist_options] - Array containing options names
* which are skipped on `inherit_options`.
*
* @class TK.ChildWidget
*
*/
var p = widget.prototype;
var key = config.option || "show_"+name;
var tmp, m;
var static_events = { };
if (!config.userset_ignore)
static_events.userset = (config.inherit_options || config.userset_delegate)
? function(key, value) { this.parent.userset(key, value); return false; }
: function(key, value) { this.parent.userset(name+"."+key, value); return false; };
if (m = config.static_events)
Object.assign(static_events, m);
if (config.create === void(0)) {
TK.warn("'create' is undefined. Skipping ChildWidget ", name);
return;
}
var child = TK.class({
Extends: config.create,
static_events: static_events,
});
/* trigger child widget creation after initialization */
add_static_event(widget, "initialized", function() {
/* we do not want to trash the class cache */
if (!this[name])
{
this[name] = null;
this.set(key, this.options[key]);
}
});
/* clean up on destroy */
add_static_event(widget, "destroy", function() {
if (this[name]) {
this[name].destroy();
this[name] = null;
}
});
var fixed = config.fixed;
var append = config.append;
var toggle_class = !!config.toggle_class;
if (append === void(0)) append = true;
/* child widget creation */
add_static_event(widget, "set_"+key, function(val) {
var C = this[name];
var show = fixed || !!val;
if (show && !C) {
var O = get_child_options(this, name, this.options, config);
if (append === true)
O.container = this.element;
var w = new child(O);
this.add_child(w);
this[name] = w;
if (typeof(append) === "function")
append.call(this);
} else if (!show && C) {
C.destroy();
this[name] = null;
}
if (toggle_class) TK.toggle_class(this.element, "toolkit-has-"+name, show);
this.trigger_resize();
});
var set_cb = function(val, key) {
if (this[name]) this[name].set(key.substr(name.length+1), val);
};
for (tmp in child.prototype._options) {
add_static_event(widget, "set_"+name+"."+tmp, set_cb);
p._options[name+"."+tmp] = child.prototype._options[tmp];
}
/* direct option inherit */
var blacklist_options = config.blacklist_options || [];
if (config.inherit_options) {
set_cb = function(val, key) {
if (this[name]) this[name].set(key, val);
};
for (tmp in child.prototype._options) {
if (tmp in TK["Widget"].prototype._options) continue;
if (blacklist_options.indexOf(tmp) > -1) continue;
add_static_event(widget, "set_"+tmp, set_cb);
if (!p._options[tmp])
p._options[tmp] = child.prototype._options[tmp];
}
}
set_cb = function(key) {
return function(val) {
if (this[name]) this[name].set(key, val);
};
};
if (m = config.map_options) {
for (tmp in m) {
p._options[tmp] = child.prototype._options[m[tmp]];
if (!p.options[tmp])
p.options[tmp] = child.prototype.options[m[tmp]];
add_static_event(widget, "set_"+tmp, set_cb(m[tmp]));
}
}
if (!config.options) {
if (!p._options[key])
p._options[key] = "boolean";
p.options[key] = fixed || !!config.show;
}
}
TK.add_static_event = add_static_event;
TK.ChildWidget = ChildWidget;
function ChildElement(widget, name, config) {
/**
* Creates a HTMLElement as a child for a widget. Is used to simplify
* widget definitions. E.g. the tiny marker used to display the back-end
* value is a simple DIV added using TK.ChildElement. The generic element
* is a DIV added to TK.Widget.element with the class
* <code>toolkit-[name]</code>. Default creating and adding can be
* overwritten with custom callback functions.
*
* @param {TK.Widget} widget - The {@link TK.Widget} to add the TK.ChildElement to.
* @param {string} name - The identifier of the element. It will be prefixed
* by an underscore <code>TK.Widget["_" + config.name]</code>.
* @param {object} config - The configuration of the child element.
*
* @property {boolean} [config.show=false] - Show/hide the child element on initialization.
* @property {string} [config.option="show_"+config.name] - A custom option of the parent widget
* to determine the visibility of the child element. If this is
* <code>null</code>, <code>TK.Widget.options["show_"+ config.name]</code>
* is used to toggle its visibility. The child element is visible, if
* this options is <code>!== false</code>.
* @property {function} [config.display_check] - A function overriding the
* generic <code>show_option</code> behavior. If set, this function
* is called with the value of <code>show_option</code> as argument
* as soon as it gets set and is supposed to return a boolean
* defining the visibility of the element.
* @property {function} [config.append] - A function overriding the generic
* append mechanism. If not <code>null</code>, this function is
* supposed to take care of adding the child element to the parent
* widgets DOM.
* @property {function} [config.create] - A function overriding the generic
* creation mechanism. If not <code>null</code>, this function is
* supposed to create and return a DOM element to be added to the
* parent widget.
* @property {boolean} [config.toggle_class=false] - Defines if the parent widget
* receives the class <code>toolkit-has-[name]</code> as soon as
* the child element is shown.
* @property {array} [config.draw_options] - A list of options of the parent
* widget which are supposed to trigger a check if the element has to
* be added or removed.
* @property {function} [config.draw] - A function to be called on redraw.
*
* @class TK.ChildElement
*
*/
var p = widget.prototype;
var show_option = config.option || ("show_" + name);
var index = "_"+name;
var display_check = config.display_check;
/* This is done to make sure that the object property is created
* inside of the constructor. Otherwise, if we add the widget later
* might be turned into a generic mapping.
*/
add_static_event(widget, "initialize", function() {
this[index] = null;
});
/* trigger child element creation after initialization */
add_static_event(widget, "initialized", function() {
this.set(show_option, this.options[show_option]);
});
/* clean up on destroy */
add_static_event(widget, "destroy", function() {
if (this[index]) {
this[index].remove();
this[index] = null;
}
});
var append = config.append;
var create = config.create;
var toggle_class = !!config.toggle_class;
if (create === void(0)) create = function() { return TK.element("div", "toolkit-"+name); }
if (append === void(0)) append = function() { this.element.appendChild(this[index]); }
add_static_event(widget, "set_"+show_option, function(value) {
var C = this[index];
var show = display_check ? display_check(value) : (value !== false);
if (show && !C) {
C = create.call(this);
this[index] = C;
append.call(this, this.options);
} else if (C && !show) {
this[index] = null;
C.remove();
}
if (toggle_class) TK.toggle_class(this.element, "toolkit-has-"+name, value);
this.trigger_resize();
});
if (config.draw) {
var m = config.draw_options;
if (!m) m = [ show_option ];
else m.push(show_option);
for (var i = 0; i < m.length; i++) {
add_static_event(widget, "set_"+m[i], function() {
if (this.options[show_option] !== false)
this.draw_once(config.draw);
});
}
}
if (p._options[show_option] === void(0)) {
p._options[show_option] = "boolean";
p.options[show_option] = !!config.show;
}
}
TK.ChildElement = ChildElement;
})(this, this.TK||{});