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)
   13 			return vals['_default'];
   14 		else if (lang in vals)
   15 			return vals[lang];
   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 	function _fillEnumSelect(e: HTMLElement|null, val: number|string): void
   38 	{
   39 		let list: NodeListOf<Element>;
   40 		let i: number;
   41 		let v: string|number;
   42 		if (null === e)
   43 			return;
   44 		list = e.getElementsByTagName('option');
   45 		for (i = 0; i < list.length; i++) {
   46 			v = 'number' === typeof val ? 
   47 			     parseInt((<HTMLOptionElement>list[i]).value) :
   48 			     (<HTMLOptionElement>list[i]).value;
   49 			if (val === v)
   50 				_attr(<HTMLOptionElement>list[i], 'selected', 'true');
   51 			else
   52 				_rattr(<HTMLOptionElement>list[i], 'selected');
   53 		}
   54 	}
   55 
   56 	function _attrcl(e: HTMLElement|null, attr: string, name: string, text: string, inc: boolean): void
   57 	{
   58 		let list: HTMLElement[];
   59 		let i: number;
   60 		if (null === e)
   61 			return;
   62 		list = _elemList(e, name, inc);
   63 		for (i = 0; i < list.length; i++)
   64 			_attr(list[i], attr, text);
   65 	}
   66 
   67 	function _elemList(e: HTMLElement|null, cls: string, inc: boolean): HTMLElement[]
   68 	{
   69 		let a: HTMLElement[];
   70 		let list: NodeListOf<Element>;
   71 		let i: number;
   72 		a = [];
   73 		if (null === e)
   74 			return a;
   75 		list = e.getElementsByClassName(cls);
   76 		for (i = 0; i < list.length; i++)
   77 			a.push(<HTMLElement>list[i]);
   78 		if (inc && e.classList.contains(cls))
   79 			a.push(e);
   80 		return a;
   81 	}
   82 
   83 	function _repl(e: HTMLElement|null, text: string): void
   84 	{
   85 		if (null === e)
   86 			return;
   87 		while (e.firstChild)
   88 			e.removeChild(e.firstChild);
   89 		e.appendChild(document.createTextNode(text));
   90 	}
   91 
   92 	function _fillfield(e: HTMLElement|null, strct: string, name: string, funcs: any, obj: any, inc: boolean, cannull: boolean, isblob: boolean, sub: any, isenum: boolean): void
   93 	{
   94 		let i: number;
   95 		let fname: string;
   96 		let list: HTMLElement[];
   97 		fname = strct + '-' + name;
   98 		/* First handle the custom callback. */
   99 		if (typeof funcs !== 'undefined' && 
  100 		    null !== funcs && fname in funcs) {
  101 			if (funcs[fname] instanceof Array) {
  102 				for (i = 0; i < funcs[fname].length; i++)
  103 					funcs[fname][i](e, fname, obj);
  104 			} else {
  105 				funcs[fname](e, fname, obj);
  106 			}
  107 		}
  108 		/* Now handle our has/no null situation. */
  109 		if (cannull) {
  110 			if (null === obj) {
  111 				_hidecl(e, strct + '-has-' + name, inc);
  112 				_showcl(e, strct + '-no-' + name, inc);
  113 			} else {
  114 				_showcl(e, strct + '-has-' + name, inc);
  115 				_hidecl(e, strct + '-no-' + name, inc);
  116 			}
  117 		}
  118 		/* Don't account for blobs any more. */
  119 		if (isblob)
  120 			return;
  121 		/* Don't process null values that can be null. */
  122 		if (cannull && null === obj)
  123 			return;
  124 		/* Non-null non-structs. */
  125 		if (null !== sub) {
  126 			list = _elemList(e, fname + '-obj', inc);
  127 			for (i = 0; i < list.length; i++) {
  128 				sub.fillInner(list[i], funcs);
  129 			}
  130 		} else {
  131 			_replcl(e, fname + '-text', obj, inc);
  132 			list = _elemList(e, fname + '-enum-select', inc);
  133 			for (i = 0; i < list.length; i++) {
  134 				_fillEnumSelect(list[i], obj);
  135 			}
  136 			_attrcl(e, 'value', fname + '-value', obj, inc);
  137 		}
  138 	}
  139 
  140 	function _replcl(e: HTMLElement|null, name: string, text: string, inc: boolean): void
  141 	{
  142 		let list: HTMLElement[];
  143 		let i: number;
  144 		if (null === e)
  145 			return;
  146 		list = _elemList(e, name, inc);
  147 		for (i = 0; i < list.length; i++)
  148 			_repl(list[i], text);
  149 	}
  150 
  151 	function _classadd(e: HTMLElement|null, name: string): HTMLElement|null
  152 	{
  153 		if (null === e)
  154 			return(null);
  155 		if ( ! e.classList.contains(name))
  156 			e.classList.add(name);
  157 		return(e);
  158 	}
  159 
  160 	function _classaddcl(e: HTMLElement|null, name: string, cls: string, inc: boolean): void
  161 	{
  162 		let list: HTMLElement[];
  163 		let i: number;
  164 		if (null === e)
  165 			return;
  166 		list = _elemList(e, name, inc);
  167 		for (i = 0; i < list.length; i++)
  168 			_classadd(list[i], cls);
  169 	}
  170 
  171 	function _hide(e: HTMLElement|null): HTMLElement|null
  172 	{
  173 		if (null === e)
  174 			return null;
  175 		if ( ! e.classList.contains('hide'))
  176 			e.classList.add('hide');
  177 		return e;
  178 	}
  179 
  180 	function _hidecl(e: HTMLElement|null, name: string, inc: boolean): void
  181 	{
  182 		let list: HTMLElement[];
  183 		let i: number;
  184 		if (null === e)
  185 			return;
  186 		list = _elemList(e, name, inc);
  187 		for (i = 0; i < list.length; i++)
  188 			_hide(list[i]);
  189 	}
  190 
  191 	function _show(e: HTMLElement|null): HTMLElement|null
  192 	{
  193 		if (null === e)
  194 			return null;
  195 		if (e.classList.contains('hide'))
  196 			e.classList.remove('hide');
  197 		return e;
  198 	}
  199 
  200 	function _showcl(e: HTMLElement|null, name: string, inc: boolean): void
  201 	{
  202 		let list: HTMLElement[];
  203 		let i: number;
  204 		if (null === e)
  205 			return;
  206 		list = _elemList(e, name, inc);
  207 		for (i = 0; i < list.length; i++)
  208 			_show(list[i]);
  209 	}
  210 
  211 	/**
  212 	 * 
  213 	 * Controlling organisation.<br />
  214 	 * 
  215 	 * @interface kwebapp.companyData
  216 	 */
  217 	export interface companyData
  218 	{
  219 		name: string;
  220 		id: number;
  221 		somenum: number;
  222 	}
  223 
  224 	/**
  225 	 * 
  226 	 * A regular user.<br />
  227 	 * 
  228 	 * @interface kwebapp.userData
  229 	 */
  230 	export interface userData
  231 	{
  232 		company: companyData;
  233 		cid: number;
  234 		sex: number;
  235 		hash: string;
  236 		email: string;
  237 		name: string;
  238 		uid: number;
  239 	}
  240 
  241 	/**
  242 	 * 
  243 	 * Authenticated session.<br />
  244 	 * 
  245 	 * @interface kwebapp.sessionData
  246 	 */
  247 	export interface sessionData
  248 	{
  249 		user: userData;
  250 		userid: number;
  251 		token: number;
  252 		mtime: number;
  253 		id: number;
  254 	}
  255 
  256 	/**
  257 	 * Accepts {@link kwebapp.companyData} for writing into a DOM 
  258 	 * tree.
  259 	 * @param {(kwebapp.companyData|kwebapp.companyData[])} obj - The 
  260 	 * object(s) to write.
  261 	 * @memberof kwebapp
  262 	 * @constructor
  263 	 * @class
  264 	 */
  265 	export class company {
  266 		obj: companyData|companyData[];
  267 		constructor(o: companyData|companyData[]) {
  268 			this.obj = o;
  269 		}
  270 
  271 		/**
  272 		 * Write the {@link kwebapp.companyData} into the given 
  273 		 * HTMLElement in the DOM tree.
  274 		 * If constructed with an array, the first element is 
  275 		 * used.
  276 		 * Elements within (and including) "e" having the following 
  277 		 * classes are manipulated as follows:
  278 		 * <ul>
  279 		 * <li>company-name-enum-select: sets the <code>select</code> 
  280 		 * attribute for <code>&lt;option&gt;</code> values matching 
  281 		 * <i>name</i> under the element</li>
  282 		 * <li>company-name-text: replace contents with <i>name</i> 
  283 		 * data</li>
  284 		 * <li>company-name-value: replace <code>value</code> attribute 
  285 		 * with <i>name</i> data</li>
  286 		 * <li>company-id-enum-select: sets the <code>select</code> 
  287 		 * attribute for <code>&lt;option&gt;</code> values matching 
  288 		 * <i>id</i> under the element</li>
  289 		 * <li>company-id-text: replace contents with <i>id</i> data</li>
  290 		 * <li>company-id-value: replace <code>value</code> attribute 
  291 		 * with <i>id</i> data</li>
  292 		 * <li>company-has-somenum: <code>hide</code> class removed if 
  293 		 * <i>somenum</i> not null, otherwise <code>hide</code> class is 
  294 		 * added</li>
  295 		 * <li>company-no-somenum: <code>hide</code> class added if 
  296 		 * <i>somenum</i> not null, otherwise <code>hide</code> class is 
  297 		 * removed</li>
  298 		 * <li>company-somenum-enum-select: sets the <code>select</code> 
  299 		 * attribute for <code>&lt;option&gt;</code> values matching 
  300 		 * <i>somenum</i> under the element (if non-null)</li>
  301 		 * <li>company-somenum-text: replace contents with <i>somenum</i> 
  302 		 * data (if non-null)</li>
  303 		 * <li>company-somenum-value: replace <code>value</code> 
  304 		 * attribute with <i>somenum</i> data (if non-null)</li>
  305 		 * </ul>
  306 		 * @param {HTMLElement} e - The DOM element.
  307 		 * @param {Object} custom - An optional dictionary of functions 
  308 		 * keyed by structure and field name (e.g., <i>foo</i> structure, 
  309 		 * <i>bar</i> field would be <code>foo-bar</code>). The value is 
  310 		 * a function for custom handling that accepts the "e" value, the 
  311 		 * name of the structure-field, and the value of the structure 
  312 		 * and field.
  313 		 * You may also specify an array of functions instead of a 
  314 		 * singleton.
  315 		 * @function fill
  316 		 * @memberof kwebapp.company#
  317 		 */
  318 		fill(e: HTMLElement|null, custom?: any): void
  319 		{
  320 			this._fill(e, this.obj, true, custom);
  321 		}
  322 
  323 		/**
  324 		 * Like {@link kwebapp.company#fill} but not including the root 
  325 		 * element "e".
  326 		 * @param {HTMLElement} e - The DOM element.
  327 		 * @param {Object} custom - The optional custom handler 
  328 		 * dictionary (see {@link kwebapp.company#fill} for 
  329 		 * details).
  330 		 * @function fillInner
  331 		 * @memberof kwebapp.company#
  332 		 */
  333 		fillInner(e: HTMLElement|null, custom?: any): void
  334 		{
  335 			this._fill(e, this.obj, false, custom);
  336 		}
  337 
  338 		/**
  339 		 * Implements all {@link kwebapp.company#fill} functions.
  340 		 * @param {HTMLElement} e - The DOM element.
  341 		 * @param {kwebapp.companyData|kwebapp.companyData[]|null} o - 
  342 		 * The object (or array) to fill.
  343 		 * @param {Number} inc - Whether to include the root or not when 
  344 		 * processing.
  345 		 * @param {Object} custom - The optional custom handler 
  346 		 * dictionary (see {@link 
  347 		 * kwebapp.company#fill}).
  348 		 * @private
  349 		 * @function _fill
  350 		 * @memberof kwebapp.company#
  351 		 */
  352 		private _fill(e: HTMLElement|null, o: kwebapp.companyData|kwebapp.companyData[]|null, inc: boolean, custom?: any): void
  353 		{
  354 			let i: number;
  355 			if (null === o || null === e)
  356 				return;
  357 			if (o instanceof Array) {
  358 				if (0 === o.length)
  359 					return;
  360 				o = o[0];
  361 			}
  362 			if (typeof custom !== 'undefined' && 
  363 			    null !== custom && 'company' in custom) {
  364 				if (custom['company'] instanceof Array) {
  365 					for (i = 0; i < custom['company'].length; i++)
  366 						custom['company'][i](e, "company", o);
  367 				} else {
  368 					custom['company'](e, "company", o);
  369 				}
  370 			}
  371 			_fillfield(e, 'company', 'name', custom, o.name, inc, false, false, null, false);
  372 			_fillfield(e, 'company', 'id', custom, o.id, inc, false, false, null, false);
  373 			_fillfield(e, 'company', 'somenum', custom, o.somenum, inc, true, false, null, false);
  374 		}
  375 
  376 		/**
  377 		 * Like {@link kwebapp.company#fill} but for an array of {@link 
  378 		 * kwebapp.companyData}.
  379 		 * This will save the first element within "e", remove all 
  380 		 * children of "e", then repeatedly clone the saved element and 
  381 		 * re-append it, filling in the cloned subtree with the array 
  382 		 * (inclusive of the subtree root).
  383 		 * If "e" is not an array, it is construed as an array of one.
  384 		 * If the input array is empty, "e" is hidden by using the 
  385 		 * <code>hide</code> class.
  386 		 * Otherwise, the <code>hide</code> class is removed.
  387 		 * @param {HTMLElement} e - The DOM element.
  388 		 * @param {Object} custom - The optional custom handler 
  389 		 * dictionary (see {@link kwebapp.company#fill}).
  390 		 * @memberof kwebapp.company#
  391 		 * @function fillArray
  392 		 */
  393 		fillArray(e: HTMLElement|null, custom?: any): void
  394 		{
  395 			let j: number;
  396 			let o: kwebapp.companyData|kwebapp.companyData[]|null;
  397 			let cln: any;
  398 			let row: HTMLElement;
  399 			o = this.obj;
  400 			if (null === o || null === e)
  401 				return;
  402 			if ( ! (o instanceof Array)) {
  403 				var ar = [];
  404 				ar.push(o);
  405 				o = ar;
  406 			}
  407 			if (0 === o.length) {
  408 				_hide(e);
  409 				return;
  410 			}
  411 			_show(e);
  412 			row = <HTMLElement>e.children[0];
  413 			if (null === row)
  414 				return;
  415 			e.removeChild(row);
  416 			while (null !== e.firstChild)
  417 				e.removeChild(e.firstChild)
  418 			for (j = 0; j < o.length; j++) {
  419 				cln = <HTMLElement>row.cloneNode(true);
  420 				e.appendChild(cln);
  421 				this._fill(cln, o[j], true, custom);
  422 			}
  423 		}
  424 	}
  425 
  426 	/**
  427 	 * Accepts {@link kwebapp.userData} for writing into a DOM 
  428 	 * tree.
  429 	 * @param {(kwebapp.userData|kwebapp.userData[])} obj - The object(s) 
  430 	 * to write.
  431 	 * @memberof kwebapp
  432 	 * @constructor
  433 	 * @class
  434 	 */
  435 	export class user {
  436 		obj: userData|userData[];
  437 		constructor(o: userData|userData[]) {
  438 			this.obj = o;
  439 		}
  440 
  441 		/**
  442 		 * Write the {@link kwebapp.userData} into the given HTMLElement 
  443 		 * in the DOM tree.
  444 		 * If constructed with an array, the first element is 
  445 		 * used.
  446 		 * Elements within (and including) "e" having the following 
  447 		 * classes are manipulated as follows:
  448 		 * <ul>
  449 		 * <li>user-company-obj: invoke {@link kwebapp.company#fillInner} 
  450 		 * with company data</li>
  451 		 * <li>user-cid-enum-select: sets the <code>select</code> 
  452 		 * attribute for <code>&lt;option&gt;</code> values matching 
  453 		 * <i>cid</i> under the element</li>
  454 		 * <li>user-cid-text: replace contents with <i>cid</i> data</li>
  455 		 * <li>user-cid-value: replace <code>value</code> attribute with 
  456 		 * <i>cid</i> data</li>
  457 		 * <li>user-sex-enum-select: sets the <code>select</code> 
  458 		 * attribute for <code>&lt;option&gt;</code> values matching 
  459 		 * <i>sex</i> under the element</li>
  460 		 * <li>user-sex-text: replace contents with <i>sex</i> data</li>
  461 		 * <li>user-sex-value: replace <code>value</code> attribute with 
  462 		 * <i>sex</i> data</li>
  463 		 * <li>user-hash-enum-select: sets the <code>select</code> 
  464 		 * attribute for <code>&lt;option&gt;</code> values matching 
  465 		 * <i>hash</i> under the element</li>
  466 		 * <li>user-hash-text: replace contents with <i>hash</i> 
  467 		 * data</li>
  468 		 * <li>user-hash-value: replace <code>value</code> attribute with 
  469 		 * <i>hash</i> data</li>
  470 		 * <li>user-email-enum-select: sets the <code>select</code> 
  471 		 * attribute for <code>&lt;option&gt;</code> values matching 
  472 		 * <i>email</i> under the element</li>
  473 		 * <li>user-email-text: replace contents with <i>email</i> 
  474 		 * data</li>
  475 		 * <li>user-email-value: replace <code>value</code> attribute 
  476 		 * with <i>email</i> data</li>
  477 		 * <li>user-name-enum-select: sets the <code>select</code> 
  478 		 * attribute for <code>&lt;option&gt;</code> values matching 
  479 		 * <i>name</i> under the element</li>
  480 		 * <li>user-name-text: replace contents with <i>name</i> 
  481 		 * data</li>
  482 		 * <li>user-name-value: replace <code>value</code> attribute with 
  483 		 * <i>name</i> data</li>
  484 		 * <li>user-uid-enum-select: sets the <code>select</code> 
  485 		 * attribute for <code>&lt;option&gt;</code> values matching 
  486 		 * <i>uid</i> under the element</li>
  487 		 * <li>user-uid-text: replace contents with <i>uid</i> data</li>
  488 		 * <li>user-uid-value: replace <code>value</code> attribute with 
  489 		 * <i>uid</i> data</li>
  490 		 * </ul>
  491 		 * @param {HTMLElement} e - The DOM element.
  492 		 * @param {Object} custom - An optional dictionary of functions 
  493 		 * keyed by structure and field name (e.g., <i>foo</i> structure, 
  494 		 * <i>bar</i> field would be <code>foo-bar</code>). The value is 
  495 		 * a function for custom handling that accepts the "e" value, the 
  496 		 * name of the structure-field, and the value of the structure 
  497 		 * and field.
  498 		 * You may also specify an array of functions instead of a 
  499 		 * singleton.
  500 		 * @function fill
  501 		 * @memberof kwebapp.user#
  502 		 */
  503 		fill(e: HTMLElement|null, custom?: any): void
  504 		{
  505 			this._fill(e, this.obj, true, custom);
  506 		}
  507 
  508 		/**
  509 		 * Like {@link kwebapp.user#fill} but not including the root 
  510 		 * element "e".
  511 		 * @param {HTMLElement} e - The DOM element.
  512 		 * @param {Object} custom - The optional custom handler 
  513 		 * dictionary (see {@link kwebapp.user#fill} for 
  514 		 * details).
  515 		 * @function fillInner
  516 		 * @memberof kwebapp.user#
  517 		 */
  518 		fillInner(e: HTMLElement|null, custom?: any): void
  519 		{
  520 			this._fill(e, this.obj, false, custom);
  521 		}
  522 
  523 		/**
  524 		 * Implements all {@link kwebapp.user#fill} functions.
  525 		 * @param {HTMLElement} e - The DOM element.
  526 		 * @param {kwebapp.userData|kwebapp.userData[]|null} o - The 
  527 		 * object (or array) to fill.
  528 		 * @param {Number} inc - Whether to include the root or not when 
  529 		 * processing.
  530 		 * @param {Object} custom - The optional custom handler 
  531 		 * dictionary (see {@link kwebapp.user#fill}).
  532 		 * @private
  533 		 * @function _fill
  534 		 * @memberof kwebapp.user#
  535 		 */
  536 		private _fill(e: HTMLElement|null, o: kwebapp.userData|kwebapp.userData[]|null, inc: boolean, custom?: any): void
  537 		{
  538 			let i: number;
  539 			if (null === o || null === e)
  540 				return;
  541 			if (o instanceof Array) {
  542 				if (0 === o.length)
  543 					return;
  544 				o = o[0];
  545 			}
  546 			if (typeof custom !== 'undefined' && 
  547 			    null !== custom && 'user' in custom) {
  548 				if (custom['user'] instanceof Array) {
  549 					for (i = 0; i < custom['user'].length; i++)
  550 						custom['user'][i](e, "user", o);
  551 				} else {
  552 					custom['user'](e, "user", o);
  553 				}
  554 			}
  555 			_fillfield(e, 'user', 'company', custom, o.company, inc, false, false, new company(o.company), false);
  556 			_fillfield(e, 'user', 'cid', custom, o.cid, inc, false, false, null, false);
  557 			_fillfield(e, 'user', 'sex', custom, o.sex, inc, false, false, null, true);
  558 			_fillfield(e, 'user', 'hash', custom, o.hash, inc, false, false, null, false);
  559 			_fillfield(e, 'user', 'email', custom, o.email, inc, false, false, null, false);
  560 			_fillfield(e, 'user', 'name', custom, o.name, inc, false, false, null, false);
  561 			_fillfield(e, 'user', 'uid', custom, o.uid, inc, false, false, null, false);
  562 		}
  563 
  564 		/**
  565 		 * Like {@link kwebapp.user#fill} but for an array of {@link 
  566 		 * kwebapp.userData}.
  567 		 * This will save the first element within "e", remove all 
  568 		 * children of "e", then repeatedly clone the saved element and 
  569 		 * re-append it, filling in the cloned subtree with the array 
  570 		 * (inclusive of the subtree root).
  571 		 * If "e" is not an array, it is construed as an array of one.
  572 		 * If the input array is empty, "e" is hidden by using the 
  573 		 * <code>hide</code> class.
  574 		 * Otherwise, the <code>hide</code> class is removed.
  575 		 * @param {HTMLElement} e - The DOM element.
  576 		 * @param {Object} custom - The optional custom handler 
  577 		 * dictionary (see {@link kwebapp.user#fill}).
  578 		 * @memberof kwebapp.user#
  579 		 * @function fillArray
  580 		 */
  581 		fillArray(e: HTMLElement|null, custom?: any): void
  582 		{
  583 			let j: number;
  584 			let o: kwebapp.userData|kwebapp.userData[]|null;
  585 			let cln: any;
  586 			let row: HTMLElement;
  587 			o = this.obj;
  588 			if (null === o || null === e)
  589 				return;
  590 			if ( ! (o instanceof Array)) {
  591 				var ar = [];
  592 				ar.push(o);
  593 				o = ar;
  594 			}
  595 			if (0 === o.length) {
  596 				_hide(e);
  597 				return;
  598 			}
  599 			_show(e);
  600 			row = <HTMLElement>e.children[0];
  601 			if (null === row)
  602 				return;
  603 			e.removeChild(row);
  604 			while (null !== e.firstChild)
  605 				e.removeChild(e.firstChild)
  606 			for (j = 0; j < o.length; j++) {
  607 				cln = <HTMLElement>row.cloneNode(true);
  608 				e.appendChild(cln);
  609 				this._fill(cln, o[j], true, custom);
  610 			}
  611 		}
  612 	}
  613 
  614 	/**
  615 	 * Accepts {@link kwebapp.sessionData} for writing into a DOM 
  616 	 * tree.
  617 	 * @param {(kwebapp.sessionData|kwebapp.sessionData[])} obj - The 
  618 	 * object(s) to write.
  619 	 * @memberof kwebapp
  620 	 * @constructor
  621 	 * @class
  622 	 */
  623 	export class session {
  624 		obj: sessionData|sessionData[];
  625 		constructor(o: sessionData|sessionData[]) {
  626 			this.obj = o;
  627 		}
  628 
  629 		/**
  630 		 * Write the {@link kwebapp.sessionData} into the given 
  631 		 * HTMLElement in the DOM tree.
  632 		 * If constructed with an array, the first element is 
  633 		 * used.
  634 		 * Elements within (and including) "e" having the following 
  635 		 * classes are manipulated as follows:
  636 		 * <ul>
  637 		 * <li>session-user-obj: invoke {@link kwebapp.user#fillInner} 
  638 		 * with user data</li>
  639 		 * <li>session-userid-enum-select: sets the <code>select</code> 
  640 		 * attribute for <code>&lt;option&gt;</code> values matching 
  641 		 * <i>userid</i> under the element</li>
  642 		 * <li>session-userid-text: replace contents with <i>userid</i> 
  643 		 * data</li>
  644 		 * <li>session-userid-value: replace <code>value</code> attribute 
  645 		 * with <i>userid</i> data</li>
  646 		 * <li>session-token-enum-select: sets the <code>select</code> 
  647 		 * attribute for <code>&lt;option&gt;</code> values matching 
  648 		 * <i>token</i> under the element</li>
  649 		 * <li>session-token-text: replace contents with <i>token</i> 
  650 		 * data</li>
  651 		 * <li>session-token-value: replace <code>value</code> attribute 
  652 		 * with <i>token</i> data</li>
  653 		 * <li>session-mtime-enum-select: sets the <code>select</code> 
  654 		 * attribute for <code>&lt;option&gt;</code> values matching 
  655 		 * <i>mtime</i> under the element</li>
  656 		 * <li>session-mtime-text: replace contents with <i>mtime</i> 
  657 		 * data</li>
  658 		 * <li>session-mtime-value: replace <code>value</code> attribute 
  659 		 * with <i>mtime</i> data</li>
  660 		 * <li>session-id-enum-select: sets the <code>select</code> 
  661 		 * attribute for <code>&lt;option&gt;</code> values matching 
  662 		 * <i>id</i> under the element</li>
  663 		 * <li>session-id-text: replace contents with <i>id</i> data</li>
  664 		 * <li>session-id-value: replace <code>value</code> attribute 
  665 		 * with <i>id</i> data</li>
  666 		 * </ul>
  667 		 * @param {HTMLElement} e - The DOM element.
  668 		 * @param {Object} custom - An optional dictionary of functions 
  669 		 * keyed by structure and field name (e.g., <i>foo</i> structure, 
  670 		 * <i>bar</i> field would be <code>foo-bar</code>). The value is 
  671 		 * a function for custom handling that accepts the "e" value, the 
  672 		 * name of the structure-field, and the value of the structure 
  673 		 * and field.
  674 		 * You may also specify an array of functions instead of a 
  675 		 * singleton.
  676 		 * @function fill
  677 		 * @memberof kwebapp.session#
  678 		 */
  679 		fill(e: HTMLElement|null, custom?: any): void
  680 		{
  681 			this._fill(e, this.obj, true, custom);
  682 		}
  683 
  684 		/**
  685 		 * Like {@link kwebapp.session#fill} but not including the root 
  686 		 * element "e".
  687 		 * @param {HTMLElement} e - The DOM element.
  688 		 * @param {Object} custom - The optional custom handler 
  689 		 * dictionary (see {@link kwebapp.session#fill} for 
  690 		 * details).
  691 		 * @function fillInner
  692 		 * @memberof kwebapp.session#
  693 		 */
  694 		fillInner(e: HTMLElement|null, custom?: any): void
  695 		{
  696 			this._fill(e, this.obj, false, custom);
  697 		}
  698 
  699 		/**
  700 		 * Implements all {@link kwebapp.session#fill} functions.
  701 		 * @param {HTMLElement} e - The DOM element.
  702 		 * @param {kwebapp.sessionData|kwebapp.sessionData[]|null} o - 
  703 		 * The object (or array) to fill.
  704 		 * @param {Number} inc - Whether to include the root or not when 
  705 		 * processing.
  706 		 * @param {Object} custom - The optional custom handler 
  707 		 * dictionary (see {@link 
  708 		 * kwebapp.session#fill}).
  709 		 * @private
  710 		 * @function _fill
  711 		 * @memberof kwebapp.session#
  712 		 */
  713 		private _fill(e: HTMLElement|null, o: kwebapp.sessionData|kwebapp.sessionData[]|null, inc: boolean, custom?: any): void
  714 		{
  715 			let i: number;
  716 			if (null === o || null === e)
  717 				return;
  718 			if (o instanceof Array) {
  719 				if (0 === o.length)
  720 					return;
  721 				o = o[0];
  722 			}
  723 			if (typeof custom !== 'undefined' && 
  724 			    null !== custom && 'session' in custom) {
  725 				if (custom['session'] instanceof Array) {
  726 					for (i = 0; i < custom['session'].length; i++)
  727 						custom['session'][i](e, "session", o);
  728 				} else {
  729 					custom['session'](e, "session", o);
  730 				}
  731 			}
  732 			_fillfield(e, 'session', 'user', custom, o.user, inc, false, false, new user(o.user), false);
  733 			_fillfield(e, 'session', 'userid', custom, o.userid, inc, false, false, null, false);
  734 			_fillfield(e, 'session', 'token', custom, o.token, inc, false, false, null, false);
  735 			_fillfield(e, 'session', 'mtime', custom, o.mtime, inc, false, false, null, false);
  736 			_fillfield(e, 'session', 'id', custom, o.id, inc, false, false, null, false);
  737 		}
  738 
  739 		/**
  740 		 * Like {@link kwebapp.session#fill} but for an array of {@link 
  741 		 * kwebapp.sessionData}.
  742 		 * This will save the first element within "e", remove all 
  743 		 * children of "e", then repeatedly clone the saved element and 
  744 		 * re-append it, filling in the cloned subtree with the array 
  745 		 * (inclusive of the subtree root).
  746 		 * If "e" is not an array, it is construed as an array of one.
  747 		 * If the input array is empty, "e" is hidden by using the 
  748 		 * <code>hide</code> class.
  749 		 * Otherwise, the <code>hide</code> class is removed.
  750 		 * @param {HTMLElement} e - The DOM element.
  751 		 * @param {Object} custom - The optional custom handler 
  752 		 * dictionary (see {@link kwebapp.session#fill}).
  753 		 * @memberof kwebapp.session#
  754 		 * @function fillArray
  755 		 */
  756 		fillArray(e: HTMLElement|null, custom?: any): void
  757 		{
  758 			let j: number;
  759 			let o: kwebapp.sessionData|kwebapp.sessionData[]|null;
  760 			let cln: any;
  761 			let row: HTMLElement;
  762 			o = this.obj;
  763 			if (null === o || null === e)
  764 				return;
  765 			if ( ! (o instanceof Array)) {
  766 				var ar = [];
  767 				ar.push(o);
  768 				o = ar;
  769 			}
  770 			if (0 === o.length) {
  771 				_hide(e);
  772 				return;
  773 			}
  774 			_show(e);
  775 			row = <HTMLElement>e.children[0];
  776 			if (null === row)
  777 				return;
  778 			e.removeChild(row);
  779 			while (null !== e.firstChild)
  780 				e.removeChild(e.firstChild)
  781 			for (j = 0; j < o.length; j++) {
  782 				cln = <HTMLElement>row.cloneNode(true);
  783 				e.appendChild(cln);
  784 				this._fill(cln, o[j], true, custom);
  785 			}
  786 		}
  787 	}
  788 
  789 	/**
  790 	 * Birthsex of individual<br />
  791 	 * This object consists of all values for the <i>sex</i> 
  792 	 * enumeration.
  793 	 * It also contains a formatting function designed to work as a 
  794 	 * custom callback for <code>fill</code> functions.
  795 	 * All of these values are static: <strong>do not use the 
  796 	 * constructor</strong>.
  797 	 * @memberof kwebapp
  798 	 * @class
  799 	 */
  800 	export class sex {
  801 		/**
  802 		 * Male<br />
  803 		 * @memberof kwebapp.sex#
  804 		 * @readonly
  805 		 * @const {number} male
  806 		 */
  807 		static readonly male: number = 0;
  808 		/**
  809 		 * Femmale<br />
  810 		 * @memberof kwebapp.sex#
  811 		 * @readonly
  812 		 * @const {number} female
  813 		 */
  814 		static readonly female: number = 1;
  815 		/**
  816 		 * Other<br />
  817 		 * @memberof kwebapp.sex#
  818 		 * @readonly
  819 		 * @const {number} other
  820 		 */
  821 		static readonly other: number = 2;
  822 		/**
  823 		 * Uses the enumeration item's <i>jslabel</i> (or just the name, 
  824 		 * if no <i>jslabel</i> is defined) to format a custom label as 
  825 		 * invoked on an object's <code>fill</code> function. This will 
  826 		 * act on <code>xxx-yyy-label</code> classes, where 
  827 		 * <code>xxx</code> is the structure name and <code>yyy</code> is 
  828 		 * the field name. For example, <code>xxx.fill(e, { 'xxx-yyy': 
  829 		 * kwebapp.sex.format });</code>, where <code>yyy</code> is a 
  830 		 * field of type <i>enum sex</i>.
  831 		 * @static
  832 		 * @function format
  833 		 * @memberof kwebapp.sex#
  834 		 */
  835 		static format(e: HTMLElement, name: string, val: string|null): void
  836 		{
  837 			name += '-label';
  838 			if (null === val) {
  839 				_replcl(e, name, 'not given', false);
  840 				_classaddcl(e, name, 'noanswer', false);
  841 				return;
  842 			}
  843 			switch(parseInt(val)) {
  844 			case sex.male:
  845 				_replcllang(e, name, {_default: ''});
  846 				break;
  847 			case sex.female:
  848 				_replcllang(e, name, {_default: ''});
  849 				break;
  850 			case sex.other:
  851 				_replcllang(e, name, {_default: ''});
  852 				break;
  853 			default:
  854 				console.log('sex.format: unknown value: ' + val);
  855 				_replcl(e, name, '', false);
  856 				break;
  857 			}
  858 		}
  859 	}
  860 
  861 }