1 /**
    2  * Top-level namespace of these objects.
    3  * @namespace
    4  */
    5 namespace kwebapp {
    6 	interface langmap { [lang: string]: string };
    7 
    8 	function _strlang(vals: langmap): string
    9 	{
   10 		let lang: string|null;
   11 		lang = document.documentElement.lang;
   12 		if (null !== lang && lang in vals)
   13 			return vals[lang];
   14 		else if ('_default' in vals)
   15 			return vals['_default'];
   16 		else
   17 			return '';
   18 	}
   19 
   20 	function _replcllang(e: HTMLElement|null, name: string, vals: langmap): void
   21 	{
   22 		_replcl(e, name, _strlang(vals), false);
   23 	}
   24 
   25 	function _attr(e: HTMLElement|null, attr: string, text: string): void
   26 	{
   27 		if (null !== e)
   28 			e.setAttribute(attr, text);
   29 	}
   30 
   31 	function _rattr(e: HTMLElement|null, attr: string): void
   32 	{
   33 		if (null !== e)
   34 			e.removeAttribute(attr);
   35 	}
   36 
   37 	/**
   38 	 * Internal function for checking inputs for all elements of class 
   39 	 * strct-name-value-checked whose value matches the object's value. 
   40 	 * If the object is null, all elements are unchecked.
   41 	 * @param {HTMLElement} e - The root of the DOM tree in which we 
   42 	 * query for elements to fill into.
   43 	 * @param {String} strct - The name of the structure that we're 
   44 	 * filling in.
   45 	 * @param {String} name - The name of the field.
   46 	 * @param {Number|String|null} obj - The data itself.
   47 	 * @param {Boolean} inc - Whether to include the root element in 
   48 	 * looking for elements to fill.
   49 	 * @private
   50 	 * @function _fillValueChecked
   51 	 * @memberof kwebapp
   52 	 */
   53 	function _fillValueChecked(e: HTMLElement, fname: string, val: number|string|null, inc: boolean): void
   54 	{
   55 		let list: HTMLElement[];
   56 		let i: number;
   57 		let valstr: string|null;
   58 		fname += '-value-checked';
   59 		valstr = null === val ? null : 
   60 			("number" === typeof val ? val.toString() : val);
   61 		list = _elemList(e, fname, inc);
   62 		for (i = 0; i < list.length; i++)
   63 			if (valstr === null)
   64 				_rattr(list[i], 'checked');
   65 			else if (valstr === (<HTMLInputElement>list[i]).value)
   66 				_attr(list[i], 'checked', 'true');
   67 			else
   68 				_rattr(list[i], 'checked');
   69 	}
   70 
   71 	/**
   72 	 * Internal function that takes all <code>&lt;option&gt;</code> 
   73 	 * elements in the root and sets or unsets their 
   74 	 * <code>selected</code> status depending upon whether it matches the 
   75 	 * object's value.
   76 	 * @param {HTMLElement} e - The root of the DOM tree in which we 
   77 	 * query for elements to fill into.
   78 	 * @param {Number|String} val - The object's 
   79 	 * value.
   80 	 * @private
   81 	 * @function _fillValueSelect
   82 	 * @memberof kwebapp
   83 	 */
   84 	function _fillValueSelect(e: HTMLElement|null, val: number|string): void
   85 	{
   86 		let list: NodeListOf<Element>;
   87 		let i: number;
   88 		let v: string|number;
   89 		if (null === e)
   90 			return;
   91 		list = e.getElementsByTagName('option');
   92 		for (i = 0; i < list.length; i++) {
   93 			v = 'number' === typeof val ? 
   94 			     parseInt((<HTMLOptionElement>list[i]).value) :
   95 			     (<HTMLOptionElement>list[i]).value;
   96 			if (val === v)
   97 				_attr(<HTMLOptionElement>list[i], 'selected', 'true');
   98 			else
   99 				_rattr(<HTMLOptionElement>list[i], 'selected');
  100 		}
  101 	}
  102 
  103 	function _attrcl(e: HTMLElement|null, attr: string, name: string, text: string, inc: boolean): void
  104 	{
  105 		let list: HTMLElement[];
  106 		let i: number;
  107 		if (null === e)
  108 			return;
  109 		list = _elemList(e, name, inc);
  110 		for (i = 0; i < list.length; i++)
  111 			_attr(list[i], attr, text);
  112 	}
  113 
  114 	function _elemList(e: HTMLElement|null, cls: string, inc: boolean): HTMLElement[]
  115 	{
  116 		let a: HTMLElement[];
  117 		let list: NodeListOf<Element>;
  118 		let i: number;
  119 		a = [];
  120 		if (null === e)
  121 			return a;
  122 		list = e.getElementsByClassName(cls);
  123 		for (i = 0; i < list.length; i++)
  124 			a.push(<HTMLElement>list[i]);
  125 		if (inc && e.classList.contains(cls))
  126 			a.push(e);
  127 		return a;
  128 	}
  129 
  130 	function _repl(e: HTMLElement|null, text: string): void
  131 	{
  132 		if (null === e)
  133 			return;
  134 		while (e.firstChild)
  135 			e.removeChild(e.firstChild);
  136 		e.appendChild(document.createTextNode(text));
  137 	}
  138 
  139 	/**
  140 	 * Internal function for filling in ISO-8601 dates.
  141 	 * @param {HTMLElement} e - The root of the DOM tree in which we 
  142 	 * query for elements to fill into.
  143 	 * @param {String} strct - The name of the structure that we're 
  144 	 * filling in.
  145 	 * @param {String} name - The name of the field.
  146 	 * @param {Number|null} obj - The data itself.
  147 	 * @param {Boolean} inc - Whether to include the root element in 
  148 	 * looking for elements to fill.
  149 	 * @private
  150 	 * @function _fillDateValue
  151 	 * @memberof kwebapp
  152 	 */
  153 	function _fillDateValue(e: HTMLElement, strct: string, name: string, val: number|null, inc: boolean): void
  154 	{
  155 		let fname: string;
  156 		let year: number;
  157 		let mo: number;
  158 		let day: number;
  159 		let full: string;
  160 		let d: Date;
  161 		if (null === val)
  162 			return;
  163 		d = new Date();
  164 		d.setTime(val * 1000);
  165 		year = d.getFullYear();
  166 		mo = d.getMonth() + 1;
  167 		day = d.getDate();
  168 		full = year + '-' +
  169 			(mo < 10 ? '0' : '') + mo + '-' +
  170 			(day < 10 ? '0' : '') + day;
  171 		fname = strct + '-' + name + '-date-value';
  172 		_attrcl(e, 'value', fname, full, inc);
  173 		fname = strct + '-' + name + '-date-text';
  174 		_replcl(e, fname, full, inc);
  175 	}
  176 
  177 	/**
  178 	 * Internal function for checking inputs for all elements of class 
  179 	 * strct-name-bits-checked whose value is the bit-wise AND of the 
  180 	 * object's value. If the object is null, all elements are 
  181 	 * unchecked.
  182 	 * @param {HTMLElement} e - The root of the DOM tree in which we 
  183 	 * query for elements to fill into.
  184 	 * @param {String} strct - The name of the structure that we're 
  185 	 * filling in.
  186 	 * @param {String} name - The name of the field.
  187 	 * @param {Number|null} obj - The data itself.
  188 	 * @param {Boolean} inc - Whether to include the root element in 
  189 	 * looking for elements to fill.
  190 	 * @private
  191 	 * @function _fillBitsChecked
  192 	 * @memberof kwebapp
  193 	 */
  194 	function _fillBitsChecked(e: HTMLElement, strct: string, name: string, val: number|null, inc: boolean): void
  195 	{
  196 		let list: HTMLElement[];
  197 		let fname: string;
  198 		let i: number;
  199 		let v: number;
  200 		fname = strct + '-' + name + '-bits-checked';
  201 		list = _elemList(e, fname, inc);
  202 		for (i = 0; i < list.length; i++) {
  203 			if (val === null) {
  204 				_rattr(list[i], 'checked');
  205 				continue;
  206 			}
  207 			v = parseInt((<HTMLInputElement>list[i]).value);
  208 			if (isNaN(v))
  209 				_rattr(list[i], 'checked');
  210 			else if (0 === v && 0 === val)
  211 				_attr(list[i], 'checked', 'true');
  212 			else if ((1 << (v - 1)) & val)
  213 				_attr(list[i], 'checked', 'true');
  214 			else
  215 				_rattr(list[i], 'checked');
  216 		}
  217 	}
  218 
  219 	/**
  220 	 * Internal function for filling a structure field.
  221 	 * @param {HTMLElement} e - The root of the DOM tree in which we 
  222 	 * query for elements to fill into.
  223 	 * @param {String} strct - The name of the structure that we're 
  224 	 * filling in.
  225 	 * @param {String} name - The name of the field.
  226 	 * @param {kwebapp.DataCallbacks|null} funcs - Custom callback 
  227 	 * functions to invoke on the field.
  228 	 * @param obj - The data itself, which is either a native type or one 
  229 	 * of the data interfaces for an application-specific type.
  230 	 * @param {Boolean} inc - Whether to include the root element in 
  231 	 * looking for elements to fill. Note that nested structures are 
  232 	 * alwyas filled non-inclusively.
  233 	 * @param {Boolean} cannull - Whether the data object might be 
  234 	 * null.
  235 	 * @param {Boolean} isblob - Whether the data object is a 
  236 	 * blob.
  237 	 * @param sub - If the data object is a nested structure interface, 
  238 	 * this is the allocated class of that interface.
  239 	 * @private
  240 	 * @function _fillfield
  241 	 * @memberof kwebapp
  242 	 */
  243 	function _fillfield(e: HTMLElement, strct: string, name: string, funcs: DataCallbacks|null, obj: any, inc: boolean, cannull: boolean, isblob: boolean, sub: any): void
  244 	{
  245 		let i: number;
  246 		let fname: string;
  247 		let list: HTMLElement[];
  248 		fname = strct + '-' + name;
  249 		/* First handle the custom callback. */
  250 		if (null !== funcs && fname in funcs) {
  251 			if (funcs[fname] instanceof Array) {
  252 				for (i = 0; i < funcs[fname].length; i++)
  253 					funcs[fname][i](e, fname, obj);
  254 			} else {
  255 				funcs[fname](e, fname, obj);
  256 			}
  257 		}
  258 		/* Now handle our has/no null situation. */
  259 		if (cannull) {
  260 			if (null === obj) {
  261 				_hidecl(e, strct + '-has-' + name, inc);
  262 				_showcl(e, strct + '-no-' + name, inc);
  263 			} else {
  264 				_showcl(e, strct + '-has-' + name, inc);
  265 				_hidecl(e, strct + '-no-' + name, inc);
  266 			}
  267 		}
  268 		/* Don't account for blobs any more. */
  269 		if (isblob)
  270 			return;
  271 		/* Don't process null values that can be null. */
  272 		if (cannull && null === obj)
  273 			return;
  274 		/* Non-null non-structs. */
  275 		if (null !== sub) {
  276 			list = _elemList(e, fname + '-obj', inc);
  277 			for (i = 0; i < list.length; i++) {
  278 				sub.fillInner(list[i], funcs);
  279 			}
  280 		} else {
  281 			list = _elemList(e, fname + '-enum-select', inc);
  282 			for (i = 0; i < list.length; i++) {
  283 				_fillValueSelect(list[i], obj);
  284 			}
  285 			_replcl(e, fname + '-text', obj, inc);
  286 			_attrcl(e, 'value', fname + '-value', obj, inc);
  287 			_fillValueChecked(e, fname, obj, inc);
  288 		}
  289 	}
  290 
  291 	function _replcl(e: HTMLElement|null, name: string, text: string, inc: boolean): void
  292 	{
  293 		let list: HTMLElement[];
  294 		let i: number;
  295 		if (null === e)
  296 			return;
  297 		list = _elemList(e, name, inc);
  298 		for (i = 0; i < list.length; i++)
  299 			_repl(list[i], text);
  300 	}
  301 
  302 	function _classadd(e: HTMLElement|null, name: string): HTMLElement|null
  303 	{
  304 		if (null === e)
  305 			return(null);
  306 		if ( ! e.classList.contains(name))
  307 			e.classList.add(name);
  308 		return(e);
  309 	}
  310 
  311 	function _classaddcl(e: HTMLElement|null, name: string, cls: string, inc: boolean): void
  312 	{
  313 		let list: HTMLElement[];
  314 		let i: number;
  315 		if (null === e)
  316 			return;
  317 		list = _elemList(e, name, inc);
  318 		for (i = 0; i < list.length; i++)
  319 			_classadd(list[i], cls);
  320 	}
  321 
  322 	function _hide(e: HTMLElement|null): HTMLElement|null
  323 	{
  324 		if (null === e)
  325 			return null;
  326 		if ( ! e.classList.contains('hide'))
  327 			e.classList.add('hide');
  328 		return e;
  329 	}
  330 
  331 	function _hidecl(e: HTMLElement|null, name: string, inc: boolean): void
  332 	{
  333 		let list: HTMLElement[];
  334 		let i: number;
  335 		if (null === e)
  336 			return;
  337 		list = _elemList(e, name, inc);
  338 		for (i = 0; i < list.length; i++)
  339 			_hide(list[i]);
  340 	}
  341 
  342 	function _show(e: HTMLElement|null): HTMLElement|null
  343 	{
  344 		if (null === e)
  345 			return null;
  346 		if (e.classList.contains('hide'))
  347 			e.classList.remove('hide');
  348 		return e;
  349 	}
  350 
  351 	function _showcl(e: HTMLElement|null, name: string, inc: boolean): void
  352 	{
  353 		let list: HTMLElement[];
  354 		let i: number;
  355 		if (null === e)
  356 			return;
  357 		list = _elemList(e, name, inc);
  358 		for (i = 0; i < list.length; i++)
  359 			_show(list[i]);
  360 	}
  361 
  362 	/**
  363 	 * All possible callback functions for passing to the "custom" 
  364 	 * associative array when filling in DOM trees.
  365 	 * @interface kwebapp.DataCallbacks
  366 	 */
  367 	export type DCbstring = (e: HTMLElement, name: string, val: string) => void;
  368 	export type DCbstringNull = (e: HTMLElement, name: string, val: string|null) => void;
  369 	export type DCbnumber = (e: HTMLElement, name: string, val: number) => void;
  370 	export type DCbnumberNull = (e: HTMLElement, name: string, val: number|null) => void;
  371 	export type DCbStructcompany = (e: HTMLElement, name: string, val: kwebapp.companyData|null) => void;
  372 	export type DCbStructuser = (e: HTMLElement, name: string, val: kwebapp.userData|null) => void;
  373 	export type DCbStructsession = (e: HTMLElement, name: string, val: kwebapp.sessionData|null) => void;
  374 
  375 	export interface DataCallbacks
  376 	{
  377 		'company'?: DCbStructcompany|DCbStructcompany[];
  378 		'company-name'?: DCbstring|DCbstring[];
  379 		'company-id'?: DCbnumber|DCbnumber[];
  380 		'company-somenum'?: DCbnumberNull|DCbnumberNull[];
  381 		'user'?: DCbStructuser|DCbStructuser[];
  382 		'user-company'?: DCbStructcompany|DCbStructcompany[];
  383 		'user-cid'?: DCbnumber|DCbnumber[];
  384 		'user-sex'?: DCbnumber|DCbnumber[];
  385 		'user-hash'?: DCbstring|DCbstring[];
  386 		'user-email'?: DCbstring|DCbstring[];
  387 		'user-name'?: DCbstring|DCbstring[];
  388 		'user-uid'?: DCbnumber|DCbnumber[];
  389 		'session'?: DCbStructsession|DCbStructsession[];
  390 		'session-user'?: DCbStructuser|DCbStructuser[];
  391 		'session-userid'?: DCbnumber|DCbnumber[];
  392 		'session-token'?: DCbnumber|DCbnumber[];
  393 		'session-mtime'?: DCbnumber|DCbnumber[];
  394 		'session-id'?: DCbnumber|DCbnumber[];
  395 	}
  396 
  397 	/**
  398 	 * 
  399 	 * Controlling organisation.<br/>
  400 	 * 
  401 	 * @interface kwebapp.companyData
  402 	 */
  403 	export interface companyData
  404 	{
  405 		name: string;
  406 		id: number;
  407 		somenum: number;
  408 	}
  409 
  410 	/**
  411 	 * 
  412 	 * A regular user.<br/>
  413 	 * 
  414 	 * @interface kwebapp.userData
  415 	 */
  416 	export interface userData
  417 	{
  418 		company: companyData;
  419 		cid: number;
  420 		sex: number;
  421 		hash: string;
  422 		email: string;
  423 		name: string;
  424 		uid: number;
  425 	}
  426 
  427 	/**
  428 	 * 
  429 	 * Authenticated session.<br/>
  430 	 * 
  431 	 * @interface kwebapp.sessionData
  432 	 */
  433 	export interface sessionData
  434 	{
  435 		user: userData;
  436 		userid: number;
  437 		token: number;
  438 		mtime: number;
  439 		id: number;
  440 	}
  441 
  442 	/**
  443 	 * Accepts {@link kwebapp.companyData} for writing into a DOM 
  444 	 * tree.
  445 	 * @param {(kwebapp.companyData|kwebapp.companyData[])} obj - The 
  446 	 * object(s) to write.
  447 	 * @memberof kwebapp
  448 	 * @constructor
  449 	 * @class
  450 	 */
  451 	export class company {
  452 		obj: companyData|companyData[];
  453 		constructor(o: companyData|companyData[]) {
  454 			this.obj = o;
  455 		}
  456 
  457 		/**
  458 		 * Write the {@link kwebapp.companyData} into the given 
  459 		 * HTMLElement in the DOM tree.
  460 		 * If constructed with an array, the first element is 
  461 		 * used.
  462 		 * Elements within (and including) "e" having the following 
  463 		 * classes are manipulated as follows:
  464 		 * <ul>
  465 		 * <li>company-name-enum-select: sets the <code>select</code> 
  466 		 * attribute for <code>&lt;option&gt;</code> values matching 
  467 		 * <i>name</i> under the element</li>
  468 		 * <li>company-name-value-checked: sets the <code>checked</code> 
  469 		 * attribute under the element matching the input</li>
  470 		 * <li>company-name-text: replace contents with <i>name</i> 
  471 		 * data</li>
  472 		 * <li>company-name-value: replace <code>value</code> attribute 
  473 		 * with <i>name</i> data</li>
  474 		 * <li>company-id-enum-select: sets the <code>select</code> 
  475 		 * attribute for <code>&lt;option&gt;</code> values matching 
  476 		 * <i>id</i> under the element</li>
  477 		 * <li>company-id-value-checked: sets the <code>checked</code> 
  478 		 * attribute under the element matching the input</li>
  479 		 * <li>company-id-text: replace contents with <i>id</i> data</li>
  480 		 * <li>company-id-value: replace <code>value</code> attribute 
  481 		 * with <i>id</i> data</li>
  482 		 * <li>company-has-somenum: <code>hide</code> class removed if 
  483 		 * <i>somenum</i> not null, otherwise <code>hide</code> class is 
  484 		 * added</li>
  485 		 * <li>company-no-somenum: <code>hide</code> class added if 
  486 		 * <i>somenum</i> not null, otherwise <code>hide</code> class is 
  487 		 * removed</li>
  488 		 * <li>company-somenum-enum-select: sets the <code>select</code> 
  489 		 * attribute for <code>&lt;option&gt;</code> values matching 
  490 		 * <i>somenum</i> under the element (if non-null)</li>
  491 		 * <li>company-somenum-value-checked: sets the 
  492 		 * <code>checked</code> attribute under the element matching the 
  493 		 * input (if non-null)</li>
  494 		 * <li>company-somenum-text: replace contents with <i>somenum</i> 
  495 		 * data (if non-null)</li>
  496 		 * <li>company-somenum-value: replace <code>value</code> 
  497 		 * attribute with <i>somenum</i> data (if non-null)</li>
  498 		 * </ul>
  499 		 * @param {HTMLElement} e - The DOM element.
  500 		 * @param {kwebapp.DataCallbacks} custom - The optional 
  501 		 * dictionary of functions keyed by structure and field name 
  502 		 * (e.g., <i>foo</i> structure, <i>bar</i> field would be 
  503 		 * <code>foo-bar</code>). The value is a function for custom 
  504 		 * handling that accepts the "e" value, the name of the 
  505 		 * structure-field, and the value of the structure and field.
  506 		 * You may also specify an array of functions instead of a 
  507 		 * singleton.
  508 		 * @function fill
  509 		 * @memberof kwebapp.company#
  510 		 */
  511 		fill(e: HTMLElement|null, custom?: DataCallbacks|null): void
  512 		{
  513 			this._fill(e, this.obj, true, custom);
  514 		}
  515 
  516 		/**
  517 		 * Like {@link kwebapp.company#fill} but not including the root 
  518 		 * element "e".
  519 		 * @param {HTMLElement} e - The DOM element.
  520 		 * @param {kwebapp.DataCallbacks} custom - The optional custom 
  521 		 * handler dictionary (see {@link kwebapp.company#fill} for 
  522 		 * details).
  523 		 * @function fillInner
  524 		 * @memberof kwebapp.company#
  525 		 */
  526 		fillInner(e: HTMLElement|null, custom?: DataCallbacks|null): void
  527 		{
  528 			this._fill(e, this.obj, false, custom);
  529 		}
  530 
  531 		/**
  532 		 * Implements all {@link kwebapp.company#fill} functions.
  533 		 * @param {HTMLElement} e - The DOM element.
  534 		 * @param {kwebapp.companyData|kwebapp.companyData[]|null} o - 
  535 		 * The object (or array) to fill.
  536 		 * @param {Number} inc - Whether to include the root or not when 
  537 		 * processing.
  538 		 * @param {kwebapp.DataCallbacks} custom - The optional custom 
  539 		 * handler dictionary (see {@link 
  540 		 * kwebapp.company#fill}).
  541 		 * @private
  542 		 * @function _fill
  543 		 * @memberof kwebapp.company#
  544 		 */
  545 		private _fill(e: HTMLElement|null, o: kwebapp.companyData|kwebapp.companyData[]|null, inc: boolean, custom?: DataCallbacks|null): void
  546 		{
  547 			let i: number;
  548 			if (null === o || null === e)
  549 				return;
  550 			if (o instanceof Array) {
  551 				if (0 === o.length)
  552 					return;
  553 				o = o[0];
  554 			}
  555 			if (typeof custom === 'undefined')
  556 				custom = null;
  557 			if (null !== custom && 'company' in custom) {
  558 				if (custom['company'] instanceof Array) {
  559 					for (i = 0; i < custom['company']!.length; i++)
  560 						(<kwebapp.DCbStructcompany[]>custom['company'])[i](e, 'company', o);
  561 				} else {
  562 					(<kwebapp.DCbStructcompany>custom['company'])(e, 'company', o);
  563 				}
  564 			}
  565 			_fillfield(e, 'company', 'name', custom, o.name, inc, false, false, null);
  566 			_fillfield(e, 'company', 'id', custom, o.id, inc, false, false, null);
  567 			_fillfield(e, 'company', 'somenum', custom, o.somenum, inc, true, false, null);
  568 		}
  569 
  570 		/**
  571 		 * Like {@link kwebapp.company#fill} but for an array of {@link 
  572 		 * kwebapp.companyData}.
  573 		 * This will save the first element within "e", remove all 
  574 		 * children of "e", then repeatedly clone the saved element and 
  575 		 * re-append it, filling in the cloned subtree with the array 
  576 		 * (inclusive of the subtree root).
  577 		 * If "e" is not an array, it is construed as an array of one.
  578 		 * If the input array is empty, "e" is hidden by using the 
  579 		 * <code>hide</code> class.
  580 		 * Otherwise, the <code>hide</code> class is removed.
  581 		 * @param {HTMLElement} e - The DOM element.
  582 		 * @param {kwebapp.DataCallbacks} custom - The optional custom 
  583 		 * handler dictionary (see {@link 
  584 		 * kwebapp.company#fill}).
  585 		 * @memberof kwebapp.company#
  586 		 * @function fillArray
  587 		 */
  588 		fillArray(e: HTMLElement|null, custom?: DataCallbacks): void
  589 		{
  590 			let j: number;
  591 			let o: kwebapp.companyData|kwebapp.companyData[]|null;
  592 			let cln: HTMLElement;
  593 			let ar: kwebapp.companyData[];
  594 			let row: HTMLElement;
  595 			o = this.obj;
  596 			if (null === o || null === e)
  597 				return;
  598 			if ( ! (o instanceof Array)) {
  599 				ar = [];
  600 				ar.push(o);
  601 				o = ar;
  602 			}
  603 			if (0 === o.length) {
  604 				_hide(e);
  605 				return;
  606 			}
  607 			_show(e);
  608 			row = <HTMLElement>e.children[0];
  609 			if (null === row)
  610 				return;
  611 			e.removeChild(row);
  612 			while (null !== e.firstChild)
  613 				e.removeChild(e.firstChild)
  614 			for (j = 0; j < o.length; j++) {
  615 				cln = <HTMLElement>row.cloneNode(true);
  616 				e.appendChild(cln);
  617 				this._fill(cln, o[j], true, custom);
  618 			}
  619 		}
  620 	}
  621 
  622 	/**
  623 	 * Accepts {@link kwebapp.userData} for writing into a DOM 
  624 	 * tree.
  625 	 * @param {(kwebapp.userData|kwebapp.userData[])} obj - The object(s) 
  626 	 * to write.
  627 	 * @memberof kwebapp
  628 	 * @constructor
  629 	 * @class
  630 	 */
  631 	export class user {
  632 		obj: userData|userData[];
  633 		constructor(o: userData|userData[]) {
  634 			this.obj = o;
  635 		}
  636 
  637 		/**
  638 		 * Write the {@link kwebapp.userData} into the given HTMLElement 
  639 		 * in the DOM tree.
  640 		 * If constructed with an array, the first element is 
  641 		 * used.
  642 		 * Elements within (and including) "e" having the following 
  643 		 * classes are manipulated as follows:
  644 		 * <ul>
  645 		 * <li>user-company-obj: invoke {@link kwebapp.company#fillInner} 
  646 		 * with company data</li>
  647 		 * <li>user-cid-enum-select: sets the <code>select</code> 
  648 		 * attribute for <code>&lt;option&gt;</code> values matching 
  649 		 * <i>cid</i> under the element</li>
  650 		 * <li>user-cid-value-checked: sets the <code>checked</code> 
  651 		 * attribute under the element matching the input</li>
  652 		 * <li>user-cid-text: replace contents with <i>cid</i> data</li>
  653 		 * <li>user-cid-value: replace <code>value</code> attribute with 
  654 		 * <i>cid</i> data</li>
  655 		 * <li>user-sex-enum-select: sets the <code>select</code> 
  656 		 * attribute for <code>&lt;option&gt;</code> values matching 
  657 		 * <i>sex</i> under the element</li>
  658 		 * <li>user-sex-value-checked: sets the <code>checked</code> 
  659 		 * attribute under the element matching the input</li>
  660 		 * <li>user-sex-text: replace contents with <i>sex</i> data</li>
  661 		 * <li>user-sex-value: replace <code>value</code> attribute with 
  662 		 * <i>sex</i> data</li>
  663 		 * <li>user-hash-enum-select: sets the <code>select</code> 
  664 		 * attribute for <code>&lt;option&gt;</code> values matching 
  665 		 * <i>hash</i> under the element</li>
  666 		 * <li>user-hash-value-checked: sets the <code>checked</code> 
  667 		 * attribute under the element matching the input</li>
  668 		 * <li>user-hash-text: replace contents with <i>hash</i> 
  669 		 * data</li>
  670 		 * <li>user-hash-value: replace <code>value</code> attribute with 
  671 		 * <i>hash</i> data</li>
  672 		 * <li>user-email-enum-select: sets the <code>select</code> 
  673 		 * attribute for <code>&lt;option&gt;</code> values matching 
  674 		 * <i>email</i> under the element</li>
  675 		 * <li>user-email-value-checked: sets the <code>checked</code> 
  676 		 * attribute under the element matching the input</li>
  677 		 * <li>user-email-text: replace contents with <i>email</i> 
  678 		 * data</li>
  679 		 * <li>user-email-value: replace <code>value</code> attribute 
  680 		 * with <i>email</i> data</li>
  681 		 * <li>user-name-enum-select: sets the <code>select</code> 
  682 		 * attribute for <code>&lt;option&gt;</code> values matching 
  683 		 * <i>name</i> under the element</li>
  684 		 * <li>user-name-value-checked: sets the <code>checked</code> 
  685 		 * attribute under the element matching the input</li>
  686 		 * <li>user-name-text: replace contents with <i>name</i> 
  687 		 * data</li>
  688 		 * <li>user-name-value: replace <code>value</code> attribute with 
  689 		 * <i>name</i> data</li>
  690 		 * <li>user-uid-enum-select: sets the <code>select</code> 
  691 		 * attribute for <code>&lt;option&gt;</code> values matching 
  692 		 * <i>uid</i> under the element</li>
  693 		 * <li>user-uid-value-checked: sets the <code>checked</code> 
  694 		 * attribute under the element matching the input</li>
  695 		 * <li>user-uid-text: replace contents with <i>uid</i> data</li>
  696 		 * <li>user-uid-value: replace <code>value</code> attribute with 
  697 		 * <i>uid</i> data</li>
  698 		 * </ul>
  699 		 * @param {HTMLElement} e - The DOM element.
  700 		 * @param {kwebapp.DataCallbacks} custom - The optional 
  701 		 * dictionary of functions keyed by structure and field name 
  702 		 * (e.g., <i>foo</i> structure, <i>bar</i> field would be 
  703 		 * <code>foo-bar</code>). The value is a function for custom 
  704 		 * handling that accepts the "e" value, the name of the 
  705 		 * structure-field, and the value of the structure and field.
  706 		 * You may also specify an array of functions instead of a 
  707 		 * singleton.
  708 		 * @function fill
  709 		 * @memberof kwebapp.user#
  710 		 */
  711 		fill(e: HTMLElement|null, custom?: DataCallbacks|null): void
  712 		{
  713 			this._fill(e, this.obj, true, custom);
  714 		}
  715 
  716 		/**
  717 		 * Like {@link kwebapp.user#fill} but not including the root 
  718 		 * element "e".
  719 		 * @param {HTMLElement} e - The DOM element.
  720 		 * @param {kwebapp.DataCallbacks} custom - The optional custom 
  721 		 * handler dictionary (see {@link kwebapp.user#fill} for 
  722 		 * details).
  723 		 * @function fillInner
  724 		 * @memberof kwebapp.user#
  725 		 */
  726 		fillInner(e: HTMLElement|null, custom?: DataCallbacks|null): void
  727 		{
  728 			this._fill(e, this.obj, false, custom);
  729 		}
  730 
  731 		/**
  732 		 * Implements all {@link kwebapp.user#fill} functions.
  733 		 * @param {HTMLElement} e - The DOM element.
  734 		 * @param {kwebapp.userData|kwebapp.userData[]|null} o - The 
  735 		 * object (or array) to fill.
  736 		 * @param {Number} inc - Whether to include the root or not when 
  737 		 * processing.
  738 		 * @param {kwebapp.DataCallbacks} custom - The optional custom 
  739 		 * handler dictionary (see {@link 
  740 		 * kwebapp.user#fill}).
  741 		 * @private
  742 		 * @function _fill
  743 		 * @memberof kwebapp.user#
  744 		 */
  745 		private _fill(e: HTMLElement|null, o: kwebapp.userData|kwebapp.userData[]|null, inc: boolean, custom?: DataCallbacks|null): void
  746 		{
  747 			let i: number;
  748 			if (null === o || null === e)
  749 				return;
  750 			if (o instanceof Array) {
  751 				if (0 === o.length)
  752 					return;
  753 				o = o[0];
  754 			}
  755 			if (typeof custom === 'undefined')
  756 				custom = null;
  757 			if (null !== custom && 'user' in custom) {
  758 				if (custom['user'] instanceof Array) {
  759 					for (i = 0; i < custom['user']!.length; i++)
  760 						(<kwebapp.DCbStructuser[]>custom['user'])[i](e, 'user', o);
  761 				} else {
  762 					(<kwebapp.DCbStructuser>custom['user'])(e, 'user', o);
  763 				}
  764 			}
  765 			_fillfield(e, 'user', 'company', custom, o.company, inc, false, false, new company(o.company));
  766 			_fillfield(e, 'user', 'cid', custom, o.cid, inc, false, false, null);
  767 			_fillfield(e, 'user', 'sex', custom, o.sex, inc, false, false, null);
  768 			_fillfield(e, 'user', 'hash', custom, o.hash, inc, false, false, null);
  769 			_fillfield(e, 'user', 'email', custom, o.email, inc, false, false, null);
  770 			_fillfield(e, 'user', 'name', custom, o.name, inc, false, false, null);
  771 			_fillfield(e, 'user', 'uid', custom, o.uid, inc, false, false, null);
  772 		}
  773 
  774 		/**
  775 		 * Like {@link kwebapp.user#fill} but for an array of {@link 
  776 		 * kwebapp.userData}.
  777 		 * This will save the first element within "e", remove all 
  778 		 * children of "e", then repeatedly clone the saved element and 
  779 		 * re-append it, filling in the cloned subtree with the array 
  780 		 * (inclusive of the subtree root).
  781 		 * If "e" is not an array, it is construed as an array of one.
  782 		 * If the input array is empty, "e" is hidden by using the 
  783 		 * <code>hide</code> class.
  784 		 * Otherwise, the <code>hide</code> class is removed.
  785 		 * @param {HTMLElement} e - The DOM element.
  786 		 * @param {kwebapp.DataCallbacks} custom - The optional custom 
  787 		 * handler dictionary (see {@link kwebapp.user#fill}).
  788 		 * @memberof kwebapp.user#
  789 		 * @function fillArray
  790 		 */
  791 		fillArray(e: HTMLElement|null, custom?: DataCallbacks): void
  792 		{
  793 			let j: number;
  794 			let o: kwebapp.userData|kwebapp.userData[]|null;
  795 			let cln: HTMLElement;
  796 			let ar: kwebapp.userData[];
  797 			let row: HTMLElement;
  798 			o = this.obj;
  799 			if (null === o || null === e)
  800 				return;
  801 			if ( ! (o instanceof Array)) {
  802 				ar = [];
  803 				ar.push(o);
  804 				o = ar;
  805 			}
  806 			if (0 === o.length) {
  807 				_hide(e);
  808 				return;
  809 			}
  810 			_show(e);
  811 			row = <HTMLElement>e.children[0];
  812 			if (null === row)
  813 				return;
  814 			e.removeChild(row);
  815 			while (null !== e.firstChild)
  816 				e.removeChild(e.firstChild)
  817 			for (j = 0; j < o.length; j++) {
  818 				cln = <HTMLElement>row.cloneNode(true);
  819 				e.appendChild(cln);
  820 				this._fill(cln, o[j], true, custom);
  821 			}
  822 		}
  823 	}
  824 
  825 	/**
  826 	 * Accepts {@link kwebapp.sessionData} for writing into a DOM 
  827 	 * tree.
  828 	 * @param {(kwebapp.sessionData|kwebapp.sessionData[])} obj - The 
  829 	 * object(s) to write.
  830 	 * @memberof kwebapp
  831 	 * @constructor
  832 	 * @class
  833 	 */
  834 	export class session {
  835 		obj: sessionData|sessionData[];
  836 		constructor(o: sessionData|sessionData[]) {
  837 			this.obj = o;
  838 		}
  839 
  840 		/**
  841 		 * Write the {@link kwebapp.sessionData} into the given 
  842 		 * HTMLElement in the DOM tree.
  843 		 * If constructed with an array, the first element is 
  844 		 * used.
  845 		 * Elements within (and including) "e" having the following 
  846 		 * classes are manipulated as follows:
  847 		 * <ul>
  848 		 * <li>session-user-obj: invoke {@link kwebapp.user#fillInner} 
  849 		 * with user data</li>
  850 		 * <li>session-userid-enum-select: sets the <code>select</code> 
  851 		 * attribute for <code>&lt;option&gt;</code> values matching 
  852 		 * <i>userid</i> under the element</li>
  853 		 * <li>session-userid-value-checked: sets the 
  854 		 * <code>checked</code> attribute under the element matching the 
  855 		 * input</li>
  856 		 * <li>session-userid-text: replace contents with <i>userid</i> 
  857 		 * data</li>
  858 		 * <li>session-userid-value: replace <code>value</code> attribute 
  859 		 * with <i>userid</i> data</li>
  860 		 * <li>session-token-enum-select: sets the <code>select</code> 
  861 		 * attribute for <code>&lt;option&gt;</code> values matching 
  862 		 * <i>token</i> under the element</li>
  863 		 * <li>session-token-value-checked: sets the <code>checked</code> 
  864 		 * attribute under the element matching the input</li>
  865 		 * <li>session-token-text: replace contents with <i>token</i> 
  866 		 * data</li>
  867 		 * <li>session-token-value: replace <code>value</code> attribute 
  868 		 * with <i>token</i> data</li>
  869 		 * <li>session-mtime-enum-select: sets the <code>select</code> 
  870 		 * attribute for <code>&lt;option&gt;</code> values matching 
  871 		 * <i>mtime</i> under the element</li>
  872 		 * <li>session-mtime-value-checked: sets the <code>checked</code> 
  873 		 * attribute under the element matching the input</li>
  874 		 * <li>session-mtime-text: replace contents with <i>mtime</i> 
  875 		 * data</li>
  876 		 * <li>session-mtime-value: replace <code>value</code> attribute 
  877 		 * with <i>mtime</i> data</li>
  878 		 * <li>session-mtime-date-value: set the element's 
  879 		 * <code>value</code> to the ISO-8601 date format of the 
  880 		 * data</li>
  881 		 * <li>session-id-enum-select: sets the <code>select</code> 
  882 		 * attribute for <code>&lt;option&gt;</code> values matching 
  883 		 * <i>id</i> under the element</li>
  884 		 * <li>session-id-value-checked: sets the <code>checked</code> 
  885 		 * attribute under the element matching the input</li>
  886 		 * <li>session-id-text: replace contents with <i>id</i> data</li>
  887 		 * <li>session-id-value: replace <code>value</code> attribute 
  888 		 * with <i>id</i> data</li>
  889 		 * </ul>
  890 		 * @param {HTMLElement} e - The DOM element.
  891 		 * @param {kwebapp.DataCallbacks} custom - The optional 
  892 		 * dictionary of functions keyed by structure and field name 
  893 		 * (e.g., <i>foo</i> structure, <i>bar</i> field would be 
  894 		 * <code>foo-bar</code>). The value is a function for custom 
  895 		 * handling that accepts the "e" value, the name of the 
  896 		 * structure-field, and the value of the structure and field.
  897 		 * You may also specify an array of functions instead of a 
  898 		 * singleton.
  899 		 * @function fill
  900 		 * @memberof kwebapp.session#
  901 		 */
  902 		fill(e: HTMLElement|null, custom?: DataCallbacks|null): void
  903 		{
  904 			this._fill(e, this.obj, true, custom);
  905 		}
  906 
  907 		/**
  908 		 * Like {@link kwebapp.session#fill} but not including the root 
  909 		 * element "e".
  910 		 * @param {HTMLElement} e - The DOM element.
  911 		 * @param {kwebapp.DataCallbacks} custom - The optional custom 
  912 		 * handler dictionary (see {@link kwebapp.session#fill} for 
  913 		 * details).
  914 		 * @function fillInner
  915 		 * @memberof kwebapp.session#
  916 		 */
  917 		fillInner(e: HTMLElement|null, custom?: DataCallbacks|null): void
  918 		{
  919 			this._fill(e, this.obj, false, custom);
  920 		}
  921 
  922 		/**
  923 		 * Implements all {@link kwebapp.session#fill} functions.
  924 		 * @param {HTMLElement} e - The DOM element.
  925 		 * @param {kwebapp.sessionData|kwebapp.sessionData[]|null} o - 
  926 		 * The object (or array) to fill.
  927 		 * @param {Number} inc - Whether to include the root or not when 
  928 		 * processing.
  929 		 * @param {kwebapp.DataCallbacks} custom - The optional custom 
  930 		 * handler dictionary (see {@link 
  931 		 * kwebapp.session#fill}).
  932 		 * @private
  933 		 * @function _fill
  934 		 * @memberof kwebapp.session#
  935 		 */
  936 		private _fill(e: HTMLElement|null, o: kwebapp.sessionData|kwebapp.sessionData[]|null, inc: boolean, custom?: DataCallbacks|null): void
  937 		{
  938 			let i: number;
  939 			if (null === o || null === e)
  940 				return;
  941 			if (o instanceof Array) {
  942 				if (0 === o.length)
  943 					return;
  944 				o = o[0];
  945 			}
  946 			if (typeof custom === 'undefined')
  947 				custom = null;
  948 			if (null !== custom && 'session' in custom) {
  949 				if (custom['session'] instanceof Array) {
  950 					for (i = 0; i < custom['session']!.length; i++)
  951 						(<kwebapp.DCbStructsession[]>custom['session'])[i](e, 'session', o);
  952 				} else {
  953 					(<kwebapp.DCbStructsession>custom['session'])(e, 'session', o);
  954 				}
  955 			}
  956 			_fillfield(e, 'session', 'user', custom, o.user, inc, false, false, new user(o.user));
  957 			_fillfield(e, 'session', 'userid', custom, o.userid, inc, false, false, null);
  958 			_fillfield(e, 'session', 'token', custom, o.token, inc, false, false, null);
  959 			_fillfield(e, 'session', 'mtime', custom, o.mtime, inc, false, false, null);
  960 			_fillDateValue(e, 'session', 'mtime', o.mtime, inc);
  961 			_fillfield(e, 'session', 'id', custom, o.id, inc, false, false, null);
  962 		}
  963 
  964 		/**
  965 		 * Like {@link kwebapp.session#fill} but for an array of {@link 
  966 		 * kwebapp.sessionData}.
  967 		 * This will save the first element within "e", remove all 
  968 		 * children of "e", then repeatedly clone the saved element and 
  969 		 * re-append it, filling in the cloned subtree with the array 
  970 		 * (inclusive of the subtree root).
  971 		 * If "e" is not an array, it is construed as an array of one.
  972 		 * If the input array is empty, "e" is hidden by using the 
  973 		 * <code>hide</code> class.
  974 		 * Otherwise, the <code>hide</code> class is removed.
  975 		 * @param {HTMLElement} e - The DOM element.
  976 		 * @param {kwebapp.DataCallbacks} custom - The optional custom 
  977 		 * handler dictionary (see {@link 
  978 		 * kwebapp.session#fill}).
  979 		 * @memberof kwebapp.session#
  980 		 * @function fillArray
  981 		 */
  982 		fillArray(e: HTMLElement|null, custom?: DataCallbacks): void
  983 		{
  984 			let j: number;
  985 			let o: kwebapp.sessionData|kwebapp.sessionData[]|null;
  986 			let cln: HTMLElement;
  987 			let ar: kwebapp.sessionData[];
  988 			let row: HTMLElement;
  989 			o = this.obj;
  990 			if (null === o || null === e)
  991 				return;
  992 			if ( ! (o instanceof Array)) {
  993 				ar = [];
  994 				ar.push(o);
  995 				o = ar;
  996 			}
  997 			if (0 === o.length) {
  998 				_hide(e);
  999 				return;
 1000 			}
 1001 			_show(e);
 1002 			row = <HTMLElement>e.children[0];
 1003 			if (null === row)
 1004 				return;
 1005 			e.removeChild(row);
 1006 			while (null !== e.firstChild)
 1007 				e.removeChild(e.firstChild)
 1008 			for (j = 0; j < o.length; j++) {
 1009 				cln = <HTMLElement>row.cloneNode(true);
 1010 				e.appendChild(cln);
 1011 				this._fill(cln, o[j], true, custom);
 1012 			}
 1013 		}
 1014 	}
 1015 
 1016 	/**
 1017 	 * Birthsex of individual<br/>
 1018 	 * This object consists of all values for the <i>sex</i> 
 1019 	 * enumeration.
 1020 	 * It also contains a formatting function designed to work as a 
 1021 	 * custom callback for <code>fill</code> functions.
 1022 	 * All of these values are static: <strong>do not use the 
 1023 	 * constructor</strong>.
 1024 	 * @memberof kwebapp
 1025 	 * @class
 1026 	 */
 1027 	export class sex {
 1028 		/**
 1029 		 * Male<br/>
 1030 		 * @memberof kwebapp.sex#
 1031 		 * @readonly
 1032 		 * @const {number} male
 1033 		 */
 1034 		static readonly male: number = 0;
 1035 		/**
 1036 		 * Female<br/>
 1037 		 * @memberof kwebapp.sex#
 1038 		 * @readonly
 1039 		 * @const {number} female
 1040 		 */
 1041 		static readonly female: number = 1;
 1042 		/**
 1043 		 * Other<br/>
 1044 		 * @memberof kwebapp.sex#
 1045 		 * @readonly
 1046 		 * @const {number} other
 1047 		 */
 1048 		static readonly other: number = 2;
 1049 		/**
 1050 		 * Uses the enumeration item's <i>jslabel</i> (or just the name, 
 1051 		 * if no <i>jslabel</i> is defined) to format a custom label as 
 1052 		 * invoked on an object's <code>fill</code> function. This will 
 1053 		 * act on <code>xxx-yyy-label</code> classes, where 
 1054 		 * <code>xxx</code> is the structure name and <code>yyy</code> is 
 1055 		 * the field name. For example, <code>xxx.fill(e, { 'xxx-yyy': 
 1056 		 * kwebapp.sex.format });</code>, where <code>yyy</code> is a 
 1057 		 * field of type <i>enum sex</i>.
 1058 		 * @static
 1059 		 * @function format
 1060 		 * @param {HTMLElement} e - The DOM element.
 1061 		 * @param {String} name - The class name root.
 1062 		 * @param {Number} v - The enumeration value.
 1063 		 * @memberof kwebapp.sex#
 1064 		 */
 1065 		static format(e: HTMLElement, name: string, v: number|null): void
 1066 		{
 1067 			name += '-label';
 1068 			if (null === v) {
 1069 				_replcl(e, name, 'not given', false);
 1070 				_classaddcl(e, name, 'noanswer', false);
 1071 				return;
 1072 			}
 1073 			switch(v) {
 1074 			case sex.male:
 1075 				_replcllang(e, name, {_default: 'male'});
 1076 				break;
 1077 			case sex.female:
 1078 				_replcllang(e, name, {_default: 'female'});
 1079 				break;
 1080 			case sex.other:
 1081 				_replcllang(e, name, {_default: 'other'});
 1082 				break;
 1083 			default:
 1084 				console.log('sex.format: unknown value: ' + v);
 1085 				_replcl(e, name, '', false);
 1086 				break;
 1087 			}
 1088 		}
 1089 	}
 1090 
 1091 }