Przeglądaj źródła

exported TableAjax js widget from TableAjax

Piotr Labudda 8 lat temu
rodzic
commit
ef498edfc3
2 zmienionych plików z 2998 dodań i 2985 usunięć
  1. 11 2985
      SE/se-lib/TableAjax.php
  2. 2987 0
      SE/se-lib/TableAjax.php.TableAjax.js

+ 11 - 2985
SE/se-lib/TableAjax.php

@@ -387,7 +387,7 @@ class TableAjax extends ViewAjax {
 			.AjaxTableCont-mapEditorContainer .ui-resizable-s { background-color:#ddd; }
 			.AjaxTableCont-mapEditorContainer .ui-resizable-s:hover { background-color:#888; }
 		");
-		echo UI::h('script', [ 'src' => "static/vendor.js" ]);
+		echo UI::h('script', [ 'src' => "static/vendor.js" ]); // window.p5VendorJs: {React, ReactDOM, createReactClass, Redux}
 		echo UI::h('script', [ 'src' => "static/p5UI/buildDom.js" ]);
 		$_rendered = true;
 	}
@@ -516,2990 +516,16 @@ class TableAjax extends ViewAjax {
 				'URL_GET_TABLE_TOOLS' => $this->showTableTools,
 			]);
 		}
-		?>
-		<script>
-(function($, undefined) {
-	var TableAjax = function() {
-		var priv = {}; //private api
-		var publ = {}; //public api
-
-		priv.options = {};
-		var defaults = {
-			namespace: '',
-			url: '', //webservice url
-			urlData: '', //webservice params
-			urlPost: false, //use POST instead of GET
-			debug: false, //prints debug info to console
-			filter: false, //show filter row
-			filterInit: false,// init filters data
-			forceFilterInit: false,
-			columnPicker: false, //show columnpicker
-			checkboxes: false, //show body checkboxes
-			actions: '', //holds action links
-			pageSize: 10, //current pagesize
-			pageSizes: [10, 20, 30, 40, 50, 'All'], //available pagesizes
-			hidePagerOnEmpty: false, //removes pager if no rows
-			types: { //type specific options
-				string: {},
-				number: {},
-				bool: {},
-				date: {}
-			},
-			rowFunctions: false,
-			tblFunctions: false,
-			specialFilterFunctions: false,
-			filtersClean: false,
-			router: false,
-			longDesc: false,
-			exportFields: [],
-			mapEditor: false,	// mapEditor visible or not
-			mapEditorContainer: 'window' // 'window' or 'dock'
-		};
-
-		var _uiNodeCont; // container holding table
-		var _uiNode$Table; // the table node
-		var _head; // table header
-		var _headSort; // table header sorting row
-		var _headFilter; // table header columns filter row
-		var _headSpecialFilter; // table header special filter row
-		var _bodyNode; // table body
-		var _body; // TODO: table body need render?
-		var _foot; // table footer
-		var _inlineEditBox; // inline edit box with form
-		var _popoverCell; // inline popover cell
-		var _popoverCellCurrent; // inline popover cell
-		var _popoverCellAjaxXhr;
-		var _mapEditor; // map editor node
-		var _mapEditorWrap; // map editor wrapper node
-		var _mapEditorDialog; // map editor jquery ui dialog node
-
-		var _state = {};// state - to replace variables below (date, ...)
-		var _data; //columns and rows
-		var _totalPages; // total pages
-		var _currDpOp; // clicked datetimepicker operator
-		var _filterFields = {}; // array with filter DOM elements
-		var _filterTimeout; // timer for delayed filtering
-		var _uniqueCols = {}; // array with checked rows /// TODO: mv to _state.
-		var _checkToggleChecked = false; // check-all toggle state
-		var _exportFieldsSelect = {};
-
-		var _tableWidth;
-
-		var _fieldWidgets = {};
-
-		/*
-		 initialize the plugin.
-		 */
-		priv.init = function() {
-			_uiNodeCont = priv.options.id;
-			_state = {};// init state
-			_state.page = 1;
-			_state.specialFilters = {};
-			_state.filters = {};
-			_state.filters.currSortCol = '';
-			_state.filters.currSortFlip = false;
-			_state.filters.filterCols = {};
-			priv.initEvents();
-			priv.initialRender();// set up _uiNode$...
-			priv.options.types.string = ((priv.options.types || {}).string || {});
-			priv.options.types.number = ((priv.options.types || {}).number || {});
-			priv.options.types.bool = ((priv.options.types || {}).bool || {});
-			priv.options.types.date = ((priv.options.types || {}).date || {});
-			if (priv.options.mapEditorContainer != 'dock'
-					&& priv.options.mapEditorContainer != 'window') {
-				priv.options.mapEditorContainer = 'window';
-			}
-
-			if (priv.options.filterInit) {
-				priv.setStateFilters(priv.options.filterInit);
-			}
-
-			if (priv.options.exportFields) {
-				$.each(priv.options.exportFields, function(ind, col) {
-					_exportFieldsSelect[col] = true;
-				});
-			}
-
-			if (location.hash) {
-				priv.options.router()
-			} else {
-				priv.update(priv.options.router)
-			}
-
-			if (typeof priv.options.router == "function") {
-				$(window).bind('hashchange', function() {
-					priv.options.router.apply(this);
-				});
-			}
-		};
-
-		priv.initEvents = function() {
-			jQuery(_uiNodeCont).on('TableAjax:render', priv.onRender);
-			jQuery(window).on('resize', priv.onWindowResize)
-		};
-
-		priv.onRender = function(e) {
-			if (priv.options.debug) console.log('onRender.arguments:', arguments.length, arguments, 'e:', e);
-			// console.log("priv.onRender arguments:", arguments);
-			// console.trace();
-			if (arguments.length > 1) {
-				for (var i=1; i<arguments.length; i++) {
-					switch (arguments[i]) {
-						case 'head': _head = undefined; break;
-						case 'body': _body = undefined; break;
-						case 'foot': _foot = undefined; break;
-						case 'foot_pagination': {
-								priv.renderFooterInfo();
-								priv.renderFooterPagination();
-								priv.renderFooterPageSizes();
-							}
-							break;
-						case 'footer__toolbar__info': priv.renderFooterInfo(); break;
-						case 'footer__toolbar__pagination': priv.renderFooterPagination(); break;
-						case 'footer__toolbar__pagesizes': priv.renderFooterPageSizes(); break;
-						case 'foot__columnPicker': priv.renderFooterColumnPicker(); break;
-						case 'head__specialFilters': priv.renderHeadSpecialFilters(); break;
-					}
-				}
-				priv.renderTable();
-				priv.onWindowResize();
-
-				{// activate saveBtn if some columns hidden
-					var selectedFilter = priv.modelColFilter_getSelected();
-					if (null === selectedFilter) {
-						priv.modelColFilter_getSaveBtn().prop("disabled", false)
-					} else {
-						priv.modelColFilter_getSaveBtn().prop("disabled", true)
-					}
-				}
-			}
-		};
-
-		priv.onWindowResize = function() {
-			var stickyCol1Width = (12 + 3) * 4 + 2 * 5 + 1;
-			var stickyCol2Width = 50 + 2 * 5 + 1;
-			var contW = jQuery(_uiNodeCont).parent().width();
-			var colsW = stickyCol1Width + stickyCol2Width;
-			jQuery(_uiNodeCont).css({width:'' + (contW - colsW) + 'px'})//, marginLeft:'' + colsW + 'px', overflowX:'scroll', overflowY:'visible', paddingBottom:'1px'});
-
-			var wh = jQuery(window).height();   // returns height of browser viewport
-			var dh = jQuery(document).height(); // returns height of HTML document (same as pageHeight in screenshot)
-			if (dh > wh) {
-				jQuery(_uiNodeCont).doubleScroll()
-				jQuery(_uiNodeCont).prev('.doubleScroll-scroll-wrapper').css({float: 'right', display:'block'})
-			} else {
-				jQuery(_uiNodeCont).prev('.doubleScroll-scroll-wrapper').css({display:'none'})
-			}
-		}
-
-		priv.initialRender = function() {
-			_uiNode$Table = $('<table class="AjaxTable table table-striped table-hover table-bordered table-condensed"></table>').appendTo(_uiNodeCont);
-				_head = $('<thead></thead>').prependTo(_uiNode$Table);
-					_headSort = $('<tr class="sort tblAjax__head__sort"></tr>').prependTo(_head);
-					_headFilter = $('<tr class="filter tblAjax__head__filter"></tr>').appendTo(_head);
-				_bodyNode = $('<tbody></tbody>').insertAfter(_head);
-			$('<tfoot></tfoot>').insertAfter(_bodyNode);
-			_foot = $('<div class="foot tblAjax__footer"></div>').insertAfter(_uiNodeCont);
-				var footToolbar = $('<div class="btn-toolbar tblAjax__footer__toolbar"></div>').appendTo(_foot);
-					$('<span class="tblAjax__footer__toolbar__info"></span>').appendTo(footToolbar);
-					$('<span class="tblAjax__footer__toolbar__pagination"></span>').appendTo(footToolbar);
-					$('<span class="tblAjax__footer__toolbar__pagesizes"></span>').appendTo(footToolbar);
-					$('<span class="tblAjax__footer__toolbar__columnPicker"></span>').appendTo(footToolbar);
-					$('<span class="tblAjax__footer__toolbar__functions"></span>').appendTo(footToolbar);
-					$('<span class="tblAjax__footer__toolbar__export"></span>').appendTo(footToolbar);
-			_mapEditorWrap = $('<div class="mapEditor" style="display:none"></div>').insertAfter(_foot);
-				_mapEditor = $('<div class="mapEditor-map"></div>').appendTo(_mapEditorWrap);
-			_popoverCell = $('<div class="popoverCell" style="display:none"></div>').insertAfter(_foot);
-			$('<div class="tblAjax__inlineEditBox"></div>').insertAfter(_foot);
-			$('<div class="btn-toolbar tblAjax__head__specialFilter"></div>').insertBefore(_uiNodeCont);
-
-			priv.renderInlineEditBox();// .tblAjax__inlineEditBox
-
-			_foot = _head = _body = _headSort = _headFilter = undefined;// TODO: refactor
-		};
-
-		priv.renderTable = function() {
-			if (!_state.colsSorted) return;
-			if (!_head) {
-				_headSort = _headFilter = _headSpecialFilter = undefined;
-				_head = _uiNode$Table.find('thead');
-			}
-			if (!_headSort) priv.renderTableTheadSort();
-			if (!_headFilter && priv.options.filter) {
-				priv.renderTableTheadFilter();
-				_headFilter = true;
-			}
-			if (!_headSpecialFilter && priv.options.specialFilterFunctions) {
-				priv.renderHeadSpecialFilters();
-				_headSpecialFilter = true;
-			}
-			if (!_body) {
-				_uiNode$Table.find('tbody').remove();
-				_bodyNode = $('<tbody></tbody>').insertAfter(_head);
-				//_bodyNode.on('change', '.unique', priv.rowChecked);
-
-				// find out what rows to show next...
-				var rowsAdded = 0;
-
-				// slice out the chunk of data we need and create rows
-				$.each(_data.rows, function(index, props) {
-					var rowNode = priv.renderRow(props);
-					if (rowNode) rowNode.appendTo(_bodyNode);
-					rowsAdded++;
-					if (rowsAdded >= priv.options.pageSize) { // enough rows created
-						return false;
-					}
-				});
-
-				if (_state.page == 1) {
-					_totalPages = Math.ceil(_data.total / priv.options.pageSize);
-				}
-
-				if (_state.page == _totalPages) { // pad with empty rows if we're at last page.
-					while (rowsAdded < priv.options.pageSize) {
-						var rowNode = priv.renderRowEmptyNode();
-						if (rowNode) rowNode.appendTo(_bodyNode);
-						rowsAdded++;
-					}
-				}
-			}
-
-			if (!_foot) {
-				priv.renderTableTfoot();
-				priv.renderFooter();
-			}
-			_foot = $(_uiNodeCont).next('.foot');
-
-			var stickyCol1Width = (12 + 3) * 4 + 2 * 5 + 1;
-			var stickyCol2Width = 50 + 2 * 5 + 1;
-			if (undefined === _tableWidth) {
-				_tableWidth = true;
-				var contW = jQuery(_uiNodeCont).width();
-				var th1 = _uiNode$Table.find('.stickyCol1:first');
-				var th2 = th1.next();
-				var th1W = th1.innerWidth();
-				var th2W = 50 + 2 * 5;//th2.innerWidth();
-				var colsW = stickyCol1Width + stickyCol2Width;
-				jQuery(_uiNodeCont).css({width:'' + (contW - colsW) + 'px', marginLeft:'' + colsW + 'px', overflowX:'scroll', overflowY:'visible', paddingBottom:'1px'});
-			}
-			_uiNode$Table.find('.stickyCol1').css({
-				position: 'absolute',
-				left: '0',
-				top: 'auto',
-				width: '' + stickyCol1Width + 'px',
-			});
-			//_uiNode$Table.find('.filter').find('.stickyCol1').css({height:'34px'});
-			var sortStickyColHeight = _uiNode$Table.find('.sort').find('th:last').outerHeight();
-			_uiNode$Table.find('.sort').find('.stickyCol1').css({height: sortStickyColHeight + 'px'});
-			_uiNode$Table.find('.sort').find('.stickyCol2').css({height: sortStickyColHeight + 'px'});
-			_uiNode$Table.find('.stickyCol2').css({
-				position: 'absolute',
-				left: '' + (stickyCol1Width) + 'px',
-				top: 'auto',
-				width: '' + stickyCol2Width + 'px',
-				borderRight: '1px solid silver',
-				overflow: 'hidden'
-			});
-
-			if (_data.total == 0 && priv.options.hidePagerOnEmpty) {
-				$('.btn-toolbar', _foot).remove();
-			}
-			if (priv.options.debug) console.log('table created');
-			if (typeof priv.options.tableCreated == 'function') {
-				priv.options.tableCreated.call(_uiNode$Table.get(0), {table: _uiNode$Table.get(0)});
-			}
-		};
-
-		// TODO: priv.renderFieldWidget_InlineEdit = function(fldName, value) {}
-		// TODO: priv.renderFieldWidget_FormField = function(fldName, value) {}
-
-		// @usage: priv.renderFieldWidget_TableCell(fldName, val, rowPK, row)
-		priv.renderFieldWidget_TableCell = function(fldName, value, rowPK, row) {
-			var renderFieldAsTableCell = priv.getFieldWidget_TableCell(fldName);
-			if (!renderFieldAsTableCell) return null;
-			return renderFieldAsTableCell(value, rowPK, row);
-		};
-
-		priv.registerFieldWidget = function(type, fldName, fieldProps) {
-			var fieldWidget = null;
-			switch (type) {
-				case "date":// @return Element - TODO: not used yet
-					fieldWidget = (function(fldName, fieldProps) {
-						var _fieldName = fldName,
-								_fieldProps = fieldProps;
-						// console.log('FieldWidget: generate function to render field('+_fieldName+') ', fieldProps);
-						return function(val, fieldPK, row) {
-							// console.log('FieldWidget: pk('+fieldPK+') run function to render field('+_fieldName+') with value('+val+') ', fieldProps);
-							return val;
-						}
-					}(fldName, fieldProps));
-					break;
-				case "bool":// @return Element - TODO: not used yet
-					fieldWidget = (function(fldName, fieldProps) {
-						var _fieldName = fldName,
-								_fieldProps = fieldProps;
-						// console.log('FieldWidget: generate function to render field('+_fieldName+') ', fieldProps);
-						return function(val, fieldPK, row) {
-							// console.log('FieldWidget: pk('+fieldPK+') run function to render field('+_fieldName+') with value('+val+') ', fieldProps);
-							fldNode = document.createElement('input');
-							fldNode.setAttribute('type', 'checkbox');
-							if (val) fldNode.setAttribute('checked', 'checked');
-							fldNode.setAttribute('disabled', 'disabled');
-							fldNode.appendChild(document.createTextNode(val));
-							return fldNode;
-						}
-					}(fldName, fieldProps));
-					break;
-				case "number":// @return String - TODO: not used yet
-					fieldWidget = (function(fldName, fieldProps) {
-						var _fieldName = fldName,
-								_fieldProps = fieldProps,
-								_fractionDigits = _.get(_fieldProps, 'fractionDigits', 2);
-						// console.log('FieldWidget: generate function to render field('+_fieldName+') _fractionDigits('+_fractionDigits+')', fieldProps);
-						return function(val, fieldPK, row) {
-							// console.log('FieldWidget: pk('+fieldPK+') run function to render field('+_fieldName+') with value('+val+') ', fieldProps);
-							if ('' === val) return null; // is nullable
-							if ('*****' === val) return val
-							// enumeration
-							// fractionDigits
-							// maxExclusive
-							// maxInclusive
-							// minExclusive
-							// minInclusive
-							// pattern
-							// totalDigits
-							// whiteSpace
-							val = Number(val);
-							return ((val || 0) % 1 === 0)? val : val.toFixed(_fractionDigits);
-						}
-					}(fldName, fieldProps));
-					break;
-				case "string":// @return String
-					fieldWidget = (function(fldName, fieldProps) {
-						var _fieldName = fldName,
-								_fieldProps = fieldProps,
-								_format = _.get(_fieldProps, 'format', '{0}');
-						// console.log('FieldWidget: generate function to render field('+_fieldName+') ', fieldProps);
-						return function(val, fieldPK, row) {
-							// console.log('FieldWidget: pk('+fieldPK+') run function to render field('+_fieldName+') with value('+val+') ', fieldProps);
-							return p5Utils__format(_format, [val]);
-						}
-					}(fldName, fieldProps));
-					break;
-				case "p5:price":// @return String
-					fieldWidget = (function(fldName, fieldProps) {
-						var _fieldName = fldName,
-								_fieldProps = fieldProps,
-								_format = _.get(_fieldProps, 'format', '<span style="text-align:right">{0}</span>');
-						// console.log('FieldWidget: generate function to render field('+_fieldName+') ', fieldProps);
-						return function(val, fieldPK, row) {
-							// console.log('FieldWidget: pk('+fieldPK+') run function to render field('+_fieldName+') with value('+val+') ', fieldProps);
-							return p5Utils__format(_format, [val]);
-						}
-					}(fldName, fieldProps));
-					break;
-				case "p5:string":// @return String
-					fieldWidget = (function(fldName, fieldProps) {
-						var _fieldName = fldName;
-						var _fieldProps = fieldProps;
-						var _formatByValue = _.get(_fieldProps, 'formatByValue', null);
-						var _aliasMap = _.get(_fieldProps, 'aliasMap', null);
-						return function(val, fieldPK, row) {
-							if (_formatByValue && val in _formatByValue) {
-								val = p5Utils__format(_formatByValue[val], [val])
-								// console.log('p5:string value', val, '_aliasMap', _aliasMap)
-								if (_aliasMap) {
-									Object.keys(_aliasMap).map(function (mapKey) {
-										var mapField = _aliasMap[mapKey]
-										if (undefined !== row[mapField]) {
-											val = val.replace(new RegExp('\{' + mapKey + '\}', 'g'), row[mapField]);
-										}
-									})
-								}
-							}
-							return val;
-						}
-					}(fldName, fieldProps));
-					break;
-				case "p5:www_link":// @return Element
-					fieldWidget = (function(fldName, fieldProps) {
-						var _fieldName = fldName,
-								_fieldProps = fieldProps;
-						//  console.log('FieldWidget: generate function to render field('+_fieldName+') ', fieldProps);
-						return function(val, fieldPK, row) {
-							//  console.log('FieldWidget: pk('+fieldPK+') run function to render field('+_fieldName+') with value('+val+') ', fieldProps);
-							var link = '',
-									a = null,
-									dbg = false;
-							if (!val.length) return '';
-							if ('index.php' == val.substr(0, 9) || 'procesy5.php' == val.substr(0, 12)) {
-								link = window.location.protocol + '//' + window.location.hostname
-								link += window.location.pathname.split('/').slice(0, -1).join('/')
-								link += '/' + val
-							} else {
-								if ('http' == val.substr(0, 4)) link = val
-								//else if ('ftp' == val.substr(0, 3))
-								else link = 'http://' + val
-							}
-
-							var linkErrors = validate({www_link: link}, {www_link: {url: true}});
-							if (linkErrors) {
-								// if(dbg)console.log('linkErrors for (' + link +')', linkErrors);
-								return val;
-							}
-							fldNode = document.createElement('a');
-							fldNode.setAttribute('href', link);
-							fldNode.setAttribute('target', '_blank');
-							fldNode.appendChild(document.createTextNode(val));
-							return fldNode;
-						}
-					}(fldName, fieldProps));
-					break;
-				case "special":
-					fieldWidget = (function(fldName, fieldProps) {
-						var _fieldName = fldName;
-						var _fieldProps = fieldProps;
-						var _format = _.get(_fieldProps, 'format', '{0}');
-						var _aliasMap = _.get(_fieldProps, 'aliasMap', null);
-						// console.log('FieldWidget: generate function to render field('+_fieldName+') ', fieldProps);
-						return function(val, fieldPK, row) {
-							// console.log('FieldWidget: pk('+fieldPK+') run function to render field('+_fieldName+') with value('+val+') ', fieldProps, 'row', row);
-							var format = _format;
-							if (_aliasMap) {
-								Object.keys(_aliasMap).map(function (mapKey) {
-									var mapField = _aliasMap[mapKey]
-									if (undefined !== row[mapField]) {
-										format = format.replace(new RegExp('\{' + mapKey + '\}', 'g'), row[mapField]);
-									}
-									// console.log('FieldWidget: pk('+fieldPK+') run function to render field('+_fieldName+') with value('+val+') _aliasMap:', _aliasMap, 'row['+mapField+']', row[mapField], 'converted val:', val);
-								})
-							}
-							return p5Utils__format(format, [val]);
-						}
-					}(fldName, fieldProps));
-					break;
-				case "simpleLink":
-					fieldWidget = (function(fldName, fieldProps) {
-						var _fieldName = fldName,
-								_fieldProps = fieldProps,
-								_format = _.get(_fieldProps, 'format', '{0}');
-						// console.log('FieldWidget: generate function to render field('+_fieldName+') ', fieldProps);
-						return function(val, fieldPK, row) {
-							// console.log('FieldWidget: pk('+fieldPK+') run function to render field('+_fieldName+') with value('+val+') ', fieldProps);
-							var valLink = String(val);
-							if (undefined !== _fieldProps._tsRetId
-									&& (0 === _fieldProps._tsRetId || '0' === _fieldProps._tsRetId || null === _fieldProps._tsRetId)
-									&& undefined !== _fieldProps._tsSimpleLink) {
-								valLink = _fieldProps._tsSimpleLink.format;
-								$.each(_fieldProps._tsSimpleLink.aliasMap, function(i, v) {
-									// console.log('simpleLink aliasMap fldName:', fldName, 'i:', i, 'v:', v, 'row['+v+']', row[v], 'val', val, 'typeof val', typeof val);
-									if (undefined !== row[v]) {
-										valLink = valLink.replace(new RegExp('\{' + i + '\}', 'g'), row[v]);
-									}
-								});
-							}
-							return p5Utils__format(_format, [valLink]);
-						}
-					}(fldName, fieldProps));
-					break;
-				case "gml:PolygonPropertyType":
-				case "gml:LineStringPropertyType":
-				case "gml:PointPropertyType":
-				case "geom":
-					fieldWidget = (function(fldName, fieldProps) {
-						var _fieldName = fldName,
-								_fieldProps = fieldProps,
-								_format = _.get(_fieldProps, 'format', '{0}');
-						// console.log('FieldWidget: generate function to render field('+_fieldName+') ', fieldProps);
-						return function(val, fieldPK, row) {
-							// console.log('FieldWidget: run function to render field('+_fieldName+') with value('+val+') ', fieldProps);
-							// console.log('FieldWidget: test priv', priv);
-							var node = jQuery('<span></span>');
-							node.TableAjaxGeomField({
-								recordID: fieldPK,
-								fieldValue: val,
-								hasValueClassName: 'cell-mapfld-hasValue',
-								linkClassNamePrefix: 'cell-mapfld',
-								linkSelectClassName: 'select',
-								linkSelectAddClassNames: 'glyphicon glyphicon-map-marker',
-								linkRemoveClassName: 'remove',
-								linkRemoveAddClassNames: 'glyphicon glyphicon-remove',
-								onSelect: function(recordID, geomShape) {
-									if (priv.options.debug) console.log('recordID:', recordID, 'geomShape:', geomShape);
-									priv.mapEditorShow();
-									_mapEditor.TableAjaxMapSelectRecord(recordID, geomShape);
-								},
-								onRemove: function(geomField, recordID, geomShape) {
-									if (confirm('Czy usunąć obiekt z mapy dla rekordu ' + recordID + '?')) {
-										if (!recordID) return;
-
-										var selectedRecordId = recordID;
-										superagent
-											.post('<?= $this->syncUrl; ?>&_hash=<?= $this->_htmlID; ?>&_task=removeTheGeomAjax')
-											.type('json') // header ĺapplication/x-www-form-urlencoded' requires type('form');
-											.send({
-												ID: recordID,
-												namespace: '<?= $acl->getNamespace(); ?>'
-											})
-											.set('Accept', 'application/json')
-											.end(function(err, res) {
-												if(priv.options.debug)console.log('DBG: res:', res, 'res.body:', res.body);
-												var payload;
-												if (err || !res.ok || 'application/json' !== res.type) {
-													payload = {type: 'warning', msg: res.body.msg || 'Wystąpiły błędy', body: res.body};
-												} else {
-													payload = {type: 'success', msg: res.body.msg || '', body: res.body};
-												}
-												p5UI__notifyAjaxCallback(payload);
-												var data = res.body;
-												if (data && data.record && data.record.the_geom) {
-													if (priv.options.debug) console.log('data.record.the_geom:2:', data.record.the_geom);
-													geomField.setValue(data.record.the_geom);
-												}
-												else if (data && data.record) {
-													if (priv.options.debug) console.log('data.record.the_geom:2:', data.record.the_geom);
-													geomField.setValue(data.record.the_geom);
-												}
-												_mapEditor.TableAjaxMapRefresh();
-											});
-									}
-								}
-							});
-							return node;
-						}
-					}(fldName, fieldProps));
-					break;
-				case "ref":
-					fieldWidget = (function(fldName, fieldProps) {
-						var _fieldName = fldName,
-								_fieldProps = fieldProps;
-						// console.log('FieldWidget: generate function to render field('+_fieldName+') fieldProps: ', fieldProps);
-						return function(val, fieldPK, row) {
-							// console.log('FieldWidget: pk('+fieldPK+') run function to render field('+_fieldName+') with value: ', val, ', fieldProps: ', fieldProps);
-							// if (val.length > 5) return '('+val.length+')' + printFirst5 + '...' => open more info
-							return _.map(val, function(v) {
-								if (!v || !v.xlink) return '';
-								var idRemote = v.xlink.split('.').pop()
-								var nsRemote = v.xlink.split(':').pop().split('.').shift()
-								var seLink = '<?= Request::getPathUri(); ?>index.php?_route=ViewTableAjax&namespace=' + fieldProps.xsdRefNsPrefix.replace(/__x3A__/g, '/') + '/' + nsRemote + '#EDIT/' + idRemote;
-								var wfsLink = fieldProps.xsdRefUri + '#' + fieldProps.xsdRefType + '.' + idRemote;
-								return '<a class="btn btn-xs btn-default" href="' + seLink + '" title="' + wfsLink + '">' + idRemote + '</a>';
-							}).join(' ');
-						}
-					}(fldName, fieldProps));
-					break;
-			}
-			_fieldWidgets[fldName] = fieldWidget;
-		};
-
-		priv.getFieldWidget_TableCell = function(fieldName) {
-			var props = _data.cols[fieldName],
-					type = null,
-					renderFieldAsTableCell = null;
-			if (!props) return null;
-			type = _.get(props, 'type');
-			if (!type) type = 'string';// TODO: throw Exception? / default string
-			renderFieldAsTableCell = _.get(_fieldWidgets, fieldName);
-			if (renderFieldAsTableCell) return renderFieldAsTableCell;
-			priv.registerFieldWidget(type, fieldName, props);
-			renderFieldAsTableCell = _.get(_fieldWidgets, fieldName);
-			if (!renderFieldAsTableCell) throw "Field type '" + type + "' not implemented";
-			return renderFieldAsTableCell;
-		};
-
-		priv.renderRow = function(props) {
-			var rowNode = $('<tr></tr>'),
-					rowPK = (_state.primaryKey in props) ? props[_state.primaryKey] : null,
-					cellNode,
-					columnName, columnProps, val, cellCnt, format, showTooltip, fldWidgetNode,
-					dbg = priv.options.debug
-			;
-			//  ondrop="drop_handler(event);" ondragover="dragover_handler(event);" ondragend="dragend_handler(event);"
-			rowNode.attr('ondrop', "p5TA_onDrop(event, '"+rowPK+"', '"+priv.options.namespace+"')")
-			rowNode.attr('ondragover', "p5TA_onDragOver(event, this, '"+rowPK+"', '"+priv.options.namespace+"')")
-			rowNode.attr('ondragend', "p5TA_onDragEnd(event, this, '"+rowPK+"', '"+priv.options.namespace+"')")
-
-			if (priv.options.rowFunctions || priv.options.filtersClean) {
-				cellNode = priv.renderCellRowFunctions(priv.options.rowFunctions, rowPK, props);
-				cellNode.addClass('text-right stickyCol1');
-				cellNode.appendTo(rowNode);
-			}
-
-			//create checkbox
-			if (_state.primaryKey && priv.options.checkboxes) {
-				var check = _uniqueCols[props[_state.primaryKey]] != undefined ? 'checked' : '';
-				var checkable = props['checkable'] === false ? 'disabled' : '';
-				cellNode = $('<td></td>').appendTo(rowNode);
-				$(p5Utils__format('<input class="unique" {0} {1} type="checkbox" />', [check, checkable])).appendTo(cellNode);
-			}
-
-			if (_state.primaryKey) rowNode.data('unique', rowPK);
-
-			//create cells
-			for (var i = 0; i < _state.colsSorted.length; i++) {
-				columnName = _state.colsSorted[i];
-				columnProps = _data.cols[columnName];
-				val = '';
-				if (!columnProps) continue;
-				if (columnProps.hidden) continue;// TODO: "unique" is hidden
-				showTooltip = true;
-
-				val = props[columnName];
-				cellNode = $('<td></td>').appendTo(rowNode);
-				if (i === 1) cellNode.addClass('stickyCol2');// primaryKey(ID)
-				if (i !== 1) cellNode.on('dblclick', {id:rowPK, col:columnName}, priv.rowDblClicked);
-				cellCnt = (i === 1)? '<div></div>' : '<span></span>';
-				cellCnt = $(cellCnt).appendTo(cellNode);// fix dblclick for copy
-
-				cellNode.data('column', columnName);
-				if (val === undefined) continue;
-
-				format = _.get(columnProps, 'format', '{0}');
-
-				// test use field widgets
-				fldWidgetNode = priv.renderFieldWidget_TableCell(columnName, val, rowPK, props);
-				if (fldWidgetNode || fldWidgetNode === 0) {
-					cellCnt.empty();
-					cellCnt.append(fldWidgetNode);
-				} else if (fldWidgetNode === '') {
-					cellCnt.empty();
-				} else {
-					console.log('TODO: !FieldWidget for row.pk('+rowPK+') col('+columnName+') typeof fldWidgetNode(' + (typeof fldWidgetNode) + ') columnProps', columnProps, 'fldWidgetNode', fldWidgetNode, 'value', props[columnName]);
-				}// test fld widgets
-
-				if (columnProps._tsRetId) {
-					showTooltip = false;
-					if (columnProps._tsRetId > 0) {
-						cellCnt.on('click', {id:rowPK, col:columnName, friendly:columnProps.friendly, value:p5Utils__format(format, [val])}, priv.popoverCellTypeSpecial);
-					}
-				}
-				if ('ref' === columnProps.type) showTooltip = false;
-				if ('ref' === columnProps.type) cellNode.css('padding', "3px")
-				if ('ref' === columnProps.type && priv.options.debug) console.log('renderRow rowPK('+rowPK+') columnProps', columnProps)
-				// if ('ref' === columnProps.type) cellNode.html('<a href="index.php?_route=ViewTableAjax' +
-				// 	'&namespace=' + columnProps.xsdRefNsPrefix.replace('__x3A__', '/') + '/' + columnProps.xsdRefType +
-				// 	'&backRefNS=' + priv.options.namespace +
-				// 	'&backRefPK=' + rowPK +
-				// 	'&backRefField=' + columnProps.column +
-				// 	'">przeglądaj</a>')
-				if ('ref' === columnProps.type) {
-					cellNode.html('<a onClick="return p5UI__tableAjaxOpenRefCell(' +
-						'\'' + columnProps.xsdRefNsPrefix.replace('__x3A__', '/') + '/' + columnProps.xsdRefType + '\', ' + // namespace
-						'\'' + priv.options.namespace + '\', ' + // backRefNS
-						'\'' + rowPK + '\', ' + // backRefPK
-						'\'' + columnProps.column + '\')' + // backRefField
-						'" ' +
-						'href="index.php?_route=ViewTableAjax' +
-						'&namespace=' + columnProps.xsdRefNsPrefix.replace('__x3A__', '/') + '/' + columnProps.xsdRefType +
-						'&backRefNS=' + priv.options.namespace +
-						'&backRefPK=' + rowPK +
-						'&backRefField=' + columnProps.column +
-						'">przeglądaj</a>')
-				}
-
-				if (columnProps.xsdType && 'xsd:base64Binary' === columnProps.xsdType) {
-					cellCnt.empty();
-					if (val) {
-						cellCnt.append('<a target="_blank" href="wfs-data.php?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetBlob&namespace='+priv.options.namespace+'&primaryKey='+rowPK+'&fieldName='+columnName+'">'+"otwórz"+'</a>');
-					} else {
-						cellCnt.append('<span style="color:silver" title="Brak danych">N/S;</span>');
-					}
-				}
-
-				if (i > 1 && priv.options.longDesc) {// TODO: use better check for columns: functions and pk
-					cellNode.addClass('tbl-short-txt');
-					if (showTooltip) {
-						cellCnt.tooltip({title: cellCnt.text(), placement: 'left'});
-					}
-				}
-			}
-			return rowNode;
-		};
-
-		priv.renderCellRowFunctions = function(rowFunctions, rowPK, rowProps) {
-			var cellNode = $('<td></td>'),
-					keys = Object.keys(rowFunctions),
-					total = keys.length,
-					moreFuncBtnNode,
-					moreFunctions = [],
-					idx
-			;
-			if (priv.options.debug) console.log('TableAjax::renderCellRowFunctions: rowFunctions', rowFunctions);
-			idx = keys.indexOf('hist');
-			if (idx !== -1) {
-				histFunc = keys.splice(idx, 1);
-				keys.push('hist');
-			}
-			moreFunctions = keys.splice(3);
-			keys.forEach(function(key) {
-				var funObj = rowFunctions[key],
-						funcNode = p5UI_TableAjax_generateFunctionNode(funObj, rowPK, {ico: true, label: false})
-				;
-				funcNode.appendTo(cellNode);
-			});
-			moreFuncBtnNode = $('<a href="#" style="margin:0 2px;text-decoration:none" title="Więcej funkcji dla rekordu nr '+rowPK+'" class="glyphicon glyphicon-menu-hamburger"> </a>');
-			moreFuncBtnNode.on('click', {rowPK: rowPK, rowFunctions: rowFunctions, more: moreFunctions}, priv.popoverCellMoreFunctions);
-			moreFuncBtnNode.appendTo(cellNode);
-			return cellNode;
-		};
-
-		priv.popoverCellMoreFunctions = function(e) {
-			e.preventDefault();
-			e.stopPropagation();
-			if (priv.options.debug) console.log('TableAjax::popoverCellMoreFunctions: rowPK', e.data.rowPK, 'moreFunctions', e.data.more, 'rowFunctions', e.data.rowFunctions);
-			if ('rowPK' in e.data && e.data.rowPK > 0) {
-				var lastId = _popoverCell.data('rowid'),
-						lastCol = _popoverCell.data('col'),
-						rowPK = e.data.rowPK,
-						moreFunctions = e.data.more,
-						rowFunctions = e.data.rowFunctions,
-						funcListNode
-				;
-
-				if (lastId == e.data.rowPK && lastCol == 'moreFunctions') {
-					//_popoverCellCurrent.popover('toggle');
-				}
-				else {
-					if (_popoverCellCurrent) {
-						_popoverCellCurrent.popover('destroy');
-					}
-
-					_popoverCell.data('rowid', rowPK);
-					_popoverCell.data('col', 'rowFunctions');
-					_popoverCell.empty();
-					funcListNode = $('<ul class="list-unstyled popoverRowFunctions"></ul>').appendTo(_popoverCell);
-
-					moreFunctions.forEach(function(funcName) {
-						var funcItemNode = $('<li></li>').appendTo(funcListNode),
-								funObj = rowFunctions[funcName],
-								funcNode = p5UI_TableAjax_generateFunctionNode(funObj, rowPK, {ico: true, label: true})
-						;
-						funcNode.addClass('func_name-' + funcName);
-						funcItemNode.append(funcNode);
-					});
-					{ // TODO: id namesapce is AntAcl [ and has ref fields or back ref ]
-						var funcItemNode = $('<li></li>').appendTo(funcListNode)
-						var funObj = {
-							ico: 'glyphicon glyphicon-random',
-							label: 'Przeglądaj powiązania',
-							href: 'index.php?_route=RefGraph&namespace=' + priv.options.namespace + '&primaryKey=' + rowPK,
-							// onclick: function (e) { // TODO: open in
-							// 	console.log('TODO: przeglądaj powiązania')
-							// }
-						}
-						var funcNode = p5UI_TableAjax_generateFunctionNode(funObj, rowPK, { ico: true, label: true })
-						funcItemNode.append(funcNode);
-					}
-
-					_popoverCell.append('<div class="popoverCellContent" style="white-space:normal"></div>');
-
-					_popoverCellCurrent = jQuery(e.currentTarget);
-					// title : '<span class="text-info"><strong>title</strong></span> <button type="button" id="close" class="close">&times;</button>'
-					var opts = {
-						container: 'body',
-						placement: 'right',
-						trigger: 'click',
-						template: '',
-						html: true,
-						content: _popoverCell.html()
-					}
-					opts.template += '<div class="popover" role="tooltip" style="max-width:600px;width:440px;">';
-					{
-						opts.template += '<div class="arrow"></div>';
-						//opts.template += '<h3 class="popover-title"></h3>';
-						opts.template += '<div style="display:block;position:relative;">';
-						{
-							opts.template += '<div class="popover-title">';
-							opts.template += '</div>';
-							opts.template += '<button type="button" class="close" onclick="return hidePopover();" style="position:absolute;right:8px;top:6px;">&times;</button>';
-						}
-						opts.template += '</div>';
-						opts.template += '<div class="popover-content"></div>';
-					}
-					opts.template += '</div>';
-
-					_popoverCellCurrent.popover(opts);
-					_popoverCellCurrent.on('shown.bs.popover', function(e) {
-						if (!_popoverCellCurrent) return;
-						var popoverNodeId = _popoverCellCurrent.attr('aria-describedby');
-						if (!popoverNodeId) return;
-						var popover$Node = jQuery('#' + popoverNodeId);
-						if (!popover$Node.length) return;
-						var arrow$Node = popover$Node.children('.arrow');
-						if (arrow$Node.length) {
-							var posTop = parseFloat(arrow$Node.css('top'));
-							arrow$Node.css('top', '' + posTop + 'px')
-						}
-						popover$Node.find('.popoverCellContent').html('<p class="text-muted">Pobieranie funkcji...<p>');
-						priv.ajaxLoadMoreFunctionsCell(rowPK);
-					});
-					_popoverCellCurrent.popover('show');
-				}
-			} else {
-				if (priv.options.debug) console.log('NO data');
-				return false;
-			}
-			return;
-		};
-
-		priv.ajaxLoadMoreFunctionsCell = function(rowPK) {
-			var dbg = priv.options.debug;
-			if (_popoverCellAjaxXhr) {
-				_popoverCellAjaxXhr.abort();
-			}
-
-			_popoverCellAjaxXhr = $.ajax({
-				type: 'GET',
-				url: '<?= $this->syncUrl; ?>&_hash=<?php echo $this->_htmlID; ?>&_task=moreFunctionsCellAjax&ID=' + rowPK,
-				dataType: 'json',
-				contentType: "application/json; charset=utf-8",
-				data: ''
-			})
-			.done(function(data, textStatus, jqXHR){
-				if (data && 'success' === data.type) {
-					var popoverCellContent,
-							rowFunctions = [],
-							funcNodesToUpdate = []
-					;
-					if (data.rowFunctions && data.rowFunctions.length > 0) {
-						rowFunctions = data.rowFunctions;
-					}
-					if (rowFunctions.length > 0) {
-						var popoverCellContent = $('<ul class="list-unstyled"></ul>');
-						rowFunctions.forEach(function(funObj) {
-							var funcNode = p5UI_TableAjax_generateFunctionNode(funObj, rowPK, {ico: true, label: true});
-							if (funObj.id) {
-								funcNodesToUpdate.push({id: funObj.id, node: funcNode});
-							} else {
-								var funcItemNode = $('<li></li>').appendTo(popoverCellContent);
-								funcItemNode.append(funcNode);
-							}
-						});
-					} else {
-						popoverCellContent = '<p class="text-muted">Brak dodatkowych funkcji</p>';
-					}
-					//_popoverCell.append(popoverCellContent);// cache
-					if (_popoverCellCurrent) {
-						var popoverNodeId = _popoverCellCurrent.attr('aria-describedby');
-						if (popoverNodeId) {
-							var popoverNode = jQuery('#' + popoverNodeId),
-									popoverCntNode = popoverNode.find('.popoverCellContent'),
-									popoverRowFuncNode = popoverNode.find('.popoverRowFunctions');
-							;
-							{// fix generated row Functions by loaded from ajax call
-								if(dbg) console.log('popoverRowFunctions. popoverRowFuncNode', popoverRowFuncNode);
-								if(dbg) console.log('popoverRowFunctions. funcNodesToUpdate', funcNodesToUpdate);
-								funcNodesToUpdate.forEach(function(funcNodeInfo) {
-									var className = 'func_name-' + funcNodeInfo.id,
-											foundRowFuncNode = null
-									;
-									if(dbg) console.log('popoverRowFunctions. funcNodesToUpdate loop:', funcNodeInfo);
-									popoverRowFuncNode.find('li > a').each(function(ind, n) {
-										var n$ = $(n);
-										if(dbg) console.log('popoverRowFunctions loop(',ind,'):', n, 'data(func_name)', n$.data('func_name'), 'data', n$.data());
-										if (n$.hasClass(className)) {
-											foundRowFuncNode = n$;
-										}
-									});
-									if (foundRowFuncNode) {
-										foundRowFuncNode.replaceWith(funcNodeInfo.node);
-									} else {
-										var funcItemNode = $('<li></li>').appendTo(popoverCellContent);
-										funcItemNode.append(foundRowFuncNode);
-									}
-								});
-							}
-							popoverCntNode.empty().append(popoverCellContent);
-							if (rowFunctions.length <= 0) {
-								popoverCntNode.find('p.text-muted').delay(600).hide(300);
-							}
-						}
-					}
-				}
-			});
-
-		};
-
-		priv.renderRowEmptyNode = function() {
-			var rowNode = $('<tr></tr>');
-
-			if (priv.options.rowFunctions || priv.options.filtersClean) {
-				$('<td class="text-right stickyCol1">&nbsp;</td>').appendTo(rowNode);
-			}
-
-			if (_state.primaryKey && priv.options.checkboxes) {
-				$('<td><input disabled type="checkbox" /></td>').appendTo(rowNode);
-			}
-			for (var i = 0; i < _state.colsSorted.length; i++) {
-				var columnName = _state.colsSorted[i],
-						columnProps = _data.cols[columnName]
-				;
-				if (!columnProps) return;
-				if (columnProps.hidden) continue;// "unique" is hidden - TODO: rm "unique" from _state.colsSorted ?
-
-				if (i == 1) {// TODO: 1 is pk - use better check
-					$('<td class="stickyCol2">&nbsp;</td>').appendTo(rowNode);
-				} else {
-					$('<td>&nbsp;</td>').appendTo(rowNode);
-				}
-			}
-			return rowNode;
-		};
-
-		priv.renderTableTheadSort = function() {
-			var nodeClass = 'tblAjax__' + 'head__sort'
-			var currentNode = _uiNode$Table.find('thead').find('.' + nodeClass)
-			var node = $('<tr class="sort ' + nodeClass + '"></tr>')
-			if (priv.options.tblFunctions || priv.options.filtersClean) {
-				var headCell = $('<th class="text-right head-info stickyCol1"></th>').appendTo(node)
-				$.map(priv.options.tblFunctions, function (funObj, funName) {
-					var funHtml = $('<a></a>')
-					funHtml.attr('href', funObj.href || '#')
-					if (funObj.title) funHtml.attr('title', funObj.title)
-					if (funObj.icon) funHtml.html('<span class="glyphicon glyphicon-' + funObj.icon + '"></span> ')
-					if (funObj.method && priv[funObj.method] && typeof priv[funObj.method] == 'function') {
-						funHtml.on('click', priv[funObj.method])
-					}
-					funHtml.appendTo(headCell)
-				})
-			}
-
-			if (_state.primaryKey && priv.options.checkboxes) {
-				var checked = _checkToggleChecked ? 'checked' : ''
-				var headCell = $('<th></th>').appendTo(node)
-				var elem = $(p5Utils__format('<input {0} class="checkToggle" type="checkbox" />', [checked])).appendTo(headCell)
-				elem.on('change', priv.checkToggleChanged)
-			}
-
-			for (var i = 0; i < _state.colsSorted.length; i++) {
-				var column = _state.colsSorted[i]
-				var props = _data.cols[column]
-				if (props.hidden) continue
-
-				var headCell = $('<th class="ta-ordering"></th>').appendTo(node)
-				if (i == 1) headCell.addClass('stickyCol2')
-
-				if (props.type != 'special' && props.type != 'geom') { // TODO: props.isSortable
-					headCell.on('click', {column: column}, priv.columnClicked)
-				}
-
-				var nodeLabel = (props.friendly) ? props.friendly : column
-				var nodeTitle = column
-				if (props.description && props.description.length > 0) {
-					nodeTitle = p5Utils__format("{0} ({1})", [props.description, column])
-				}
-				else if (props._tsRetId > 0) {
-					nodeTitle = p5Utils__format("Kliknij na pole i przejdź do powiązanych rekordów ({0})", [nodeTitle])
-				}
-				if ('ref' === props.type && props.xsdRefType) {
-					nodeLabel = '<i class="glyphicon glyphicon-export"></i> ' + props.xsdRefType
-					if (props.description && props.description.length > 0 && props.description !== column) {
-						nodeTitle = p5Utils__format("{0} (ref {1})", [props.description, column])
-					} else {
-						nodeTitle = p5Utils__format("(ref {0})", [column])
-					}
-				}
-				var headCnt = $(p5Utils__format('<span class="pull-left" title="{1}">{0}</span>', [nodeLabel, nodeTitle]))
-				headCnt.appendTo(headCell)
-
-				if (column == _state.filters.currSortCol) {
-					headCell.addClass((_state.filters.currSortFlip) ? 'ta-ordering-down' : 'ta-ordering-up')
-				}
-
-				if (column !== _state.primaryKey) {
-					var hideColBtn = $('<i class="glyphicon glyphicon-remove remove-cell"></i>')
-					hideColBtn.on('click', {column: column},  priv.columnHideClicked)
-					hideColBtn.appendTo(headCell)
-				}
-			}
-
-			currentNode.replaceWith(node)
-		};
-
-		priv.renderHeadSpecialFilters = function() {
-			// console.log("priv.renderHeadSpecialFilters state:", { 'priv.options.specialFilterFunctions': priv.options.specialFilterFunctions, '_state.specialFilters': _state.specialFilters });
-			var nodeClass = 'tblAjax__' + 'head__specialFilter',
-					currentNode = $(_uiNodeCont).prevAll('.' + nodeClass),
-					node;
-			if (!priv.options.specialFilterFunctions) {
-				return;
-			}
-			var node = $('<div class="btn-toolbar tblAjax__head__specialFilter"></div>');
-			$.map(priv.options.specialFilterFunctions, function(groupObj, groupName) {
-				var btnHtml,
-						btnSelected,
-						groupLabel = groupObj.label || groupName,
-						groupHtml = $('<div class="btn-group"></div>')
-				;
-				// _state.specialFilters[e.data.group] = e.data.value;
-				if (groupName in _state.specialFilters) {
-					btnSelected = _state.specialFilters[groupName];
-				}
-				// console.log("priv.renderHeadSpecialFilters loop:", {groupName: groupName, btnSelected: btnSelected});
-				if (groupObj.icon) {
-					$('<button class="btn btn-xs btn-default" title="' + groupLabel + '"><i class="' + groupObj.icon + '"></i></button>').appendTo(groupHtml);
-				}
-				$.map(groupObj.btns, function(btnObj, btnName) {
-					btnHtml = $('<button class="btn btn-xs btn-default' + ((btnSelected && btnSelected == btnObj.value)? ' active' : '') + '">' + btnName + '</button>').appendTo(groupHtml);
-					btnHtml.on('click', {group: groupName, value: btnObj.value}, priv.specialFilterChanged);
-				});
-				btnHtml = $('<button class="btn btn-xs btn-default' + ((!btnSelected)? ' disabled' : '') + '" title="Kasuj filtr"><i class="glyphicon glyphicon-remove"></i></button>').appendTo(groupHtml);
-				btnHtml.on('click', {group: groupName, value: ''}, priv.specialFilterChanged);
-				groupHtml.appendTo(node);
-			});
-
-			// console.log("priv.renderHeadSpecialFilters replace:", { 'priv.options.specialFilterFunctions': priv.options.specialFilterFunctions, '_state.specialFilters': _state.specialFilters });
-			// console.log("priv.renderHeadSpecialFilters replace node:", { 'currentNode': currentNode, 'node': node, '_uiNodeCont': _uiNodeCont });
-			currentNode.replaceWith(node);
-		};
-
-		priv.renderTableTheadFilter = function() {
-			var nodeClass = 'tblAjax__' + 'head__filter',
-					currentNode = _uiNode$Table.find('thead').find('.' + nodeClass),
-					node;
-			/// console.log('L.<?php echo __LINE__; ?> renderTable::renderTableTheadFilter...');//TODO:DBG:RMME
-			// currentNode.find('i').tooltip('hide');
-			node = $('<tr class="filter ' + nodeClass + '"></tr>');
-			//_head.find(".filter").remove();
-			//_headFilter = $('<tr class="filter"></tr>').appendTo(_head);
-			var headCell;
-			var elem;
-			var placeHolder = '';
-			var tooltip = '';
-
-			//create the functions column
-			if (priv.options.rowFunctions || priv.options.filtersClean) {
-				var headCell = $('<th class="text-right stickyCol1"></th>').appendTo(node);
-				headCell.css({padding: '0'});
-				if (priv.options.filtersClean) {
-					var elem = $('<button title="Kasuj filtry" class="btn btn-xs btn-link glyphicon glyphicon-remove"></button>').appendTo(headCell);
-					elem.on('click', priv.filtersClean);
-				}
-			}
-
-			//create the filter checkbox
-			if (_state.primaryKey && priv.options.checkboxes) {
-				tooltip = priv.options.types.bool.filterTooltip || 'Toggle between:<br/>indeterminate,<br/>checked,<br/>unchecked';
-				headCell = $('<th></th>').appendTo(node);
-				elem = $('<input class="filter indeterminate" checked type="checkbox" />').appendTo(headCell);
-				$.map(_state.filters.filterCols, function(colProps, col) {
-					if (col == "unique") {
-						if (colProps.filter) elem.prop('checked', true).removeClass('indeterminate');
-						else if (!colProps.filter) elem.prop('checked', false).removeClass('indeterminate');
-						else if (colProps.filter == '') elem.addClass('indeterminate');
-					}
-				});
-
-				if (tooltip) {
-					elem.tooltip({
-						title: tooltip.trim(),
-						html: true,
-						container: 'body',
-						trigger: 'hover',
-						placement: 'top',
-						delay: {
-							show: 500,
-							hide: 100
-						}
-					});
-				}
-				elem.on('click', {column: "unique"}, priv.filterChanged);
-			}
-
-			//create the column filters
-			for (var i = 0; i < _state.colsSorted.length; i++) {
-				var column = _state.colsSorted[i];
-				var props = _data.cols[column];
-				tooltip = props.filterTooltip === true ? undefined : props.filterTooltip === false ? '' : props.filterTooltip;
-				placeHolder = props.placeHolder === true ? undefined : props.placeHolder === false ? '' : props.placeHolder;
-				if (props.hidden) continue;
-
-				headCell = $('<th></th>').appendTo(node);
-				headCell.css({padding: '0'});
-				if (i == 1) headCell.addClass('stickyCol2');
-
-				// @mark MarkTableAjaxFilterColType
-				switch (props.type || 'string') {
-						case "number":
-								if (placeHolder == undefined) placeHolder = priv.options.types.number.placeHolder;
-								placeHolder = (placeHolder === true || placeHolder == undefined) ? '' : placeHolder === false ? '' : placeHolder;
-								if (tooltip == undefined) tooltip = priv.options.types.number.filterTooltip;
-								tooltip = (tooltip === true || tooltip == undefined) ? '' : tooltip === false ? '' : tooltip;
-								elem = $(p5Utils__format('<input placeholder="{0}" class="filter" type="text" />', [placeHolder]));
-								elem.on('keyup', {column: column}, priv.filterChanged);
-								break;
-						case "date":
-								if (placeHolder == undefined) placeHolder = priv.options.types.date.placeHolder;
-								placeHolder = (placeHolder === true || placeHolder == undefined) ? '' : placeHolder === false ? '' : placeHolder;
-								if (tooltip == undefined) tooltip = priv.options.types.date.filterTooltip;
-								tooltip = (tooltip === true || tooltip == undefined) ? '' : tooltip === false ? '' : tooltip;
-								elem = $(p5Utils__format('<div><input placeholder="{0}" class="filter" type="text" /></div>', [placeHolder]));
-
-								if (priv.options.types.date.datePicker === true || priv.options.types.date.datePicker == undefined)
-								{
-										if ($().datepicker)
-										{
-												elem.addClass('date-wrap');
-												var today = 0;//new priv.ext.XDate(false).setHours(0, 0, 0, 0).toString('yyyy-MM-dd');
-												var dp = $(p5Utils__format('<div style="float:right" class="date" data-date="{0}" data-date-format="{1}" />', [today, 'yyyy-mm-dd'])).appendTo(elem);
-												$('<input style="display:none" type="text"  />').appendTo(dp);
-												$('<span class="add-on"><i class="glyphicon glyphicon-chevron-right"></i></span>').on('click', {op: "l"}, priv.dpOpChanged).appendTo(dp);
-												$('<span class="add-on"><i class="glyphicon glyphicon-chevron-left"></i></span>').on('click', {op: "r"}, priv.dpOpChanged).appendTo(dp);
-												dp.datepicker({weekStart:1});
-												dp.on('changeDate', {column: column, input: $('input.filter', elem)}, priv.dpClicked);
-										}
-										else
-										if (priv.options.debug) console.log('datepicker plugin not found');
-								}
-								elem.on('keyup', 'input.filter', {column: column}, priv.filterChanged);
-								break;
-						case "bool":
-								if (tooltip == undefined) tooltip = priv.options.types.bool.filterTooltip;
-								tooltip = (tooltip === true || tooltip == undefined) ? 'Toggle between:<br/>indeterminate,<br/>checked,<br/>unchecked' : tooltip === false ? '' : tooltip;
-								elem = $('<input class="filter indeterminate" checked type="checkbox" />');
-								elem.on('click', {column: column}, priv.filterChanged);
-								break;
-						case "p5:price":
-						case "p5:www_link":
-						case "p5:string":
-						case "string":
-								if (placeHolder == undefined) placeHolder = priv.options.types.string.placeHolder;
-								placeHolder = (placeHolder === true || placeHolder == undefined) ? '%' : placeHolder === false ? '' : placeHolder;
-								if (tooltip == undefined) tooltip = priv.options.types.string.filterTooltip;
-								tooltip = (tooltip === true || tooltip == undefined) ? 'Find str: str<br/>Find all but str: !str<br/>Find str inside: %str%' : tooltip === false ? '' : tooltip;
-								elem = $(p5Utils__format('<input placeholder="{0}" class="filter" type="text" size="8" />', [placeHolder]));
-								elem.on('keyup', {column: column}, priv.filterChanged);
-								break;
-						case "special":
-								elem = $('<div>&nbsp;</div>');
-								break;
-						case "simpleLink":
-								elem = $('<div>&nbsp;</div>');
-								break;
-						case "geom":
-								if (placeHolder == undefined) placeHolder = priv.options.types.string.placeHolder;
-								placeHolder = (placeHolder === true || placeHolder == undefined) ? '%' : placeHolder === false ? '' : placeHolder;
-								elem = $(p5Utils__format('<input placeholder="{0}" class="filter" type="text" size="8" />', [placeHolder]));
-								elem.on('keyup', {column: column}, priv.filterChanged);
-								break;
-						case "none":
-								elem = $('<div>&nbsp;</div>');
-								break;
-						case "ref": // TODO: search for ref fields
-								elem = $('<div>&nbsp;</div>')
-								break;
-						default:
-								elem = $('<div>&nbsp;</div>')
-								break;
-				}
-
-				if (false) {// tooltip OFF - (tooltip)
-					elem.tooltip({
-						title: tooltip.trim(),
-						html: true,
-						container: 'body',
-						trigger: 'focus',
-						placement: 'top',
-						delay: {
-							show: 500,
-							hide: 100
-						}
-					});
-				}
-
-				if (elem && props.filter) {
-					$.map(_state.filters.filterCols, function(colProps, col) {
-						if (col == column) {
-							var columnSettings = _data.cols[col];
-							if (columnSettings.type == 'bool') {
-								if (colProps.filter) elem.prop('checked', true).removeClass('indeterminate');
-								else if (!colProps.filter) elem.prop('checked', false).removeClass('indeterminate');
-								else if (colProps.filter == '') elem.addClass('indeterminate');
-							}
-							else elem.val(colProps.filter);
-						}
-					});
-					if (priv.options.forceFilterInit && undefined !== priv.options.forceFilterInit[column]) {
-						elem.prop('disabled', true);
-					}
-					elem.appendTo(headCell);
-
-					_filterFields[column] = elem;
-				}
-			}
-
-			currentNode.replaceWith(node);
-		};
-
-		priv.renderTableTfoot = function() {
-			_uiNode$Table.find('tfoot').remove();
-			_foot = $('<tfoot></tfoot>').insertAfter(_bodyNode);
-			//$(_uiNodeCont).next('.foot').replaceWith(_foot);
-		};
-
-		priv.renderFooter = function() {
-			priv.renderFooterInfo();
-			priv.renderFooterPagination();
-			priv.renderFooterPageSizes();
-			priv.renderFooterColumnPicker();
-			priv.renderFooterFunctions();
-			priv.renderFooterExport();
-		};
-
-		priv.renderFooterInfo = function() {
-			var nodeClass = 'tblAjax__' + 'footer__toolbar__info',
-					currentNode = $(_uiNodeCont).next('.foot').find('.' + nodeClass),
-					node;
-			if (priv.options.debug) console.log('Render: ', nodeClass);
-			node = $('<div class="foot-info ' + nodeClass + '"></div>');
-			//console.log('renderFooterInfo total', _data.total, 'page', _state.page, 'priv.options.pageSize', priv.options.pageSize);
-			var fromRow = Math.max(_state.page - 1, 0) * _state.pageSize;
-			var total = _data.total;
-			var toRow = Math.min(fromRow + _state.pageSize, total);
-			if (_data.total > 0) {
-				$(p5Utils__format('<p>Wiersze od {0} do {1} z {2}</p>', [fromRow + 1, toRow, total])).appendTo(node);
-			} else {
-				$('<p>Brak wierszy pasujących do kryteriów wyszukiwania</p>').appendTo(node);
-			}
-			currentNode.replaceWith(node);
-		};
-		priv.renderFooterPagination = function() {
-			var nodeClass = 'tblAjax__' + 'footer__toolbar__pagination',
-					currentNode = $(_uiNodeCont).next('.foot').find('.' + nodeClass),
-					node;
-			var currPage = _state.page;
-			var totalPages = Math.ceil(_data.total / _state.pageSize);
-			if (priv.options.debug) console.log('Render: ', nodeClass, '_data.total', _data.total, 'currPage', currPage);
-/// console.log('Render: ', nodeClass, '_data.total', _data.total, 'currPage', currPage, '_state.page', _state.page);
-			if (_data.total > 0) {
-				node = $('<div class="btn-group ' + nodeClass + '"></div>');
-				//create the pager.
-				var lowerPage = currPage - 2;
-				var upperPage = currPage + 2;
-				if (upperPage > totalPages) {
-					var diff = upperPage - totalPages;
-					upperPage = totalPages;
-					lowerPage -= diff;
-				}
-				if (lowerPage < 1) lowerPage = 1;
-				if (upperPage < 5) upperPage = 5;
-
-				//$(p5Utils__format('<li class="{0}"><a href="#">&lt;&lt;</a></li>', [currPage == 1 ? 'disabled' : '']))
-				$(p5Utils__format('<button type="button" class="btn btn-default{0}">&lt;&lt;</button>', [currPage == 1 ? ' disabled' : '']))
-					.on('click', {pageIndex: 1}, priv.pageChanged)
-					.appendTo(node);
-				//$(p5Utils__format('<li class="{0}"><a href="#">&lt;</a></li>', [currPage == 1 ? 'disabled' : '']))
-				$(p5Utils__format('<button type="button" class="btn btn-default{0}">&lt;</button>', [currPage == 1 ? ' disabled' : '']))
-					.on('click', {pageIndex: currPage - 1}, priv.pageChanged)
-					.appendTo(node);
-
-				for (var i = lowerPage; i <= upperPage; i++) {
-					var link;
-					//if (i != currPage) link = $(p5Utils__format('<li class="{1}"><a href="#">{0}</a></li>', [i, i > totalPages ? 'disabled' : '']));
-					if (i != currPage) link = $(p5Utils__format('<button type="button" class="btn btn-default{1}">{0}</button>', [i, i > totalPages ? ' disabled' : '']));
-					//if (i == currPage) link = $(p5Utils__format('<li class="active"><a href="#">{0}</a></li>', [i]));
-					if (i == currPage) link = $(p5Utils__format('<button type="button" class="btn btn-default active">{0}</button>', [i]));
-
-					if (link) {
-						link.on('click', {pageIndex: i}, priv.pageChanged);
-						link.appendTo(node);
-					}
-				}
-				//$(p5Utils__format('<li class="{0}"><a href="#">&gt;</a></li>', [currPage == totalPages ? 'disabled' : '']))
-				$(p5Utils__format('<button type="button" class="btn btn-default{0}">&gt;</button>', [currPage == totalPages ? ' disabled' : '']))
-					.on('click', {pageIndex: currPage + 1}, priv.pageChanged)
-					.appendTo(node);
-				//$(p5Utils__format('<li class="{0}"><a href="#">&gt;&gt;</a></li>', [currPage == totalPages ? 'disabled' : '']))
-				$(p5Utils__format('<button type="button" class="btn btn-default{0}">&gt;&gt;</button>', [currPage == totalPages ? ' disabled' : '']))
-					.on('click', {pageIndex: totalPages}, priv.pageChanged)
-					.appendTo(node);
-			} else {
-				node = $('<span class="' + nodeClass + '"></span>');
-			}
-			currentNode.replaceWith(node);
-		};
-		priv.renderFooterPageSizes = function() {
-			var nodeClass = 'tblAjax__' + 'footer__toolbar__pagesizes',
-					currentNode = $(_uiNodeCont).next('.foot').find('.' + nodeClass),
-					node;
-			if (priv.options.debug) console.log('Render: ', nodeClass);
-			if (_data.total > 0 && priv.options.pageSizes.length > 0) {
-				node = $('<div class="btn-group dropup pagesize ' + nodeClass + '"></div>');
-				var btn = $('<button class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" href="#">Liczba wierszy&nbsp;</button>').appendTo(node);
-				var span = $('<span class="caret"></span>').appendTo(btn);
-				var ul = $('<ul class="dropdown-menu" style="max-height:250px;overflow:auto;">').appendTo(node);
-
-				$.each(priv.options.pageSizes, function(index, val) {
-					var li = $('<li></li>').appendTo(ul);
-					if (val == priv.options.pageSize) {
-						$(p5Utils__format('<a style="color:#337AB7;">{0}</a>', [val])).appendTo(li);
-					} else {
-						$(p5Utils__format('<a href="#">{0}</a>', [val])).appendTo(li);
-					}
-				});
-				node.on('click', 'a', priv.pageSizeChanged);
-			} else {
-				node = $('<span class="' + nodeClass + '"></span>');
-			}
-			currentNode.replaceWith(node);
-		};
-		priv.renderFooterColumnPicker = function() {
-			var nodeClass = 'tblAjax__' + 'footer__toolbar__columnPicker',
-					currentNode = $(_uiNodeCont).next('.foot').find('.' + nodeClass),
-					node;
-			if (priv.options.debug) console.log('Render: ', nodeClass);
-			if (priv.options.columnPicker) {
-				node = $('<div class="btn-group dropup columnpicker ' + nodeClass + '"></div>');
-				var btn = $('<button class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" href="#">Kolumny&nbsp;</button>').appendTo(node);
-				var span = $('<span class="caret"></span>').appendTo(btn);
-				var ul = $('<ul class="dropdown-menu" style="max-height:250px;overflow:auto;">').appendTo(node);
-
-				var selectedFilter = priv.modelColFilter_getSelected();
-				priv.modelColFilter_getFilters().map(function(colFltr) {
-					var li = $('<li></li>').appendTo(ul),
-							selected = (selectedFilter && selectedFilter == colFltr.name)? 'checked="checked"' : '',
-							input = '<input ' + selected + ' type="radio" style="margin:0"/>',
-							a = $('<a href="#" data-col_filter="' + colFltr.name + '" style="padding:0px 20px;">' + input + ' ' + colFltr.label + '</a>').appendTo(li);
-							if ('all' != colFltr.name && 'most_used' != colFltr.name) {
-								arm = $('<button data-col_filter="' + colFltr.name + '" class="pull-right btn btn-xs btn-link" style="margin:0;padding:0;border:0;color:red">' + '&times;' + '</a>').appendTo(a);
-								arm.on('click', priv.modelColFilter_onClickRemoveFilter)
-							}
-				});
-
-				{// if (!selectedFilter) {// save current filter
-					var li = $('<li style="text-align:center; clear:both"></li>').appendTo(ul)
-					priv.modelColFilter_getSaveBtn().appendTo(li);
-				}
-
-				$('<li class="divider"></li>').appendTo(ul);
-				$.each(_data.cols, function(col, props) {
-					if (props.type != "unique" && col != _state.primaryKey) {
-						var li = $('<li></li>').appendTo(ul),
-								label = (props.friendly || col).replace(/<br\/?>/g, ' '),
-								input = p5Utils__format('<input {0} type="checkbox" title="{1}" value="{1}" style="margin:0"/>&nbsp;{2}', [(props.hidden) ? '' : 'checked', col, label]),
-								a = $('<a href="#" style="padding:0px 20px;">' + input + '</a>').appendTo(li);
-					}
-				});
-				node.on('click', 'input[type="checkbox"]', priv.columnPickerClicked);
-				node.on('click', 'a', priv.columnPickerLinkClicked);
-			} else {
-				node = $('<span class="' + nodeClass + '"></span>');
-			}
-			currentNode.replaceWith(node);
-		};
-		priv.renderFooterFunctions = function() {
-			var nodeClass = 'tblAjax__' + 'footer__toolbar__functions',
-					currentNode = $(_uiNodeCont).next('.foot').find('.' + nodeClass),
-					node;
-			if (priv.options.debug) console.log('Render: ', nodeClass);
-			if (priv.options.tblFunctions) {
-				node = $('<div class="btn-group ' + nodeClass + '"></div>');
-				$.map(priv.options.tblFunctions, function(funObj, funName){
-					var funHtml = $('<button class="btn btn-sm btn-default"></button>');
-					if (funObj.title) funHtml.attr('title', funObj.title);
-					if (funObj.method) funHtml.on('click', priv[funObj.method]);
-					else if (funObj.href) funHtml.on('click', {hash:funObj.href}, priv.routeChanged);
-
-					var funIconHtml = $('<i></i>');
-					if (funObj.icon) funIconHtml.attr('class', 'glyphicon glyphicon-' + funObj.icon);
-					funIconHtml.prependTo(funHtml);
-
-					funHtml.addClass('btn');
-					funHtml.appendTo(node);
-				});
-			} else {
-				node = $('<span class="' + nodeClass + '"></span>');
-			}
-			currentNode.replaceWith(node);
-		};
-		priv.renderFooterExport = function() {
-			var nodeClass = 'tblAjax__' + 'footer__toolbar__export',
-					currentNode = $(_uiNodeCont).next('.foot').find('.' + nodeClass),
-					node;
-			if (priv.options.debug) console.log('Render: ', nodeClass);
-			if (priv.options.exportFields && priv.options.exportFields.length) {
-				node = $('<div class="btn-group dropup pagesize ' + nodeClass + '"></div>');
-				var btn = $('<button class="btn btn-sm dropdown-toggle" data-toggle="dropdown" href="#">Export&nbsp;</button>').appendTo(node);
-				var span = $('<span class="caret"></span>').appendTo(btn);
-				var ul = $('<ul class="dropdown-menu" style="max-height:250px;padding:5px 20px 5px 8px;overflow:auto;">').appendTo(node);
-
-				$.each(_data.cols, function(col, props) {
-					if (-1 !== priv.options.exportFields.indexOf(col)) {
-						var li = $('<li></li>').appendTo(ul);
-						$(p5Utils__format('<input {0} type="checkbox" title="{1}" value="{1}" >&nbsp;{2}</input>', [(_exportFieldsSelect[col])? 'checked' : '', col, props.friendly || col])).appendTo(li);
-						li.on('click', 'input', priv.exportFieldChanged);
-					}
-				});
-
-				var li = $('<li></li>').appendTo(ul);
-				$('<a href="index.php" target="_blank" class=""><i class="glyphicon glyphicon-share"></i>Export do HTML</a>').appendTo(li);
-				li.on('click', 'a', priv.exportToHTML);
-				var li = $('<li></li>').appendTo(ul);
-				$('<a href="index.php" target="_blank" class=""><i class="glyphicon glyphicon-share"></i>Export do CSV</a>').appendTo(li);
-				li.on('click', 'a', priv.exportToCSV);
-				var li = $('<li></li>').appendTo(ul);
-				$('<a href="index.php" target="_blank" class=""><i class="glyphicon glyphicon-share" title="Export do pliku CSV w kodowaniu Windows-1250"></i>Export do CSV (Windows-1250)</a>').appendTo(li);
-				li.on('click', 'a', priv.exportToCSVWinCP1250);
-			} else {
-				node = $('<span class="' + nodeClass + '"></span>');
-			}
-			currentNode.replaceWith(node);
-		};
-
-		priv.renderInlineEditBox = function() {
-			var nodeClass = 'tblAjax__' + 'inlineEditBox',
-					currentNode = $(_uiNodeCont).parent().children('.' + nodeClass),
-					node;
-			var node = $('<div class="' + nodeClass + ' modal fade" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"></div>');
-			/// console.log('L.<?php echo __LINE__; ?> Render: priv.renderInlineEditBox, currentNode.html:', currentNode.html(), currentNode);
-			var modalWrap = $('<div class="modal-dialog"></div>').appendTo(node);
-			var modalWrap = $('<div class="modal-content"></div>').appendTo(modalWrap);
-			var frmInlineEdit = $('<form style="margin:0;padding:0;"></form>').appendTo(modalWrap);
-			var iebHead = $('<div class="modal-header" style="cursor:pointer">').appendTo(frmInlineEdit);
-			$('<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="glyphicon glyphicon-remove" style="color:red"></i></button>').appendTo(iebHead);
-			$('<h4 id="myModalLabel">Edytuj</h4>').appendTo(iebHead);
-			var iebBodyWrap = $('<div class="modal-body" style="padding:0"></div>').appendTo(frmInlineEdit);
-			var iebBody = $('<div style="padding:15px"></div>').appendTo(iebBodyWrap);
-			$('<input type="hidden" name="ID" value="">').appendTo(iebBody);
-			$('<input type="hidden" name="col" value="">').appendTo(iebBody);
-			$('<div class="inlineEditBox-cnt"></div>').appendTo(iebBody);
-			var iebFoot = $('<div class="modal-footer"></div>').appendTo(frmInlineEdit);
-			$('<button class="btn btn-close" data-dismiss="modal" aria-hidden="true">Zamknij</button>').appendTo(iebFoot);
-			var iebBtnSave = $('<input type="submit" value="Zapisz" class="btn btn-primary btn-save">').appendTo(iebFoot);
-
-			frmInlineEdit.on('submit', function() {
-				var inlineEditBox$Node = $(_uiNodeCont).parent().children('.tblAjax__inlineEditBox');
-				var data = inlineEditBox$Node.find('form').serialize();
-				inlineEditBox$Node.find('.inlineEditBox-cnt').html('<span class="loading-info"> loading ...</span>');
-				function notifyAjaxCallback(data) {
-					var notify = {};
-					notify.type = (data && data.type)? data.type : '';
-					notify.msg = (data && data.msg)? data.msg : '';
-					switch (notify.type) {
-						case 'success':
-							if (!notify.msg) notify.msg = 'Dane poprawnie zaktualizowane';
-							break;
-						case 'info':
-							if (!notify.msg) notify.msg = 'Nie wprowadzono żadnych zmian';
-							break;
-						case 'error':
-							if (!notify.msg) notify.msg = 'Wystąpiły błędy';
-							break;
-						case 'warning':
-							notify.type = 'warn';
-							if (!notify.msg) notify.msg = 'Wystąpiły błędy';
-							break;
-						default:
-							notify.msg = 'Nieznany błąd';
-							if (data && data.errorCode) notify.msg += ' ' + data.errorCode;
-							notify.type = '';
-					}
-					jQuery.notify(notify.msg, notify.type);
-				}
-
-				$.ajax({
-					data: data,
-					dataType: 'json',
-					type: "POST",
-					url: 'index-ajax.php?_zasobID=<?php echo $this->_zasobID; ?>&_cls=<?php echo __CLASS__; ?>&_hash=<?php echo $this->_htmlID; ?>&_task=EDIT_INLINE_SAVE'
-				})
-				.done(function(data, textStatus, jqXHR){
-					notifyAjaxCallback(data);
-					publ.refresh();
-					inlineEditBox$Node.modal('hide');
-				})
-				.fail(function(jqXHR){// jqXHR.fail(function( jqXHR, textStatus, errorThrown ) {});
-					if (jqXHR.responseJSON) {
-						notifyAjaxCallback(jqXHR.responseJSON);
-					}
-					else {
-						var txt = jqXHR.responseText || 'Wystąpiły błędy';
-						if (jqXHR.status == 404) {
-							jQuery.notify(jqXHR.responseText, 'error');
-						} else {
-							jQuery.notify(jqXHR.responseText, 'warn');
-						}
-					}
-					inlineEditBox$Node.modal('hide');
-				});
-
-				return false;
-			});
-
-			currentNode.replaceWith(node);
-		};
-
-		priv.exportFieldChanged = function(e) {
-
-			e.stopPropagation();
-
-			var column = $(this).val();
-
-			_exportFieldsSelect[column] = !_exportFieldsSelect[column];
-		};
-
-		priv.exportToHTML = function(e) {
-			priv.exportData('html', $(this), e);
-		};
-		priv.exportToCSV = function(e) {
-			priv.exportData('csv', $(this), e);
-		};
-		priv.exportToCSVWinCP1250 = function(e) {
-			priv.exportData('csv_cp1250', $(this), e);
-		};
-		priv.exportData = function(format, node, e) {
-			var exportFields = [];
-			$.each(_exportFieldsSelect, function(col, selected) {
-				if (selected) {
-					exportFields.push(col);
-				}
-			});
-
-			if (!exportFields.length) {
-				alert('Nie wybrano żadnych pól do eksportu.');
-				e.preventDefault();
-				return false;
-			}
-
-			var limit = 10000;
-			if (!_data.total || _data.total <= 0) {
-				alert('Brak rekordów do eksportu.');
-				e.preventDefault();
-				return false;
-			}
-			if (_data.total > limit) {
-				if (!confirm('Za dużo rekordów. Wyeksportowane zostaną tylko pierwsze ' + limit + ' z ' + _data.total + ' rekordów.')) {
-					e.preventDefault();
-					return false;
-				}
-			}
-
-			var exportUrl = 'index.php?_route=ViewTableAjax&_task=export&namespace=<?= $acl->getNamespace(); ?>';
-			exportUrl += '&format=' + format;
-			exportUrl += '&flds=' + exportFields.join(',');
-			exportUrl += '&sortCol=' + (_state.filters.currSortCol || '');
-			exportUrl += '&sortDir=' + (_state.filters.currSortFlip ? "desc" : "asc");
-
-			if (Object.keys(_state.filters.filterCols).length > 0) {
-				$.each(_state.filters.filterCols, function(col, colProps) {
-					if (colProps.filter && colProps.filter.length > 0) {
-						exportUrl += '&f_' + col + '=' + colProps.filter;
-					}
-				});
-			}
-
-			// specialFilters
-			$.each(_state.specialFilters, function(groupName, btnValue) {
-				if (btnValue.length > 0) {
-					exportUrl += '&sf_' + groupName + '=' + btnValue;
-				}
-			});
-
-			if (priv.options.forceFilterInit) {
-				$.map(priv.options.forceFilterInit, function(fltrProps, fltr) {
-					exportUrl += '&sf_' + fltr + '=' + fltrProps;
-				});
-			}
-
-			node.attr('href', exportUrl);
-		};
-
-		/*
-		 calls the webservice(if defined).
-		 used only inside priv.init
-		 */
-		priv.update = function(callback) {
-			var skipCols, resetChecked;// undefined
-			if (!priv.options.url) {
-				if (priv.options.debug) console.log('no url found');
-				return;
-			}
-
-			if (priv.options.debug) console.log(p5Utils__format('requesting data from url:{0} data:{1}', [priv.options.url, JSON.stringify(priv.options.urlData) || '']));
-
-			var initUrlAdd = '';
-			var filtersInitSet = false;
-			initUrlAdd += '&currSortCol=' + _state.filters.currSortCol;
-			initUrlAdd += '&currSortFlip=' + ((_state.filters.currSortCol)? "desc" : "asc");
-			if (Object.keys(_state.filters.filterCols).length > 0) {
-				$.each(_state.filters.filterCols, function(col, colProps) {
-					if (colProps.filter && colProps.filter.length > 0) {
-						initUrlAdd += '&f_' + col + '=' + encodeURIComponent(colProps.filter);
-						filtersInitSet = true;
-					}
-				});
-			}
-			$.each(_state.specialFilters, function(groupName, btnValue) {
-				if (btnValue.length > 0) {
-					initUrlAdd += '&sf_' + groupName + '=' + encodeURIComponent(btnValue);
-					filtersInitSet = true;
-				}
-			});
-
-			if (priv.options.forceFilterInit) {
-				$.map(priv.options.forceFilterInit, function(fltrProps, fltr) {
-					initUrlAdd += '&f_' + fltr + '=' + encodeURIComponent(fltrProps);
-					filtersInitSet = true;
-				});
-			}
-
-			// p5UI__notifyAjaxCallback({type: 'info', msg: 'pobieranie danych (init) ...'});
-			window.fetch(priv.options.url + initUrlAdd, {
-				method: priv.options.urlPost ? 'POST' : 'GET',
-				credentials: 'same-origin',// add cookies
-			}).then(function (response) {
-				return response.json()
-			}).then(function (data) {
-				if (priv.options.debug) console.log('loadDataAjax:fetch:update: request finished response data:', data);
-				if ('success' == data.type) {
-					// p5UI__notifyAjaxCallback(data);
-					return data;
-				} else if ('error' == data.type) {
-					p5UI__notifyAjaxCallback(data);
-				} else {
-					p5UI__notifyAjaxCallback(data);
-				}
-				return null;
-			}).then(function (data) {
-				if (priv.options.debug) console.log('loadDataAjax:fetch:update: request finished data:', data);
-				if (!data) return;
-				if (data && data.cols) {
-					priv.setStateCols(data.cols, data.primaryKey);
-				}
-
-				// set initial filters (_state.filters.filterCols)
-				if (filtersInitSet && _data && _data.cols) {
-					$.map(reqData, function(fltrValue, fltr){
-						var colName = null;
-						if (fltr.substr(0, 3) == 'sf_') {
-							colName = fltr.substr(3);
-						}
-						else if (fltr.substr(0, 2) == 'f_') {
-							colName = fltr.substr(2);
-						}
-
-						if (colName && _data.cols[colName]) {
-							//add the filter to the filter array
-							_state.filters.filterCols[colName] = {
-								filter: fltrValue,
-								colName: colName
-							};
-						}
-					});
-				}
-
-				// assign the new state (data)
-				state = {data: {}};
-				if (!skipCols) state.data.cols = data.cols || {};
-				state.data.rows = data.rows || [];
-				state.data.total = data.total || 0;
-				state.data.primaryKey = data.primaryKey || 'ID';
-				state.page = data.page || 0;
-				state.pageSize = data.pageSize || priv.options.pageSize;
-				state.filters = data.filters || {};
-				priv.setState(state);
-
-				if (typeof callback == "function") {
-					callback.call(this);
-				}
-			}).catch(function (e) {
-				console.log('loadDataAjax:fetch: ERR:', e);
-			});
-		};
-
-		/*
-		 assigns the new data.
-		 */
-		priv.setState = function(state) {
-			var oldState = _state; // TODO: use to check what really changed (use extend!)
-			var renderParts = {};
-			if (state.data) {
-				if (state.data.cols && Object.keys(state.data.cols).length > 0) {// TODO: never happen, but if happend then rerender all
-					priv.setStateCols(state.data.cols, state.data.primaryKey);
-					priv.setStateData(state.data);
-				} else {
-					priv.setStateData(state.data);
-				}
-				renderParts['body'] = true;
-				renderParts['foot_pagination'] = true;
-			}
-			if (state.hasOwnProperty('specialFilters')) {
-				_state.specialFilters = state.specialFilters;
-				renderParts['head__specialFilters'] = true;
-			}
-			if (state.hasOwnProperty('page')) {
-				if (state.page != _state.page) {
-					_state.page = state.page;
-					renderParts['foot_pagination'] = true;
-				}
-			}
-			if (state.hasOwnProperty('pageSize')) {
-				if (state.pageSize != _state.pageSize) {
-					_state.pageSize = state.pageSize;
-					renderParts['foot_pagination'] = true;
-				}
-			}
-			if (state.hasOwnProperty('filters')) {
-				priv.setStateFilters(state.filters);
-				renderParts['head__specialFilters'] = true;
-				renderParts['foot_pagination'] = true;
-			}
-
-			renderParts = Object.keys(renderParts);
-
-			if (priv.options.debug) console.log('setState::renderParts: ', renderParts);//TODO:DBG:RMME
-			if (renderParts.length > 0) {
-				jQuery(_uiNodeCont).trigger('TableAjax:render', renderParts);
-			}
-		};
-
-		priv.setStateCols = function(cols, primaryKey) {
-			if (priv.options.debug) console.log('priv.setStateCols: ', {primaryKey: primaryKey, cols: cols});
-			// console.log("priv.setStateCols");
-			// console.trace();
-			_state.cols = cols;
-			_state.primaryKey = primaryKey || _state.primaryKey;
-			// fix col name - props.column
-			$.each(_state.cols, function(col, props) {
-				props.column = col;
-			});
-			// fix col types - default 'string'
-			$.each(_state.cols, function(col, props) {
-				if (!props.type) cols[col].type = "string";
-			});
-			// fix props.filter - set true if not set - TODO: allow filter this col?
-			$.each(_state.cols, function(col, props) {
-				if (props.filter == undefined) props.filter = true;
-			});
-			if (_state.primaryKey) {
-				//create a unique column definition
-				_state.cols["unique"] = {
-					column: "unique",
-					type: "unique",
-					index: -1,
-					hidden: true
-				};
-			}
-			_state.colsSorted = Object.keys(_state.cols).sort(function(a, b) {
-				return _state.cols[a].index - _state.cols[b].index;
-			});
-		};
-
-		priv.setStateFilters = function(stateFilters) {
-			var newFilterCols = {},
-					newSpecialFilters = {};
-			$.map(stateFilters, function(fltrProps, fltr) {
-				switch (fltr) {
-					case 'currSortCol': _state.filters.currSortCol = fltrProps; break;
-					case 'currSortFlip': _state.filters.currSortFlip = (fltrProps == 'desc')? true : false; break;
-					default:
-						if (fltr.substr(0, 2) == 'f_') {
-							newFilterCols[fltr.substr(2)] = {
-								filter: fltrProps,
-								colName: fltr.substr(2)
-							};
-						}
-						else if (fltr.substr(0, 3) == 'sf_') {
-							var groupName = fltr.substr(3);
-							if (priv.options.specialFilterFunctions.hasOwnProperty(groupName)) {
-								newSpecialFilters[groupName] = fltrProps;
-							}
-						}
-				}
-			});
-			_state.filters.filterCols = newFilterCols;
-			_state.specialFilters = newSpecialFilters;
-		};
-
-		priv.setStateData = function(pData, skipCols, resetChecked) {
-			var data = $.extend(true, {}, pData);
-			data.cols = _state.cols;// always use old cols - change cols mved to priv.setStateCols
-
-			_data = data;
-			_data.rowsOrg = _data.rows;
-
-			//we might have more/less data now. Recalculate stuff.
-			if (_state.page > 1) {
-				_totalPages = Math.ceil(_data.total / priv.options.pageSize);
-			}
-
-			//keep any previously checked rows around?
-			if (resetChecked === true || resetChecked === undefined)
-				_uniqueCols = {};
-			else {
-				for (var key in _uniqueCols)
-					_uniqueCols[key] = priv.getRow(key);
-			}
-
-			if (_state.primaryKey) {
-				//add rows that needs to be pre-checked
-				$.each(_data.rows, function(index, row) {
-					if (row["checked"] === true)
-						_uniqueCols[row[_state.primaryKey]] = row;
-				});
-			}
-		};
-
-		/*
-		 sorts the data on the current sorting column
-		 */
-		priv.sort = function() {
-			if (!_data.cols[_state.filters.currSortCol]) _state.filters.currSortCol = "";
-			if (!_state.filters.currSortCol) return;
-
-			publ.loadPage(0);
-		};
-
-		/*
-		 helper that returns the underlying data by the unique value
-		 */
-		priv.getRow = function(uniqueValue) {
-			var row;
-			$.each(_data.rowsOrg, function(i, r) {
-				if (r[_state.primaryKey] == uniqueValue) {
-					row = r;
-					return false;
-				}
-			});
-			return row;
-		};
-
-		/*
-		 when: typing a filter
-		 what: triggers filtering on the value
-		 */
-		priv.filterChanged = function(e) {
-			//clear old timer if we're typing fast enough
-			if (priv.options.debug) console.log('filterChanged::#1');
-			if (_filterTimeout) {
-				clearTimeout(_filterTimeout);
-				if (priv.options.debug) console.log('filterChanged::#2 previous filtering cancelled');
-			}
-
-			var filter = this.value
-				, fltr = $(this)
-				, col = _data.cols[e.data.column]
-				, timeout = 450;
-
-			if (this.value) {
-				if (!fltr.hasClass('filter-active')) {
-					fltr.addClass('filter-active');
-				}
-			} else {
-				if (fltr.hasClass('filter-active')) {
-					fltr.removeClass('filter-active');
-				}
-			}
-
-			//boolean filters needs some special care
-			if (col.type == "bool" || col.type == "unique") {
-				timeout = 0;
-				var elem = $(this);
-				var cssClass = 'indeterminate';
-				if (elem.hasClass(cssClass)) {
-					e.preventDefault();
-					elem.removeClass(cssClass);
-					filter = true;
-				} else {
-					if (!elem.is(':checked')) {
-						filter = false;
-					} else {
-						elem.addClass(cssClass);
-						filter = '';
-					}
-				}
-			}
-
-			//add the filter to the filter array
-			_state.filters.filterCols[col.column] = {
-				filter: filter,
-				colName: col.column
-			};
-			//wait a few deciseconds before filtering
-			_filterTimeout = setTimeout(function(filterValue, colName) {
-				_state.filters.filterCols[colName] = {
-					filter: filterValue,
-					colName: colName
-				};
-				priv.filter();
-			}, timeout, filter, col.column);
-		};
-
-		priv.filtersClean = function(e) {
-			if (_filterTimeout) {
-				clearTimeout(_filterTimeout);
-				if (priv.options.debug) console.log('filtering cancelled - filtersClean');
-			}
-
-			var filtersActive = false;
-			$.map(_state.filters.filterCols, function(colProps, col) {
-				if (colProps.filter.length > 0) {
-					filtersActive = true;
-					colProps.filter = '';
-				}
-			});
-
-			if (filtersActive) {
-				// TODO: dont remove value from forceFilterInit fields - no field names in search fields?
-				/// TODO: rerender _head (thead.filter)
-				var elems = _uiNode$Table.find('thead').find('.filter').find('input');
-				elems.val('').prop('disabled', false);
-				elems.removeClass('filter-active');
-				priv.filter();
-			}
-			return false;
-		},
-
-		/**
-		 * Filters the data.
-		 */
-		priv.filter = function() {
-			if (!priv.options.filter) return;
-			if (Object.keys(_state.filters.filterCols).length == 0) return;
-			priv.loadPage(0);
-		};
-
-		/**
-		 * Filters the data by specialFilter.
-		 * when: click on special filter
-		 * what: triggers filtering on the value
-		 */
-		priv.specialFilterChanged = function(e) {
-			if (priv.options.debug) console.log('specialFilterChanged e.data:', e.data);
-			var state = {};
-			state.specialFilters = _state.specialFilters;
-			state.specialFilters[e.data.group] = e.data.value;
-			priv.setState(state);
-
-			publ.loadPage(0);
-		};
-
-		priv.longTextChanged = function(e) {
-			priv.options.longDesc = !priv.options.longDesc;
-			_bodyNode.find('td').each(function(ind, el){
-				var $el = jQuery(el);
-				if (!$el.attr('class') || $el.attr('class') == 'tbl-short-txt') {
-					//console.log(el.attr('class') + ': ' + el.text());
-					if (el.firstChild && el.firstChild.nodeName == 'SPAN') {
-						if (priv.options.longDesc) {
-							$el.addClass('tbl-short-txt');
-							var $elSpan = jQuery(el.firstChild);
-							$elSpan.tooltip({title: $elSpan.text(), placement: 'left'});
-						} else {
-							$el.removeClass('tbl-short-txt');
-						}
-					}
-				}
-			});
-			return false;
-		};
-
-		priv.mapEditorHide = function() {
-			priv.options.mapEditor = false;
-			if ('window' == priv.options.mapEditorContainer) {
-				//_mapEditorDialog.dialog("close");
-				_mapEditorDialog.dialog("destroy");
-			}
-			_mapEditorWrap.hide();
-			_mapEditor.hide();
-		};
-
-		priv.mapEditorShow = function() {
-			//console.log('TODO:mapEditorShow():', priv.options.mapEditorContainer, 'visible:', priv.options.mapEditor);
-			priv.options.mapEditor = true;
-			_mapEditor.show();
-			_mapEditorWrap.show();
-			_mapEditor.TableAjaxMap({
-				//debug: true,
-				wpsUrl: 'http://biuro.biall-net.pl/wps',
-				wfsUrl: 'http://biuro.biall-net.pl/wps',
-				showAddLayerWidget: <?php echo ($this->hasAdditionalLayers())? 'true' : 'false'; ?>,
-				zoomStrategyActivate: 14,
-				layerName: '<?php echo $this->getLabelHtml(); ?>',
-				addBtn: {
-					title: 'Przenieś mapę do okna',
-					displayClass: 'mapEditor-btnBackToWindow',
-					trigger: function() {
-						priv.mapEditorHide();
-						priv.options.mapEditorContainer = 'window';
-						priv.mapEditorShow();
-						priv.resizableMapDockNode('destroy');
-					}
-				},
-				onSaveFeature: function(selectedRecordId, selectedFeatureExtent) {
-					if (priv.options.debug) console.log('onSaveFeature (',selectedRecordId,'/',selectedFeatureExtent,') ', this);
-					if (!selectedRecordId) {
-						alert('Brak zaznaczonego rekordu - wybierz rekord z tabeli');
-						return;
-					}
-
-					function notifyAjaxCallback(data) {
-						var notify = {};
-						notify.type = (data && data.type)? data.type : '';
-						notify.msg = (data && data.msg)? data.msg : '';
-						switch (notify.type) {
-							case 'success':
-								if (!notify.msg) notify.msg = 'Aktualizacja danych dla rekordu ' + selectedRecordId;
-								break;
-							case 'info':
-								if (!notify.msg) notify.msg = 'Nie wprowadzono żadnych zmian';
-								break;
-							case 'error':
-								if (!notify.msg) notify.msg = 'Wystąpiły błędy';
-								break;
-							case 'warning':
-								notify.type = 'warn';
-								if (!notify.msg) notify.msg = 'Wystąpiły błędy';
-								break;
-							default:
-								notify.msg = 'Nieznany błąd';
-								if (data && data.errorCode) notify.msg += ' ' + data.errorCode;
-								notify.type = '';
-						}
-						jQuery.notify(notify.msg, notify.type);
-					}
-
-					$.ajax({
-						data: {polygon: selectedFeatureExtent},
-						dataType: 'json',
-						type: "POST",
-						url: 'index-ajax.php?_zasobID=<?php echo $this->_zasobID; ?>&_cls=<?php echo __CLASS__; ?>&_hash=<?php echo $this->_htmlID; ?>&_task=THE_GEOM_SAVE&ID=' + selectedRecordId
-					})
-					.done(function(data, textStatus, jqXHR){
-						notifyAjaxCallback(data);
-						//jQuery.notify('Aktualizacja danych dla rekordu ' + selectedRecordId, 'success');
-						publ.loadPage(0);// TODO: reload table data
-					})
-					.fail(function(jqXHR){// jqXHR.fail(function( jqXHR, textStatus, errorThrown ) {});
-						if (jqXHR.responseJSON) {
-							notifyAjaxCallback(jqXHR.responseJSON);
-						}
-						else {
-							var txt = jqXHR.responseText || 'Wystąpiły błędy';
-							if (jqXHR.status == 404) {
-								jQuery.notify(jqXHR.responseText, 'error');
-							} else {
-								jQuery.notify(jqXHR.responseText, 'warn');
-							}
-						}
-					});
-
-				},
-				onSelectBox: function(bounds) {
-					if (undefined !== OpenLayers.Bounds && bounds instanceof OpenLayers.Bounds) {
-						var column = 'the_geom';
-						if (undefined !== _filterFields['the_geom']) {
-							var filter = 'BBOX:' + bounds.top + ',' + bounds.right + ',' + bounds.bottom + ',' + bounds.left;
-							_filterFields[column].val(filter);
-							_state.filters.filterCols[column] = {
-								filter: filter,
-								colName: column
-							};
-							priv.filter();
-						}
-					}
-				}
-			});
-
-			priv.mapEditorShowElement();
-		};
-
-		priv.mapEditorShowElement = function() {
-			if ('window' == priv.options.mapEditorContainer) {
-				var mapEditor = _mapEditorWrap.children('.mapEditor-map');
-				if (!mapEditor || !mapEditor.length) {
-					var tblCont = jQuery(_uiNodeCont).parent('.AjaxTableCont');
-					var mapEditor = tblCont.children('.AjaxTableCont-mapEditorContainer').children('.mapEditor-map');
-					if (!mapEditor || !mapEditor.length) {
-						// TODO: create new map
-					}
-					_mapEditorWrap.append(mapEditor);
-				}
-
-				_mapEditorWrap.css({padding:'5px'});
-				_mapEditorDialog = _mapEditorWrap.dialog({
-					width: 540,
-					minWidth: 400,
-					minHeight: 400,
-					open: function(e, ui) {
-						var n = jQuery(this),
-								mapWrap = n.children(":first"),
-								map = mapWrap.children(":first");
-						map.css({width: n.width() - 5, height: n.height() - 5});
-						_mapEditor.TableAjaxMapUpdateSize();
-					},
-					resizeStart: function(e, ui) {
-						jQuery(this).children(":first").children(":first").css({display:'none'});
-					},
-					resizeStop: function(e, ui){
-						var n = jQuery(this),
-								map = n.children(":first").children(":first");
-						n.css({width: n.parent().width()});
-						map.css({display: 'block', height: n.height(), width: n.width()});
-						_mapEditor.TableAjaxMapUpdateSize();
-					},
-					dragStop: function(e, ui){
-						_mapEditor.TableAjaxMapUpdateSize();// to prevent drag-zoom error
-					}
-				});
-				_mapEditorWrap.bind('dialogclose', function(e) {
-					priv.options.mapEditor = false;
-				});
-				var dialogTitleBar = _mapEditorDialog.parent().parent().find('.ui-dialog-titlebar');
-				var dialogTitleBarCloseBtn = dialogTitleBar.find('.ui-dialog-titlebar-close');
-				if (dialogTitleBarCloseBtn) {
-					var btnToggle = jQuery('<span style="width:18px;height:18px;position:absolute;top:50%;right:25px;margin-top:-9px;" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-icon-only"><i style="margin-top:3px;" class="glyphicon glyphicon-fullscreen"></i></span>');
-					btnToggle.on('click', function(e) {
-						priv.options.mapEditorContainer = 'dock';
-						_mapEditorWrap.dialog('close');
-						priv.mapEditorShowElement();
-					});
-					btnToggle.insertBefore(dialogTitleBarCloseBtn);
-				}
-			}
-			else if ('dock' == priv.options.mapEditorContainer) {
-				priv.resizableMapDockNode('prepare');
-				_mapEditor.TableAjaxMapUpdateSize();
-			}
-		};
-
-		priv.resizableMapDockNode = function(task) {
-			if ('prepare' == task) {
-				var mapEditor = _mapEditorWrap.children('.mapEditor-map');
-				if (mapEditor && mapEditor.length) {// map is inside _mapEditorWrap (window)
-					var mapDockNode = jQuery('<div class="AjaxTableCont-mapEditorContainer"></div>');
-					var tblCont = jQuery(_uiNodeCont).parent('.AjaxTableCont');
-					tblCont.children('.AjaxTableCont-mapEditorContainer').remove();
-					var breadcrumb = tblCont.children('.breadcrumb');
-					if (!breadcrumb || !breadcrumb.length) {
-						tblCont.prepend(mapDockNode);
-					} else {
-						mapDockNode.insertAfter(breadcrumb);
-					}
-					mapDockNode.append(mapEditor);
-				}
-				var mapDockResizable = mapEditor.parent();
-				mapDockResizable.resizable({handles: 's', minHeight: 300, minWidth: 300});
-				var resizeLineHeigth = 6;
-				mapDockResizable.children(":first").css({height: 'auto'});
-				mapDockResizable.css({marginBottom: resizeLineHeigth+'px'});
-				mapEditor.css({marginBottom: resizeLineHeigth+'px'});
-				mapEditor.children(":first").css({width: mapEditor.width()});
-				mapDockResizable.find('.ui-resizable-s').css({height:resizeLineHeigth+'px', bottom: '-'+resizeLineHeigth+'px'});
-				mapDockResizable.on('resizestart', function(event, ui) {
-					ui.element.children(":first").children(":first").css({display:'none'})
-				});
-				mapDockResizable.on('resizestop', function(event, ui) {
-					ui.element.children(":first").children(":first").css({display:'block', height: ui.element.height()});
-				});
-			} else if ('destroy' == task) {
-				var mapDockResizable = jQuery(_uiNodeCont).parent('.AjaxTableCont').children('.AjaxTableCont-mapEditorContainer');
-				mapDockResizable.resizable('destroy');
-				mapDockResizable.remove();
-			}
-		};
-
-		priv.mapEditorChanged = function(e) {
-			if (priv.options.debug) console.log('mapEditorChanged option(',priv.options.mapEditor,')');
-			priv.options.mapEditor = !priv.options.mapEditor;
-			if (priv.options.mapEditor) {
-				priv.mapEditorShow();
-			}
-			else {
-				priv.mapEditorHide();
-			}
-			return false;
-		};
-
-		/*
-		 when: changing page in pager
-		 what: triggers table to be created with new page
-		 */
-		priv.pageChanged = function(e) {
-			e.preventDefault();
-			var totalPages = Math.ceil(_data.total / _state.pageSize);
-			if (e.data.pageIndex < 1 || e.data.pageIndex > totalPages) return;
-
-			//set the new page
-			_state.page = e.data.pageIndex;
-
-			publ.loadPage(_state.page);
-		};
-
-		/*
-		 when: changing pagesize in pagesize dropdown
-		 what: triggers table to be created with new pagesize
-		 */
-		priv.pageSizeChanged = function(e) {
-			e.preventDefault();
-			var val = $(this).text();
-			if (priv.options.debug) console.log(p5Utils__format('pagesize changed to:{0}', [val]));
-
-			if (parseInt(val) == priv.options.pageSize) {
-				return false;
-			}
-			priv.options.pageSize = parseInt(val);
-
-			publ.loadPage(1, priv.options.pageSize);
-
-			jQuery(_uiNodeCont).trigger('TableAjax:render', ['body', 'foot_pagination']);
-
-			priv.saveProfilePageSize(priv.options.pageSize);
-		};
-
-		/*
-		 when: clicking a column
-		 what: triggers table to be sorted by the column
-		 */
-		priv.columnClicked = function(e) {
-			e.preventDefault();
-			if (priv.options.debug) console.log(p5Utils__format('col:{0} clicked', [e.data.column]));
-
-			//set the new sorting column
-			if (_state.filters.currSortCol == e.data.column) _state.filters.currSortFlip = !_state.filters.currSortFlip;
-			_state.filters.currSortCol = e.data.column;
-			/// TODO: var state = {}; state.currSortCol = ''; state.currSortFlip = ''; priv.setState(state);
-
-			_headSort = undefined;
-			_body = undefined;
-			priv.sort();
-		};
-
-		priv.saveProfilePageSize = function(pageSize) {
-			var reqData = {};
-			reqData.pageSize = pageSize;
-
-			$.ajax({
-				data: reqData,
-				type: "POST",
-				dataType: 'json',
-				// async: true,
-				url: 'index-ajax.php?_zasobID=<?php echo $this->_zasobID; ?>&_cls=<?php echo __CLASS__; ?>&_hash=<?php echo $this->_htmlID; ?>&_task=PAGE_SIZE_SAVE'
-			})
-			.done(function(data, textStatus, jqXHR){
-				if (data && data.type && data.type == 'info') {
-					jQuery.notify('Nie wprowadzono żadnych zmian', 'info');
-				} else {
-					jQuery.notify('Zapisano ustawienia', 'success');
-				}
-			})
-			.fail(function(){
-				jQuery.notify('Wystąpił błąd podczas zapisywania ustawień', 'error');
-			});
-		};
-
-		priv.saveHiddenCols = function() {
-			var reqData = {};
-
-			$.each(_data.cols, function(col, props) {
-				if (props.type != "unique" && col != 'unique' && col != _state.primaryKey) {
-					reqData[col] = (props.hidden)? 'HIDE' : 'SHOW';
-				}
-			});
-
-			$.ajax({
-				data: reqData,
-				type: "POST",
-				dataType: 'json',
-				// async: true,
-				url: 'index-ajax.php?_zasobID=<?php echo $this->_zasobID; ?>&_cls=<?php echo __CLASS__; ?>&_hash=<?php echo $this->_htmlID; ?>&_task=HIDDEN_COLS_SAVE'
-			})
-			.done(function(data, textStatus, jqXHR){
-				if (data && data.type && data.type == 'info') {
-					jQuery.notify('Nie wprowadzono żadnych zmian', 'info');
-				} else {
-					jQuery.notify('Zapisano ustawienia', 'success');
-				}
-			})
-			.fail(function(){
-				jQuery.notify('Wystąpił błąd podczas zapisywania ustawień', 'error');
-			});
-		};
-
-		/*
-		 when: clicking a column in columnpicker
-		 what: triggers table to show/hide the column
-		 */
-		priv.columnPickerClicked = function(e) {
-			e.stopPropagation();
-
-			var column = $(this).val();
-
-			//toggle column visibility
-			priv.modelColFilter_toggleColumn(column);
-
-			priv.saveHiddenCols();
-
-			//_data.cols[column].index = new priv.ext.XDate();
-			jQuery(_uiNodeCont).trigger('TableAjax:render', ['head', 'body']);
-		};
-
-		priv.modelColFilter_init = function() {// run only once, set _state._modelColFilter {selected, filters: [ {name, label, visibleCols} ]}
-			var isAllSelected = true;
-			if (undefined === _state._modelColFilter) {
-				var columnFilters = [];
-				{
-					var fltrAll = {'name': 'all', 'label': 'Wszystkie', visibleCols: []};
-					$.each(_data.cols, function(col, props) {
-						if ("unique" == props.type) return;
-						fltrAll.visibleCols.push(col);
-						if (_data.cols[col].hidden) isAllSelected = false;
-					});
-					columnFilters.push(fltrAll);
-				}
-				// {
-				// 	var fltrMostUsed = {'name': 'most_used', 'label': 'Najczęściej używane', visibleCols: []};
-				// 	var fltrMostUsedLimit = 10;
-				// 	$.each(_data.cols, function(col, props) {
-				// 		if ("unique" == props.type) return;
-				// 		if (fltrMostUsedLimit-- > 0) {
-				// 			fltrMostUsed.visibleCols.push(col);
-				// 		}
-				// 	});
-				// 	columnFilters.push(fltrMostUsed);
-				// }
-
-				_state._modelColFilter = {
-					selected: (isAllSelected)? 'all' : null,
-					filters: columnFilters,
-					saveBtn: $('<button class="btn btn-xs btn-primary" disabled="disabled">Zapisz widoczne kolumny</button>')
-				};
-				_state._modelColFilter.saveBtn.on('click', priv.modelColFilter_saveBtnClicked)
-
-				if (priv.options.userTableFilterUrl) {
-					window.fetch(priv.options.userTableFilterUrl, {
-						method: 'POST',
-						headers: {
-							'Content-Type': 'application/json'
-						},
-						credentials: 'same-origin',// add cookies
-						body: JSON.stringify({
-							namespace: priv.options.namespace,
-						})
-					}).then(function (response) {
-						return response.json()
-					}).then(function (result) {
-						if ('success' == result.type) {
-							// p5UI__notifyAjaxCallback(result)
-							_state._modelColFilter.filters = _state._modelColFilter.filters.filter(function (filter) {
-								return ('all' === filter.name || 'most_used' == filter.name)
-							}).concat(Object.keys(result.data).map(function (fltr) {
-								return {
-									name: fltr,
-									label: fltr,
-									visibleCols: result.data[fltr].split(',')
-								}
-							}))
-							jQuery(_uiNodeCont).trigger('TableAjax:render', ['foot__columnPicker']);
-						} else {
-							p5UI__notifyAjaxCallback(result)
-						}
-					}).catch(function (e) {
-						// TODO: show error ("ajax response error: " + e)
-					});
-				}
-			}
-		};
-		priv.modelColFilter_onClickRemoveFilter = function (e) {
-			e.preventDefault()
-			e.stopPropagation()
-			var filtrName = $(this).data('col_filter')
-			if (!filtrName) return
-			window.fetch('<?= Request::getPathUri() . "index.php?_route=ViewTableAjax&_task=rmUserTableFilterAjax" ?>', {
-				method: 'POST',
-				headers: {
-					'Content-Type': 'application/json'
-				},
-				credentials: 'same-origin',// add cookies
-				body: JSON.stringify({
-					namespace: '<?= $acl->getNamespace(); ?>',
-					filtrName: filtrName,
-				})
-			}).then(function (response) {
-				return response.json()
-			}).then(function (result) {
-				if ('success' == result.type) {
-					p5UI__notifyAjaxCallback(result)
-					var userTableFilters = result.data;// resolve(result.data)
-					_state._modelColFilter.filters = _state._modelColFilter.filters.filter(function (filter) {
-						return ('all' === filter.name || 'most_used' == filter.name)
-					}).concat(Object.keys(userTableFilters).map(function (fltr) {
-						return {
-							name: fltr,
-							label: fltr,
-							visibleCols: userTableFilters[fltr].split(',')
-						}
-					}))
-					jQuery(_uiNodeCont).trigger('TableAjax:render', ['foot__columnPicker']);
-				} else {
-					p5UI__notifyAjaxCallback(result)
-				}
-			}).catch(function (e) {
-				p5UI__notifyAjaxCallback({ type: 'error', msg: new String(e) })
-			});
-		};
-		priv.modelColFilter_saveBtnClicked = function (e) {
-			swal({
-				title: 'Zapisz widoczne kolumny',
-				html: '',
-				animation: true,
-				input: 'text',
-				inputPlaceholder: 'nazwa filtra',
-				// inputValue: null,
-				// inputAttributes: {'step': '0.01'},
-				showCancelButton: true,
-				confirmButtonText: 'Zapisz',
-				showLoaderOnConfirm: true,
-				showCloseButton: true,
-				preConfirm: function(filtrName) {
-					return new Promise(function(resolve, reject) {
-						if (!filtrName) reject('Proszę podać nazwę filtra')
-						if (filtrName.length > 255) reject('Nazwa za długa')
-						window.fetch('<?= Request::getPathUri() . "index.php?_route=ViewTableAjax&_task=addUserTableFilterAjax" ?>', {
-							method: 'POST',
-							headers: {
-								'Content-Type': 'application/json'
-							},
-							credentials: 'same-origin',// add cookies
-							body: JSON.stringify({
-								namespace: '<?= $acl->getNamespace(); ?>',
-								filtrName: filtrName,
-								visibleCols: Object.keys(_data.cols).filter(function(col) {
-									return !_data.cols[col].hidden
-								}).join(','),
-							})
-						}).then(function (response) {
-							return response.text()
-						}).then(function (responseText) {
-							try {
-								// json = response.json() // BUG: error trafia do catch promisów, zamiast aktualnego bloku
-								// -- obiekt response pewnie jest powiązany z Promisem z fetch
-								// -- response.json() zwraca Promise tak samo jak response.text()
-								return JSON.parse(responseText)
-							} catch (e) {
-								throw responseText
-							}
-						}).then(function (result) {
-							if ('success' == result.type) {
-								p5UI__notifyAjaxCallback(result)
-								resolve(result.data)
-							} else {
-								p5UI__notifyAjaxCallback(result)
-								reject(result.msg || "Wystąpił błąd!")
-							}
-						}).catch(function (e) {
-							reject("ajax response error: " + e)
-						});
-			    })
-			  },
-			  allowOutsideClick: false
-			}).then(function(userTableFilters) {
-				_state._modelColFilter.filters = _state._modelColFilter.filters.filter(function (filter) {
-					return ('all' === filter.name || 'most_used' == filter.name)
-				}).concat(Object.keys(userTableFilters).map(function (fltr) {
-					return {
-						name: fltr,
-						label: fltr,
-						visibleCols: userTableFilters[fltr].split(',')
-					}
-				}))
-				jQuery(_uiNodeCont).trigger('TableAjax:render', ['head', 'body', 'foot__columnPicker']);
-			}).catch(function(e) {
-				// eg. hit Cancel
-			})
-		}
-		priv.modelColFilter_getSaveBtn = function () {
-			priv.modelColFilter_init();
-			return _state._modelColFilter.saveBtn
-		}
-		priv.modelColFilter_getFilters = function() {
-			priv.modelColFilter_init();
-			return _state._modelColFilter.filters;
-		};
-		priv.modelColFilter_getSelected = function() {
-			priv.modelColFilter_init();
-			return _state._modelColFilter.selected;
-		};
-		priv.modelColFilter_setFilter = function(filterKey) {
-			priv.modelColFilter_init();
-			_state._modelColFilter.selected = filterKey;
-
-			priv.modelColFilter_uiUncheckAllColFilters();
-			priv.modelColFilter_uiCheckColFilter(filterKey);
-		};
-		priv.modelColFilter_toggleColumn = function(column, value) {
-			priv.modelColFilter_init();
-			_state._modelColFilter.selected = null;
-			if (!_data.cols[column] || !_data.cols[column]) return;
-			_data.cols[column].hidden = (undefined !== value)? value : !_data.cols[column].hidden;
-
-			priv.modelColFilter_uiUncheckAllColFilters();
-			{
-				var isAllSelected = true;
-				$.each(_data.cols, function(col, props) {
-					if ("unique" == props.type) return;
-					if (_data.cols[col].hidden) isAllSelected = false;
-				});
-				if (isAllSelected) {
-					_state._modelColFilter.selected = 'all';
-					priv.modelColFilter_uiCheckColFilter('all');
-				}
-			}
-		};
-		priv.modelColFilter_uiUncheckAllColFilters = function() {
-			var nodeClass = 'tblAjax__' + 'footer__toolbar__columnPicker',
-					currentNode = $(_uiNodeCont).next('.foot').find('.' + nodeClass)
-			;
-			currentNode.find('input[type="radio"]').prop("checked", false);
-		};
-		priv.modelColFilter_uiCheckColFilter = function(filterKey) {
-			var nodeClass = 'tblAjax__' + 'footer__toolbar__columnPicker',
-					currentNode = $(_uiNodeCont).next('.foot').find('.' + nodeClass)
-			;
-			currentNode.find('input[type="radio"]').each(function(idx, input) {
-				var input$ = jQuery(input);
-				if (filterKey == jQuery(input$).parent().data('col_filter')) {
-					input$.prop("checked", true);
-				} else {
-					input$.prop("checked", false);
-				}
-			});
-		};
-
-		priv.columnPickerLinkClicked = function(e) {
-			e.stopPropagation();
-			var input$ = $(this).find('input'),
-					col_filter = $(this).data('col_filter'),
-					columnFilters = priv.modelColFilter_getFilters()
-			;
-
-			if (col_filter) {
-				var filter = null;
-				$.each(columnFilters, function(idx, colFltr) {
-					if (col_filter == colFltr.name) {
-						filter = colFltr;
-					}
-				});
-				if (!filter) return;
-				$.each(_data.cols, function(col, props) {
-					var isHidden = (-1 === filter.visibleCols.indexOf(col));
-					priv.modelColFilter_toggleColumn(col, isHidden);
-				});
-				priv.modelColFilter_setFilter(col_filter);
-				priv.saveHiddenCols();
-				jQuery(_uiNodeCont).trigger('TableAjax:render', ['head', 'body', 'foot__columnPicker']);
-			} else {
-				e.preventDefault();
-				if (input$.length != 1) return;
-				var column = input$.val();
-				if (!column) return;
-				//toggle column visibility
-				priv.modelColFilter_toggleColumn(column);
-				input$.prop("checked", !input$.is(':checked'));// update view
-				priv.saveHiddenCols();
-				//_data.cols[column].index = new priv.ext.XDate();
-				jQuery(_uiNodeCont).trigger('TableAjax:render', ['head', 'body']);
-			}
-		};
-
-		priv.columnHideClicked = function(e) {
-			e.stopPropagation();
-
-			var column;
-			if (e.data && e.data.column) {
-				column = e.data.column;
-			} else {
-				return;
-			}
-
-			//toggle column visibility
-			priv.modelColFilter_toggleColumn(column);
-
-			priv.saveHiddenCols();
-
-			//_data.cols[column].index = new priv.ext.XDate();
-			jQuery(_uiNodeCont).trigger('TableAjax:render', ['head', 'body', 'foot__columnPicker']);
-		};
-
-		/*
-		 when: clicking a row checkbox
-		 what: toggles checked state on row, and add/removes it from checked array
-		 */
-		priv.rowChecked = function(e) {
-			var elem = $(this);
-
-			//get the row's unique value
-			var unique = elem.closest('tr').data('unique');
-			if (priv.options.debug) console.log(p5Utils__format('row({0}) {1}', [unique, elem.is(':checked') ? 'checked' : 'unchecked']));
-
-			//store the row in checked array
-			if (elem.is(':checked')) _uniqueCols[unique] = priv.getRow(unique);
-			else delete _uniqueCols[unique];
-		};
-
-		/*
-		 when: clicking anywhere on a row
-		 what: row data and other info is returned to caller
-		 */
-//		priv.rowClicked = function(e) {// TODO: not used
-//			if (!_state.primaryKey) {
-//				if (priv.options.debug) console.log('no unique column specified');
-//				return;
-//			}
-
-//			//gather callback data
-//			var elem = $(this);
-//			var column = _data.cols[elem.data('column')];
-//			var unique = elem.closest('tr').data('unique');
-//			var row = priv.getRow(unique);
-//			var isChecked = elem.closest('tr').find('.unique').is(':checked');
-
-//			//trigger callback
-//			if (typeof priv.options.rowClicked == 'function') {
-//				priv.options.rowClicked.call(e.target, {
-//					event: e,
-//					row: row,
-//					column: column,
-//					checked: isChecked
-//				});
-//			}
-//		};
-
-		/**
-		 * Inline edit.
-		 */
-		priv.rowDblClicked = function(e) {
-			var inlineEditBox$Node = $(_uiNodeCont).parent().children('.tblAjax__inlineEditBox');
-
-			// hide popover for typespecial fld on click
-			if (_popoverCellCurrent) {
-				_popoverCellCurrent.popover('hide');
-			}
-
-			// e.clientX: 1002; e.clientY: 245
-			if ('id' in e.data && 'col' in e.data && e.data.id > 0) {
-				inlineEditBox$Node.modal();
-				inlineEditBox$Node.show();
-				inlineEditBox$Node.on('shown.bs.modal', function(e) {
-					var dialogBox = jQuery(this).find('.modal-dialog'),
-							modalBody = dialogBox.find('.modal-body'),
-							boudingRect = dialogBox.get(0).getBoundingClientRect();
-					dialogBox.css({position: 'absolute', margin:0, top: boudingRect.top, left: boudingRect.left});
-					modalBody.css({overflow: 'scroll'});
-					dialogBox.resizable({minHeight: 300, minWidth: 300, alsoResize: modalBody});
-					dialogBox.draggable({handle: '.modal-header'});
-				});
-				inlineEditBox$Node.on('hidden.bs.modal', function(e) {
-					var dialogBox = jQuery(this).find('.modal-dialog'),
-							modalBody = dialogBox.find('.modal-body');
-					dialogBox.removeAttr('style');
-					modalBody.removeAttr('style');
-					modalBody.css({padding: '0'});
-					dialogBox.resizable();
-					dialogBox.draggable();
-					dialogBox.resizable('destroy');
-					dialogBox.draggable('destroy');
-				});
-				inlineEditBox$Node.find('input[name=ID]').val(e.data.id);
-				inlineEditBox$Node.find('input[name=col]').val(e.data.col);
-				inlineEditBox$Node.find('.inlineEditBox-cnt').html('<span class="loading-info"> loading ...</span>');
-				$.ajax({
-					url: 'index-ajax.php?_zasobID=<?php echo $this->_zasobID; ?>&_cls=<?php echo __CLASS__; ?>&_hash=<?php echo $this->_htmlID; ?>&_task=EDIT_INLINE&ID=' + e.data.id + '&col=' + e.data.col,
-					type: 'GET',
-					dataType: 'json',
-					success: function(data) {
-						if ('p5:www_link' == _.get(data, 'simpleType')) {
-							inlineEditBox$Node.find('.inlineEditBox-cnt').empty();
-							var label = jQuery('<label>');// for="' + data.htmlFieldName + '">');// "<label for="f22579" class="AjaxTableEdit-label"> ... </label>
-							label.attr('for', data.htmlFieldName);
-							label.attr('class', 'AjaxTableEdit-label');
-							inlineEditBox$Node.find('.inlineEditBox-cnt').append(label);
-							var inLabel = jQuery('<strong title="[' + data.idZasob + '] ' + data.fieldLabel + '">' + data.fieldLabel + '</strong>');
-							label.append(inLabel);
-							var frmItem = jQuery('<input>');
-							frmItem.attr('type', 'text');
-							frmItem.attr('name', data.htmlFieldName);
-							frmItem.attr('value', data.formItem.value);
-							frmItem.attr('maxlength', _.get(data, 'restrictions.maxLength', 255));
-							frmItem.attr('style', 'width:98%');
-							frmItem.attr('class', 'form-control');
-							frmItem.insertAfter(label);
-						} else {
-							var content = _.get(data, 'legacy_html', "Nieznany błąd");
-							inlineEditBox$Node.find('.inlineEditBox-cnt').html(content);
-						}
-						inlineEditBox$Node.find('.btn-save').show();
-
-						initDateTimePicker(inlineEditBox$Node);
-
-						inlineEditBox$Node.find('textarea').autosize();
-
-						var fld = inlineEditBox$Node.find('input[name^="f"]');
-						if (fld.length > 0 && !fld.hasClass('se_type-date')) {
-							fld.keydown(function(event) {
-								if (event.which == 13) {
-									event.preventDefault();
-									inlineEditBox$Node.find('form').submit();
-								}
-							});
-						}
-
-						(function setFocusAtFirstInlineEditFormFld(modal$node){
-							var fld, dbg = false;
-							if(dbg)console.log('focus...');
-							fld = modal$node.find('input[name^="f"]:first');
-							if (fld.length > 0 && !fld.hasClass('se_type-date')) {
-								if(dbg)console.log('set focus to first input element');
-								fld.focus(); return;
-							}
-							fld = modal$node.find('select[name^="f"]:first');
-							if (fld.length) {
-								if (fld.get(0).selectize) {
-									if(dbg)console.log('set focus to first typepecial element');
-								} else {
-									if(dbg)console.log('set focus to first select element');
-									fld.focus(); return;
-								}
-							}
-							if(dbg)console.log('set focus to close btn');
-							modal$node.find('.btn-close').focus();
-						})(inlineEditBox$Node);
-					},
-					error: function(err) {
-						if (priv.options.debug) console.log('err');
-					}
-				});
-			} else {
-				if (priv.options.debug) console.log('NO data');
-				return false;
-			}
-		};
-
-		priv.ajaxLoadTypeSpeciallCell = function(id, col) {
-			if (_popoverCellAjaxXhr) {
-				_popoverCellAjaxXhr.abort();
-			}
-
-			_popoverCellAjaxXhr = $.ajax({
-				type: 'GET',
-				url: 'index.php?_route=ViewTableAjax&_task=typeSpecialCell&namespace=<?= $acl->getNamespace(); ?>' + '&ID=' + id + '&col=' + col,
-				dataType: 'json',
-				contentType: "application/json; charset=utf-8",
-				data: '',
-				success: function(req){
-					if (_.get(req, 'data.tbl_id') > 0) {
-						var addHtml = '';
-						for (var i in req.data.items) {
-							var url = 'index.php?_route=ViewTableAjax';
-							url += '&namespace=' + req.namespace;
-							url += '&f_' + req.data.fld_name + '=' + req.data.items[i].id;
-							url += '&_hash=' + Math.random().toString(36).substring(2);
-							addHtml += '<a href="' + url + '">Wyszukaj ' + req.data.items[i].id + '</a>: ' + req.data.items[i].label;
-							addHtml += '<br>';
-						}
-						//_popoverCell.append(addHtml);// cache
-						if (_popoverCellCurrent) {
-							var popoverNodeId = _popoverCellCurrent.attr('aria-describedby');
-							if (popoverNodeId) {
-								jQuery('#' + popoverNodeId).find('.popoverCellContent').append(addHtml);
-							}
-						}
-					}
-				}
-			});
-
-		};
-
-		priv.popoverCellTypeSpecial = function(e) {
-			e.preventDefault();
-			e.stopPropagation();
-			if ('id' in e.data && 'col' in e.data && e.data.id > 0) {
-				var lastId = _popoverCell.data('rowid'),
-						lastCol = _popoverCell.data('col'),
-						rowPK = e.data.id,
-						colName = e.data.col
-				;
-
-				if (lastId == rowPK && lastCol == colName) {
-					//_popoverCellCurrent.popover('toggle');
-				}
-				else {
-					if (_popoverCellCurrent) {
-						_popoverCellCurrent.popover('destroy');
-					}
-
-					_popoverCell.data('rowid', rowPK);
-					_popoverCell.data('col', colName);
-					_popoverCell.html(e.data.value + '<div class="popoverCellContent loading" style="white-space:normal"></div>');
-
-					_popoverCellCurrent = jQuery(e.currentTarget);
-					// title : '<span class="text-info"><strong>title</strong></span> <button type="button" id="close" class="close">&times;</button>'
-					var opts = {
-						placement: 'left'
-						, trigger: 'click'
-					//	, title: e.data.col + '<a href="#" class="glyphicon glyphicon-remove pull-right" onclick="return hidePopover();"></a>'
-						, title: '<div style="display:block;position:relative;padding:0 20px 0 0;">' + (e.data.friendly || colName) + ' <button type="button" class="close" onclick="return hidePopover();" style="position:absolute;right:0;top:0;">&times;</button>' + '</div>'
-						, html: true
-						, content: _popoverCell.html()
-					}
-
-					_popoverCellCurrent.popover(opts);
-					if (_data.cols[colName]) {
-						if (_data.cols[colName]._tsRetId > 0) {
-							_popoverCellCurrent.on('shown.bs.popover', function() {
-								priv.ajaxLoadTypeSpeciallCell(rowPK, colName);
-							});
-						}
-					}
-					_popoverCellCurrent.popover('show');
-				}
-			} else {
-				if (priv.options.debug) console.log('NO data');
-				return false;
-			}
-			return;
-		};
-
-		priv.routeChanged = function(e) {
-			hash = e.data.hash || '';
-			if (hash.length == 0) {
-				return;
-			}
-			if (hash.substring(0, 1) != '#') {
-				hash = '#' + hash;
-			}
-			location.hash = hash;
-		};
-
-		priv.refresh = function(e) {
-			e.preventDefault();
-			publ.loadPage(_state.page);
-		};
-
-		publ.init = function(options) {
-			if (priv.options.debug) console.log('TableAjax initialization...');
-			//merge supplied options with defaults
-			$.extend(priv.options, defaults, options);
-			priv.init();
-			return publ;
-		};
-
-		publ.refresh = function() {
-			publ.loadPage(_state.page);
-		};
-
-		publ.loadPage = function(page, pageSize) {
-			priv.loadPage(page, pageSize);
-		}
-		priv.loadPage = function(page, pageSize) {
-			var resetChecked = false;
-			var reqData = {
-				page: page,
-				pageSize: (pageSize || priv.options.pageSize),
-				currSortCol: (_state.filters.currSortCol || ''),
-				currSortFlip: _state.filters.currSortFlip ? "desc" : "asc"
-			};
-			var urlAdd = '';
-			urlAdd += '&page=' + page;
-			urlAdd += '&pageSize=' + (pageSize || priv.options.pageSize);
-			urlAdd += '&currSortCol=' + (_state.filters.currSortCol || '');
-			urlAdd += '&currSortFlip=' + (_state.filters.currSortFlip ? "desc" : "asc");
-
-			if (Object.keys(_state.filters.filterCols).length > 0) {
-				$.each(_state.filters.filterCols, function(col, colProps) {
-					if (colProps.filter && colProps.filter.length > 0) {
-						urlAdd += '&f_' + col + '=' + encodeURIComponent(colProps.filter);
-					}
-				});
-			}
-
-			// specialFilters
-			$.each(_state.specialFilters, function(groupName, btnValue) {
-				if (btnValue.length > 0) {
-					urlAdd += '&sf_' + groupName + '=' + encodeURIComponent(btnValue);
-				}
-			});
-
-			if (priv.options.forceFilterInit) {
-				$.map(priv.options.forceFilterInit, function(fltrProps, fltr) {
-					urlAdd += '&f_' + fltr + '=' + encodeURIComponent(fltrProps);
-					filtersInitSet = true;
-				});
-			}
-
-			_uiNode$Table.parent().parent().addClass('AjaxTable-loading');
-
-			// p5UI__notifyAjaxCallback({type: 'info', msg: 'pobieranie danych...'});
-			window.fetch(priv.options.url + urlAdd, {
-			  method: priv.options.urlPost ? 'POST' : 'GET',
-				credentials: 'same-origin',// add cookies
-			}).then(function (response) {
-				return response.json()
-			}).then(function (data) {
-				if (priv.options.debug) console.log('loadDataAjax:fetch:loadPage: request finished, data:', data);
-				// p5UI__notifyAjaxCallback(data);
-				if ('success' == data.type) {
-					state = {data: {}};
-					state.data.cols = data.cols || {};
-					state.data.rows = data.rows || [];
-					state.data.total = data.total || 0;
-					state.data.primaryKey = data.primaryKey || 'ID';
-					state.page = data.page || 0;
-					state.pageSize = data.pageSize || priv.options.pageSize;
-					state.filters = data.filters || {};
-					priv.setState(state);
-					_uiNode$Table.parent().parent().removeClass('AjaxTable-loading');
-				} else if ('error' == data.type) {
-					p5UI__notifyAjaxCallback(data);
-				} else {
-					p5UI__notifyAjaxCallback(data);
-				}
-			}).catch(function (e) {
-				console.log('loadDataAjax:fetch: ERR:', e);
-			});
-		};
-
-		publ.getCurrentPage = function() {
-			return _state.page;
-		};
-
-		publ.popoverCellRemove = function() {
-			if (_popoverCellCurrent) {
-				_popoverCellCurrent.popover('destroy');
-			}
-			_popoverCell.data('rowid', -1);
-			_popoverCell.data('col', -1);
-			_popoverCell.html('');
-		};
-
-		return publ;
-	}
-
-	$.fn.TableAjax = function(options) {
-		options = options || {};
-		return this.each(function() {
-			options.id = this;
-			$(this).data('TableAjax', new TableAjax().init(options));
-		});
-	};
-
-	$.fn.TableAjaxLoadPage = function(page, pageSize) {
-		return this.each(function() {
-			var tblAjax = jQuery(this).data('TableAjax');
-			var curPage = page || tblAjax.getCurrentPage();
-			if (tblAjax) tblAjax.loadPage(curPage, pageSize);
-		});
-	};
-
-	$.fn.TableAjaxRefresh = function(page, pageSize) {
-		return this.each(function() {
-			var tblAjax = jQuery(this).data('TableAjax');
-			if (tblAjax) tblAjax.refresh();
-		});
-	};
-})(jQuery);
-		</script>
-<?php
+		UI::inlineJS(__FILE__ . '.TableAjax.js', [
+			'SYNC_URL' => $this->syncUrl,
+			'HTML_ID' => $this->_htmlID,
+			'NAMESPACE' => $acl->getNamespace(),
+			'URI_BASE' => Request::getPathUri(),
+			'ZASOB_ID' => $this->_zasobID,
+			'CLASS_NAME' => __CLASS__,
+			'HAS_ADDITIONAL_LAYERS' => $this->hasAdditionalLayers(),
+			'LABEL_HTML' => $this->getLabelHtml(),
+		]);
 	$filterInit = $this->_filterInit;
 	$forceFilterInit = $this->_forceFilterInit;
 	$pageSizes = $this->_pageSizes;

+ 2987 - 0
SE/se-lib/TableAjax.php.TableAjax.js

@@ -0,0 +1,2987 @@
+if (!SYNC_URL) throw "Missing SYNC_URL"; // => $this->syncUrl,
+if (!HTML_ID) throw "Missing HTML_ID"; // => $this->_htmlID,
+if (!NAMESPACE) throw "Missing NAMESPACE"; // => $acl->getNamespace()
+if (!URI_BASE) throw "Missing URI_BASE"; // => Request::getPathUri()
+if (!ZASOB_ID) throw "Missing ZASOB_ID"; // => $this->_zasobID
+if (!CLASS_NAME) throw "Missing CLASS_NAME"; // => __CLASS__
+if ("undefined" === typeof HAS_ADDITIONAL_LAYERS) throw "Missing HAS_ADDITIONAL_LAYERS"; // => $this->hasAdditionalLayers()
+if ("undefined" === typeof LABEL_HTML) throw "Missing LABEL_HTML"; // => $this->getLabelHtml()
+if (!global.jQuery) throw "Missing jQuery"
+var $ = global.jQuery
+
+var TableAjax = function() {
+	var priv = {}; //private api
+	var publ = {}; //public api
+
+	priv.options = {};
+	var defaults = {
+		namespace: '',
+		url: '', //webservice url
+		urlData: '', //webservice params
+		urlPost: false, //use POST instead of GET
+		debug: false, //prints debug info to console
+		filter: false, //show filter row
+		filterInit: false,// init filters data
+		forceFilterInit: false,
+		columnPicker: false, //show columnpicker
+		checkboxes: false, //show body checkboxes
+		actions: '', //holds action links
+		pageSize: 10, //current pagesize
+		pageSizes: [10, 20, 30, 40, 50, 'All'], //available pagesizes
+		hidePagerOnEmpty: false, //removes pager if no rows
+		types: { //type specific options
+			string: {},
+			number: {},
+			bool: {},
+			date: {}
+		},
+		rowFunctions: false,
+		tblFunctions: false,
+		specialFilterFunctions: false,
+		filtersClean: false,
+		router: false,
+		longDesc: false,
+		exportFields: [],
+		mapEditor: false,	// mapEditor visible or not
+		mapEditorContainer: 'window' // 'window' or 'dock'
+	};
+
+	var _uiNodeCont; // container holding table
+	var _uiNode$Table; // the table node
+	var _head; // table header
+	var _headSort; // table header sorting row
+	var _headFilter; // table header columns filter row
+	var _headSpecialFilter; // table header special filter row
+	var _bodyNode; // table body
+	var _body; // TODO: table body need render?
+	var _foot; // table footer
+	var _inlineEditBox; // inline edit box with form
+	var _popoverCell; // inline popover cell
+	var _popoverCellCurrent; // inline popover cell
+	var _popoverCellAjaxXhr;
+	var _mapEditor; // map editor node
+	var _mapEditorWrap; // map editor wrapper node
+	var _mapEditorDialog; // map editor jquery ui dialog node
+
+	var _state = {};// state - to replace variables below (date, ...)
+	var _data; //columns and rows
+	var _totalPages; // total pages
+	var _currDpOp; // clicked datetimepicker operator
+	var _filterFields = {}; // array with filter DOM elements
+	var _filterTimeout; // timer for delayed filtering
+	var _uniqueCols = {}; // array with checked rows /// TODO: mv to _state.
+	var _checkToggleChecked = false; // check-all toggle state
+	var _exportFieldsSelect = {};
+
+	var _tableWidth;
+
+	var _fieldWidgets = {};
+
+	/*
+	 initialize the plugin.
+	 */
+	priv.init = function() {
+		_uiNodeCont = priv.options.id;
+		_state = {};// init state
+		_state.page = 1;
+		_state.specialFilters = {};
+		_state.filters = {};
+		_state.filters.currSortCol = '';
+		_state.filters.currSortFlip = false;
+		_state.filters.filterCols = {};
+		priv.initEvents();
+		priv.initialRender();// set up _uiNode$...
+		priv.options.types.string = ((priv.options.types || {}).string || {});
+		priv.options.types.number = ((priv.options.types || {}).number || {});
+		priv.options.types.bool = ((priv.options.types || {}).bool || {});
+		priv.options.types.date = ((priv.options.types || {}).date || {});
+		if (priv.options.mapEditorContainer != 'dock'
+				&& priv.options.mapEditorContainer != 'window') {
+			priv.options.mapEditorContainer = 'window';
+		}
+
+		if (priv.options.filterInit) {
+			priv.setStateFilters(priv.options.filterInit);
+		}
+
+		if (priv.options.exportFields) {
+			$.each(priv.options.exportFields, function(ind, col) {
+				_exportFieldsSelect[col] = true;
+			});
+		}
+
+		if (location.hash) {
+			priv.options.router()
+		} else {
+			priv.update(priv.options.router)
+		}
+
+		if (typeof priv.options.router == "function") {
+			$(window).bind('hashchange', function() {
+				priv.options.router.apply(this);
+			});
+		}
+	};
+
+	priv.initEvents = function() {
+		jQuery(_uiNodeCont).on('TableAjax:render', priv.onRender);
+		jQuery(window).on('resize', priv.onWindowResize)
+	};
+
+	priv.onRender = function(e) {
+		if (priv.options.debug) console.log('onRender.arguments:', arguments.length, arguments, 'e:', e);
+		// console.log("priv.onRender arguments:", arguments);
+		// console.trace();
+		if (arguments.length > 1) {
+			for (var i=1; i<arguments.length; i++) {
+				switch (arguments[i]) {
+					case 'head': _head = undefined; break;
+					case 'body': _body = undefined; break;
+					case 'foot': _foot = undefined; break;
+					case 'foot_pagination': {
+							priv.renderFooterInfo();
+							priv.renderFooterPagination();
+							priv.renderFooterPageSizes();
+						}
+						break;
+					case 'footer__toolbar__info': priv.renderFooterInfo(); break;
+					case 'footer__toolbar__pagination': priv.renderFooterPagination(); break;
+					case 'footer__toolbar__pagesizes': priv.renderFooterPageSizes(); break;
+					case 'foot__columnPicker': priv.renderFooterColumnPicker(); break;
+					case 'head__specialFilters': priv.renderHeadSpecialFilters(); break;
+				}
+			}
+			priv.renderTable();
+			priv.onWindowResize();
+
+			{// activate saveBtn if some columns hidden
+				var selectedFilter = priv.modelColFilter_getSelected();
+				if (null === selectedFilter) {
+					priv.modelColFilter_getSaveBtn().prop("disabled", false)
+				} else {
+					priv.modelColFilter_getSaveBtn().prop("disabled", true)
+				}
+			}
+		}
+	};
+
+	priv.onWindowResize = function() {
+		var stickyCol1Width = (12 + 3) * 4 + 2 * 5 + 1;
+		var stickyCol2Width = 50 + 2 * 5 + 1;
+		var contW = jQuery(_uiNodeCont).parent().width();
+		var colsW = stickyCol1Width + stickyCol2Width;
+		jQuery(_uiNodeCont).css({width:'' + (contW - colsW) + 'px'})//, marginLeft:'' + colsW + 'px', overflowX:'scroll', overflowY:'visible', paddingBottom:'1px'});
+
+		var wh = jQuery(window).height();   // returns height of browser viewport
+		var dh = jQuery(document).height(); // returns height of HTML document (same as pageHeight in screenshot)
+		if (dh > wh) {
+			jQuery(_uiNodeCont).doubleScroll()
+			jQuery(_uiNodeCont).prev('.doubleScroll-scroll-wrapper').css({float: 'right', display:'block'})
+		} else {
+			jQuery(_uiNodeCont).prev('.doubleScroll-scroll-wrapper').css({display:'none'})
+		}
+	}
+
+	priv.initialRender = function() {
+		_uiNode$Table = $('<table class="AjaxTable table table-striped table-hover table-bordered table-condensed"></table>').appendTo(_uiNodeCont);
+			_head = $('<thead></thead>').prependTo(_uiNode$Table);
+				_headSort = $('<tr class="sort tblAjax__head__sort"></tr>').prependTo(_head);
+				_headFilter = $('<tr class="filter tblAjax__head__filter"></tr>').appendTo(_head);
+			_bodyNode = $('<tbody></tbody>').insertAfter(_head);
+		$('<tfoot></tfoot>').insertAfter(_bodyNode);
+		_foot = $('<div class="foot tblAjax__footer"></div>').insertAfter(_uiNodeCont);
+			var footToolbar = $('<div class="btn-toolbar tblAjax__footer__toolbar"></div>').appendTo(_foot);
+				$('<span class="tblAjax__footer__toolbar__info"></span>').appendTo(footToolbar);
+				$('<span class="tblAjax__footer__toolbar__pagination"></span>').appendTo(footToolbar);
+				$('<span class="tblAjax__footer__toolbar__pagesizes"></span>').appendTo(footToolbar);
+				$('<span class="tblAjax__footer__toolbar__columnPicker"></span>').appendTo(footToolbar);
+				$('<span class="tblAjax__footer__toolbar__functions"></span>').appendTo(footToolbar);
+				$('<span class="tblAjax__footer__toolbar__export"></span>').appendTo(footToolbar);
+		_mapEditorWrap = $('<div class="mapEditor" style="display:none"></div>').insertAfter(_foot);
+			_mapEditor = $('<div class="mapEditor-map"></div>').appendTo(_mapEditorWrap);
+		_popoverCell = $('<div class="popoverCell" style="display:none"></div>').insertAfter(_foot);
+		$('<div class="tblAjax__inlineEditBox"></div>').insertAfter(_foot);
+		$('<div class="btn-toolbar tblAjax__head__specialFilter"></div>').insertBefore(_uiNodeCont);
+
+		priv.renderInlineEditBox();// .tblAjax__inlineEditBox
+
+		_foot = _head = _body = _headSort = _headFilter = undefined;// TODO: refactor
+	};
+
+	priv.renderTable = function() {
+		if (!_state.colsSorted) return;
+		if (!_head) {
+			_headSort = _headFilter = _headSpecialFilter = undefined;
+			_head = _uiNode$Table.find('thead');
+		}
+		if (!_headSort) priv.renderTableTheadSort();
+		if (!_headFilter && priv.options.filter) {
+			priv.renderTableTheadFilter();
+			_headFilter = true;
+		}
+		if (!_headSpecialFilter && priv.options.specialFilterFunctions) {
+			priv.renderHeadSpecialFilters();
+			_headSpecialFilter = true;
+		}
+		if (!_body) {
+			_uiNode$Table.find('tbody').remove();
+			_bodyNode = $('<tbody></tbody>').insertAfter(_head);
+			//_bodyNode.on('change', '.unique', priv.rowChecked);
+
+			// find out what rows to show next...
+			var rowsAdded = 0;
+
+			// slice out the chunk of data we need and create rows
+			$.each(_data.rows, function(index, props) {
+				var rowNode = priv.renderRow(props);
+				if (rowNode) rowNode.appendTo(_bodyNode);
+				rowsAdded++;
+				if (rowsAdded >= priv.options.pageSize) { // enough rows created
+					return false;
+				}
+			});
+
+			if (_state.page == 1) {
+				_totalPages = Math.ceil(_data.total / priv.options.pageSize);
+			}
+
+			if (_state.page == _totalPages) { // pad with empty rows if we're at last page.
+				while (rowsAdded < priv.options.pageSize) {
+					var rowNode = priv.renderRowEmptyNode();
+					if (rowNode) rowNode.appendTo(_bodyNode);
+					rowsAdded++;
+				}
+			}
+		}
+
+		if (!_foot) {
+			priv.renderTableTfoot();
+			priv.renderFooter();
+		}
+		_foot = $(_uiNodeCont).next('.foot');
+
+		var stickyCol1Width = (12 + 3) * 4 + 2 * 5 + 1;
+		var stickyCol2Width = 50 + 2 * 5 + 1;
+		if (undefined === _tableWidth) {
+			_tableWidth = true;
+			var contW = jQuery(_uiNodeCont).width();
+			var th1 = _uiNode$Table.find('.stickyCol1:first');
+			var th2 = th1.next();
+			var th1W = th1.innerWidth();
+			var th2W = 50 + 2 * 5;//th2.innerWidth();
+			var colsW = stickyCol1Width + stickyCol2Width;
+			jQuery(_uiNodeCont).css({width:'' + (contW - colsW) + 'px', marginLeft:'' + colsW + 'px', overflowX:'scroll', overflowY:'visible', paddingBottom:'1px'});
+		}
+		_uiNode$Table.find('.stickyCol1').css({
+			position: 'absolute',
+			left: '0',
+			top: 'auto',
+			width: '' + stickyCol1Width + 'px',
+		});
+		//_uiNode$Table.find('.filter').find('.stickyCol1').css({height:'34px'});
+		var sortStickyColHeight = _uiNode$Table.find('.sort').find('th:last').outerHeight();
+		_uiNode$Table.find('.sort').find('.stickyCol1').css({height: sortStickyColHeight + 'px'});
+		_uiNode$Table.find('.sort').find('.stickyCol2').css({height: sortStickyColHeight + 'px'});
+		_uiNode$Table.find('.stickyCol2').css({
+			position: 'absolute',
+			left: '' + (stickyCol1Width) + 'px',
+			top: 'auto',
+			width: '' + stickyCol2Width + 'px',
+			borderRight: '1px solid silver',
+			overflow: 'hidden'
+		});
+
+		if (_data.total == 0 && priv.options.hidePagerOnEmpty) {
+			$('.btn-toolbar', _foot).remove();
+		}
+		if (priv.options.debug) console.log('table created');
+		if (typeof priv.options.tableCreated == 'function') {
+			priv.options.tableCreated.call(_uiNode$Table.get(0), {table: _uiNode$Table.get(0)});
+		}
+	};
+
+	// TODO: priv.renderFieldWidget_InlineEdit = function(fldName, value) {}
+	// TODO: priv.renderFieldWidget_FormField = function(fldName, value) {}
+
+	// @usage: priv.renderFieldWidget_TableCell(fldName, val, rowPK, row)
+	priv.renderFieldWidget_TableCell = function(fldName, value, rowPK, row) {
+		var renderFieldAsTableCell = priv.getFieldWidget_TableCell(fldName);
+		if (!renderFieldAsTableCell) return null;
+		return renderFieldAsTableCell(value, rowPK, row);
+	};
+
+	priv.registerFieldWidget = function(type, fldName, fieldProps) {
+		var fieldWidget = null;
+		switch (type) {
+			case "date":// @return Element - TODO: not used yet
+				fieldWidget = (function(fldName, fieldProps) {
+					var _fieldName = fldName,
+							_fieldProps = fieldProps;
+					// console.log('FieldWidget: generate function to render field('+_fieldName+') ', fieldProps);
+					return function(val, fieldPK, row) {
+						// console.log('FieldWidget: pk('+fieldPK+') run function to render field('+_fieldName+') with value('+val+') ', fieldProps);
+						return val;
+					}
+				}(fldName, fieldProps));
+				break;
+			case "bool":// @return Element - TODO: not used yet
+				fieldWidget = (function(fldName, fieldProps) {
+					var _fieldName = fldName,
+							_fieldProps = fieldProps;
+					// console.log('FieldWidget: generate function to render field('+_fieldName+') ', fieldProps);
+					return function(val, fieldPK, row) {
+						// console.log('FieldWidget: pk('+fieldPK+') run function to render field('+_fieldName+') with value('+val+') ', fieldProps);
+						fldNode = document.createElement('input');
+						fldNode.setAttribute('type', 'checkbox');
+						if (val) fldNode.setAttribute('checked', 'checked');
+						fldNode.setAttribute('disabled', 'disabled');
+						fldNode.appendChild(document.createTextNode(val));
+						return fldNode;
+					}
+				}(fldName, fieldProps));
+				break;
+			case "number":// @return String - TODO: not used yet
+				fieldWidget = (function(fldName, fieldProps) {
+					var _fieldName = fldName,
+							_fieldProps = fieldProps,
+							_fractionDigits = _.get(_fieldProps, 'fractionDigits', 2);
+					// console.log('FieldWidget: generate function to render field('+_fieldName+') _fractionDigits('+_fractionDigits+')', fieldProps);
+					return function(val, fieldPK, row) {
+						// console.log('FieldWidget: pk('+fieldPK+') run function to render field('+_fieldName+') with value('+val+') ', fieldProps);
+						if ('' === val) return null; // is nullable
+						if ('*****' === val) return val
+						// enumeration
+						// fractionDigits
+						// maxExclusive
+						// maxInclusive
+						// minExclusive
+						// minInclusive
+						// pattern
+						// totalDigits
+						// whiteSpace
+						val = Number(val);
+						return ((val || 0) % 1 === 0)? val : val.toFixed(_fractionDigits);
+					}
+				}(fldName, fieldProps));
+				break;
+			case "string":// @return String
+				fieldWidget = (function(fldName, fieldProps) {
+					var _fieldName = fldName,
+							_fieldProps = fieldProps,
+							_format = _.get(_fieldProps, 'format', '{0}');
+					// console.log('FieldWidget: generate function to render field('+_fieldName+') ', fieldProps);
+					return function(val, fieldPK, row) {
+						// console.log('FieldWidget: pk('+fieldPK+') run function to render field('+_fieldName+') with value('+val+') ', fieldProps);
+						return p5Utils__format(_format, [val]);
+					}
+				}(fldName, fieldProps));
+				break;
+			case "p5:price":// @return String
+				fieldWidget = (function(fldName, fieldProps) {
+					var _fieldName = fldName,
+							_fieldProps = fieldProps,
+							_format = _.get(_fieldProps, 'format', '<span style="text-align:right">{0}</span>');
+					// console.log('FieldWidget: generate function to render field('+_fieldName+') ', fieldProps);
+					return function(val, fieldPK, row) {
+						// console.log('FieldWidget: pk('+fieldPK+') run function to render field('+_fieldName+') with value('+val+') ', fieldProps);
+						return p5Utils__format(_format, [val]);
+					}
+				}(fldName, fieldProps));
+				break;
+			case "p5:string":// @return String
+				fieldWidget = (function(fldName, fieldProps) {
+					var _fieldName = fldName;
+					var _fieldProps = fieldProps;
+					var _formatByValue = _.get(_fieldProps, 'formatByValue', null);
+					var _aliasMap = _.get(_fieldProps, 'aliasMap', null);
+					return function(val, fieldPK, row) {
+						if (_formatByValue && val in _formatByValue) {
+							val = p5Utils__format(_formatByValue[val], [val])
+							// console.log('p5:string value', val, '_aliasMap', _aliasMap)
+							if (_aliasMap) {
+								Object.keys(_aliasMap).map(function (mapKey) {
+									var mapField = _aliasMap[mapKey]
+									if (undefined !== row[mapField]) {
+										val = val.replace(new RegExp('\{' + mapKey + '\}', 'g'), row[mapField]);
+									}
+								})
+							}
+						}
+						return val;
+					}
+				}(fldName, fieldProps));
+				break;
+			case "p5:www_link":// @return Element
+				fieldWidget = (function(fldName, fieldProps) {
+					var _fieldName = fldName,
+							_fieldProps = fieldProps;
+					//  console.log('FieldWidget: generate function to render field('+_fieldName+') ', fieldProps);
+					return function(val, fieldPK, row) {
+						//  console.log('FieldWidget: pk('+fieldPK+') run function to render field('+_fieldName+') with value('+val+') ', fieldProps);
+						var link = '',
+								a = null,
+								dbg = false;
+						if (!val.length) return '';
+						if ('index.php' == val.substr(0, 9) || 'procesy5.php' == val.substr(0, 12)) {
+							link = window.location.protocol + '//' + window.location.hostname
+							link += window.location.pathname.split('/').slice(0, -1).join('/')
+							link += '/' + val
+						} else {
+							if ('http' == val.substr(0, 4)) link = val
+							//else if ('ftp' == val.substr(0, 3))
+							else link = 'http://' + val
+						}
+
+						var linkErrors = validate({www_link: link}, {www_link: {url: true}});
+						if (linkErrors) {
+							// if(dbg)console.log('linkErrors for (' + link +')', linkErrors);
+							return val;
+						}
+						fldNode = document.createElement('a');
+						fldNode.setAttribute('href', link);
+						fldNode.setAttribute('target', '_blank');
+						fldNode.appendChild(document.createTextNode(val));
+						return fldNode;
+					}
+				}(fldName, fieldProps));
+				break;
+			case "special":
+				fieldWidget = (function(fldName, fieldProps) {
+					var _fieldName = fldName;
+					var _fieldProps = fieldProps;
+					var _format = _.get(_fieldProps, 'format', '{0}');
+					var _aliasMap = _.get(_fieldProps, 'aliasMap', null);
+					// console.log('FieldWidget: generate function to render field('+_fieldName+') ', fieldProps);
+					return function(val, fieldPK, row) {
+						// console.log('FieldWidget: pk('+fieldPK+') run function to render field('+_fieldName+') with value('+val+') ', fieldProps, 'row', row);
+						var format = _format;
+						if (_aliasMap) {
+							Object.keys(_aliasMap).map(function (mapKey) {
+								var mapField = _aliasMap[mapKey]
+								if (undefined !== row[mapField]) {
+									format = format.replace(new RegExp('\{' + mapKey + '\}', 'g'), row[mapField]);
+								}
+								// console.log('FieldWidget: pk('+fieldPK+') run function to render field('+_fieldName+') with value('+val+') _aliasMap:', _aliasMap, 'row['+mapField+']', row[mapField], 'converted val:', val);
+							})
+						}
+						return p5Utils__format(format, [val]);
+					}
+				}(fldName, fieldProps));
+				break;
+			case "simpleLink":
+				fieldWidget = (function(fldName, fieldProps) {
+					var _fieldName = fldName,
+							_fieldProps = fieldProps,
+							_format = _.get(_fieldProps, 'format', '{0}');
+					// console.log('FieldWidget: generate function to render field('+_fieldName+') ', fieldProps);
+					return function(val, fieldPK, row) {
+						// console.log('FieldWidget: pk('+fieldPK+') run function to render field('+_fieldName+') with value('+val+') ', fieldProps);
+						var valLink = String(val);
+						if (undefined !== _fieldProps._tsRetId
+								&& (0 === _fieldProps._tsRetId || '0' === _fieldProps._tsRetId || null === _fieldProps._tsRetId)
+								&& undefined !== _fieldProps._tsSimpleLink) {
+							valLink = _fieldProps._tsSimpleLink.format;
+							$.each(_fieldProps._tsSimpleLink.aliasMap, function(i, v) {
+								// console.log('simpleLink aliasMap fldName:', fldName, 'i:', i, 'v:', v, 'row['+v+']', row[v], 'val', val, 'typeof val', typeof val);
+								if (undefined !== row[v]) {
+									valLink = valLink.replace(new RegExp('\{' + i + '\}', 'g'), row[v]);
+								}
+							});
+						}
+						return p5Utils__format(_format, [valLink]);
+					}
+				}(fldName, fieldProps));
+				break;
+			case "gml:PolygonPropertyType":
+			case "gml:LineStringPropertyType":
+			case "gml:PointPropertyType":
+			case "geom":
+				fieldWidget = (function(fldName, fieldProps) {
+					var _fieldName = fldName,
+							_fieldProps = fieldProps,
+							_format = _.get(_fieldProps, 'format', '{0}');
+					// console.log('FieldWidget: generate function to render field('+_fieldName+') ', fieldProps);
+					return function(val, fieldPK, row) {
+						// console.log('FieldWidget: run function to render field('+_fieldName+') with value('+val+') ', fieldProps);
+						// console.log('FieldWidget: test priv', priv);
+						var node = jQuery('<span></span>');
+						node.TableAjaxGeomField({
+							recordID: fieldPK,
+							fieldValue: val,
+							hasValueClassName: 'cell-mapfld-hasValue',
+							linkClassNamePrefix: 'cell-mapfld',
+							linkSelectClassName: 'select',
+							linkSelectAddClassNames: 'glyphicon glyphicon-map-marker',
+							linkRemoveClassName: 'remove',
+							linkRemoveAddClassNames: 'glyphicon glyphicon-remove',
+							onSelect: function(recordID, geomShape) {
+								if (priv.options.debug) console.log('recordID:', recordID, 'geomShape:', geomShape);
+								priv.mapEditorShow();
+								_mapEditor.TableAjaxMapSelectRecord(recordID, geomShape);
+							},
+							onRemove: function(geomField, recordID, geomShape) {
+								if (confirm('Czy usunąć obiekt z mapy dla rekordu ' + recordID + '?')) {
+									if (!recordID) return;
+
+									var selectedRecordId = recordID;
+									superagent
+										.post(SYNC_URL + '&_hash=' + HTML_ID + '&_task=removeTheGeomAjax')
+										.type('json') // header ĺapplication/x-www-form-urlencoded' requires type('form');
+										.send({
+											ID: recordID,
+											namespace: NAMESPACE
+										})
+										.set('Accept', 'application/json')
+										.end(function(err, res) {
+											if(priv.options.debug)console.log('DBG: res:', res, 'res.body:', res.body);
+											var payload;
+											if (err || !res.ok || 'application/json' !== res.type) {
+												payload = {type: 'warning', msg: res.body.msg || 'Wystąpiły błędy', body: res.body};
+											} else {
+												payload = {type: 'success', msg: res.body.msg || '', body: res.body};
+											}
+											p5UI__notifyAjaxCallback(payload);
+											var data = res.body;
+											if (data && data.record && data.record.the_geom) {
+												if (priv.options.debug) console.log('data.record.the_geom:2:', data.record.the_geom);
+												geomField.setValue(data.record.the_geom);
+											}
+											else if (data && data.record) {
+												if (priv.options.debug) console.log('data.record.the_geom:2:', data.record.the_geom);
+												geomField.setValue(data.record.the_geom);
+											}
+											_mapEditor.TableAjaxMapRefresh();
+										});
+								}
+							}
+						});
+						return node;
+					}
+				}(fldName, fieldProps));
+				break;
+			case "ref":
+				fieldWidget = (function(fldName, fieldProps) {
+					var _fieldName = fldName,
+							_fieldProps = fieldProps;
+					// console.log('FieldWidget: generate function to render field('+_fieldName+') fieldProps: ', fieldProps);
+					return function(val, fieldPK, row) {
+						// console.log('FieldWidget: pk('+fieldPK+') run function to render field('+_fieldName+') with value: ', val, ', fieldProps: ', fieldProps);
+						// if (val.length > 5) return '('+val.length+')' + printFirst5 + '...' => open more info
+						return _.map(val, function(v) {
+							if (!v || !v.xlink) return '';
+							var idRemote = v.xlink.split('.').pop()
+							var nsRemote = v.xlink.split(':').pop().split('.').shift()
+							var seLink = URI_BASE + 'index.php?_route=ViewTableAjax&namespace=' + fieldProps.xsdRefNsPrefix.replace(/__x3A__/g, '/') + '/' + nsRemote + '#EDIT/' + idRemote;
+							var wfsLink = fieldProps.xsdRefUri + '#' + fieldProps.xsdRefType + '.' + idRemote;
+							return '<a class="btn btn-xs btn-default" href="' + seLink + '" title="' + wfsLink + '">' + idRemote + '</a>';
+						}).join(' ');
+					}
+				}(fldName, fieldProps));
+				break;
+		}
+		_fieldWidgets[fldName] = fieldWidget;
+	};
+
+	priv.getFieldWidget_TableCell = function(fieldName) {
+		var props = _data.cols[fieldName],
+				type = null,
+				renderFieldAsTableCell = null;
+		if (!props) return null;
+		type = _.get(props, 'type');
+		if (!type) type = 'string';// TODO: throw Exception? / default string
+		renderFieldAsTableCell = _.get(_fieldWidgets, fieldName);
+		if (renderFieldAsTableCell) return renderFieldAsTableCell;
+		priv.registerFieldWidget(type, fieldName, props);
+		renderFieldAsTableCell = _.get(_fieldWidgets, fieldName);
+		if (!renderFieldAsTableCell) throw "Field type '" + type + "' not implemented";
+		return renderFieldAsTableCell;
+	};
+
+	priv.renderRow = function(props) {
+		var rowNode = $('<tr></tr>'),
+				rowPK = (_state.primaryKey in props) ? props[_state.primaryKey] : null,
+				cellNode,
+				columnName, columnProps, val, cellCnt, format, showTooltip, fldWidgetNode,
+				dbg = priv.options.debug
+		;
+		//  ondrop="drop_handler(event);" ondragover="dragover_handler(event);" ondragend="dragend_handler(event);"
+		rowNode.attr('ondrop', "p5TA_onDrop(event, '"+rowPK+"', '"+priv.options.namespace+"')")
+		rowNode.attr('ondragover', "p5TA_onDragOver(event, this, '"+rowPK+"', '"+priv.options.namespace+"')")
+		rowNode.attr('ondragend', "p5TA_onDragEnd(event, this, '"+rowPK+"', '"+priv.options.namespace+"')")
+
+		if (priv.options.rowFunctions || priv.options.filtersClean) {
+			cellNode = priv.renderCellRowFunctions(priv.options.rowFunctions, rowPK, props);
+			cellNode.addClass('text-right stickyCol1');
+			cellNode.appendTo(rowNode);
+		}
+
+		//create checkbox
+		if (_state.primaryKey && priv.options.checkboxes) {
+			var check = _uniqueCols[props[_state.primaryKey]] != undefined ? 'checked' : '';
+			var checkable = props['checkable'] === false ? 'disabled' : '';
+			cellNode = $('<td></td>').appendTo(rowNode);
+			$(p5Utils__format('<input class="unique" {0} {1} type="checkbox" />', [check, checkable])).appendTo(cellNode);
+		}
+
+		if (_state.primaryKey) rowNode.data('unique', rowPK);
+
+		//create cells
+		for (var i = 0; i < _state.colsSorted.length; i++) {
+			columnName = _state.colsSorted[i];
+			columnProps = _data.cols[columnName];
+			val = '';
+			if (!columnProps) continue;
+			if (columnProps.hidden) continue;// TODO: "unique" is hidden
+			showTooltip = true;
+
+			val = props[columnName];
+			cellNode = $('<td></td>').appendTo(rowNode);
+			if (i === 1) cellNode.addClass('stickyCol2');// primaryKey(ID)
+			if (i !== 1) cellNode.on('dblclick', {id:rowPK, col:columnName}, priv.rowDblClicked);
+			cellCnt = (i === 1)? '<div></div>' : '<span></span>';
+			cellCnt = $(cellCnt).appendTo(cellNode);// fix dblclick for copy
+
+			cellNode.data('column', columnName);
+			if (val === undefined) continue;
+
+			format = _.get(columnProps, 'format', '{0}');
+
+			// test use field widgets
+			fldWidgetNode = priv.renderFieldWidget_TableCell(columnName, val, rowPK, props);
+			if (fldWidgetNode || fldWidgetNode === 0) {
+				cellCnt.empty();
+				cellCnt.append(fldWidgetNode);
+			} else if (fldWidgetNode === '') {
+				cellCnt.empty();
+			} else {
+				console.log('TODO: !FieldWidget for row.pk('+rowPK+') col('+columnName+') typeof fldWidgetNode(' + (typeof fldWidgetNode) + ') columnProps', columnProps, 'fldWidgetNode', fldWidgetNode, 'value', props[columnName]);
+			}// test fld widgets
+
+			if (columnProps._tsRetId) {
+				showTooltip = false;
+				if (columnProps._tsRetId > 0) {
+					cellCnt.on('click', {id:rowPK, col:columnName, friendly:columnProps.friendly, value:p5Utils__format(format, [val])}, priv.popoverCellTypeSpecial);
+				}
+			}
+			if ('ref' === columnProps.type) showTooltip = false;
+			if ('ref' === columnProps.type) cellNode.css('padding', "3px")
+			if ('ref' === columnProps.type && priv.options.debug) console.log('renderRow rowPK('+rowPK+') columnProps', columnProps)
+			// if ('ref' === columnProps.type) cellNode.html('<a href="index.php?_route=ViewTableAjax' +
+			// 	'&namespace=' + columnProps.xsdRefNsPrefix.replace('__x3A__', '/') + '/' + columnProps.xsdRefType +
+			// 	'&backRefNS=' + priv.options.namespace +
+			// 	'&backRefPK=' + rowPK +
+			// 	'&backRefField=' + columnProps.column +
+			// 	'">przeglądaj</a>')
+			if ('ref' === columnProps.type) {
+				cellNode.html('<a onClick="return p5UI__tableAjaxOpenRefCell(' +
+					'\'' + columnProps.xsdRefNsPrefix.replace('__x3A__', '/') + '/' + columnProps.xsdRefType + '\', ' + // namespace
+					'\'' + priv.options.namespace + '\', ' + // backRefNS
+					'\'' + rowPK + '\', ' + // backRefPK
+					'\'' + columnProps.column + '\')' + // backRefField
+					'" ' +
+					'href="index.php?_route=ViewTableAjax' +
+					'&namespace=' + columnProps.xsdRefNsPrefix.replace('__x3A__', '/') + '/' + columnProps.xsdRefType +
+					'&backRefNS=' + priv.options.namespace +
+					'&backRefPK=' + rowPK +
+					'&backRefField=' + columnProps.column +
+					'">przeglądaj</a>')
+			}
+
+			if (columnProps.xsdType && 'xsd:base64Binary' === columnProps.xsdType) {
+				cellCnt.empty();
+				if (val) {
+					cellCnt.append('<a target="_blank" href="wfs-data.php?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetBlob&namespace='+priv.options.namespace+'&primaryKey='+rowPK+'&fieldName='+columnName+'">'+"otwórz"+'</a>');
+				} else {
+					cellCnt.append('<span style="color:silver" title="Brak danych">N/S;</span>');
+				}
+			}
+
+			if (i > 1 && priv.options.longDesc) {// TODO: use better check for columns: functions and pk
+				cellNode.addClass('tbl-short-txt');
+				if (showTooltip) {
+					cellCnt.tooltip({title: cellCnt.text(), placement: 'left'});
+				}
+			}
+		}
+		return rowNode;
+	};
+
+	priv.renderCellRowFunctions = function(rowFunctions, rowPK, rowProps) {
+		var cellNode = $('<td></td>'),
+				keys = Object.keys(rowFunctions),
+				total = keys.length,
+				moreFuncBtnNode,
+				moreFunctions = [],
+				idx
+		;
+		if (priv.options.debug) console.log('TableAjax::renderCellRowFunctions: rowFunctions', rowFunctions);
+		idx = keys.indexOf('hist');
+		if (idx !== -1) {
+			histFunc = keys.splice(idx, 1);
+			keys.push('hist');
+		}
+		moreFunctions = keys.splice(3);
+		keys.forEach(function(key) {
+			var funObj = rowFunctions[key],
+					funcNode = p5UI_TableAjax_generateFunctionNode(funObj, rowPK, {ico: true, label: false})
+			;
+			funcNode.appendTo(cellNode);
+		});
+		moreFuncBtnNode = $('<a href="#" style="margin:0 2px;text-decoration:none" title="Więcej funkcji dla rekordu nr '+rowPK+'" class="glyphicon glyphicon-menu-hamburger"> </a>');
+		moreFuncBtnNode.on('click', {rowPK: rowPK, rowFunctions: rowFunctions, more: moreFunctions}, priv.popoverCellMoreFunctions);
+		moreFuncBtnNode.appendTo(cellNode);
+		return cellNode;
+	};
+
+	priv.popoverCellMoreFunctions = function(e) {
+		e.preventDefault();
+		e.stopPropagation();
+		if (priv.options.debug) console.log('TableAjax::popoverCellMoreFunctions: rowPK', e.data.rowPK, 'moreFunctions', e.data.more, 'rowFunctions', e.data.rowFunctions);
+		if ('rowPK' in e.data && e.data.rowPK > 0) {
+			var lastId = _popoverCell.data('rowid'),
+					lastCol = _popoverCell.data('col'),
+					rowPK = e.data.rowPK,
+					moreFunctions = e.data.more,
+					rowFunctions = e.data.rowFunctions,
+					funcListNode
+			;
+
+			if (lastId == e.data.rowPK && lastCol == 'moreFunctions') {
+				//_popoverCellCurrent.popover('toggle');
+			}
+			else {
+				if (_popoverCellCurrent) {
+					_popoverCellCurrent.popover('destroy');
+				}
+
+				_popoverCell.data('rowid', rowPK);
+				_popoverCell.data('col', 'rowFunctions');
+				_popoverCell.empty();
+				funcListNode = $('<ul class="list-unstyled popoverRowFunctions"></ul>').appendTo(_popoverCell);
+
+				moreFunctions.forEach(function(funcName) {
+					var funcItemNode = $('<li></li>').appendTo(funcListNode),
+							funObj = rowFunctions[funcName],
+							funcNode = p5UI_TableAjax_generateFunctionNode(funObj, rowPK, {ico: true, label: true})
+					;
+					funcNode.addClass('func_name-' + funcName);
+					funcItemNode.append(funcNode);
+				});
+				{ // TODO: id namesapce is AntAcl [ and has ref fields or back ref ]
+					var funcItemNode = $('<li></li>').appendTo(funcListNode)
+					var funObj = {
+						ico: 'glyphicon glyphicon-random',
+						label: 'Przeglądaj powiązania',
+						href: 'index.php?_route=RefGraph&namespace=' + priv.options.namespace + '&primaryKey=' + rowPK,
+						// onclick: function (e) { // TODO: open in
+						// 	console.log('TODO: przeglądaj powiązania')
+						// }
+					}
+					var funcNode = p5UI_TableAjax_generateFunctionNode(funObj, rowPK, { ico: true, label: true })
+					funcItemNode.append(funcNode);
+				}
+
+				_popoverCell.append('<div class="popoverCellContent" style="white-space:normal"></div>');
+
+				_popoverCellCurrent = jQuery(e.currentTarget);
+				// title : '<span class="text-info"><strong>title</strong></span> <button type="button" id="close" class="close">&times;</button>'
+				var opts = {
+					container: 'body',
+					placement: 'right',
+					trigger: 'click',
+					template: '',
+					html: true,
+					content: _popoverCell.html()
+				}
+				opts.template += '<div class="popover" role="tooltip" style="max-width:600px;width:440px;">';
+				{
+					opts.template += '<div class="arrow"></div>';
+					//opts.template += '<h3 class="popover-title"></h3>';
+					opts.template += '<div style="display:block;position:relative;">';
+					{
+						opts.template += '<div class="popover-title">';
+						opts.template += '</div>';
+						opts.template += '<button type="button" class="close" onclick="return hidePopover();" style="position:absolute;right:8px;top:6px;">&times;</button>';
+					}
+					opts.template += '</div>';
+					opts.template += '<div class="popover-content"></div>';
+				}
+				opts.template += '</div>';
+
+				_popoverCellCurrent.popover(opts);
+				_popoverCellCurrent.on('shown.bs.popover', function(e) {
+					if (!_popoverCellCurrent) return;
+					var popoverNodeId = _popoverCellCurrent.attr('aria-describedby');
+					if (!popoverNodeId) return;
+					var popover$Node = jQuery('#' + popoverNodeId);
+					if (!popover$Node.length) return;
+					var arrow$Node = popover$Node.children('.arrow');
+					if (arrow$Node.length) {
+						var posTop = parseFloat(arrow$Node.css('top'));
+						arrow$Node.css('top', '' + posTop + 'px')
+					}
+					popover$Node.find('.popoverCellContent').html('<p class="text-muted">Pobieranie funkcji...<p>');
+					priv.ajaxLoadMoreFunctionsCell(rowPK);
+				});
+				_popoverCellCurrent.popover('show');
+			}
+		} else {
+			if (priv.options.debug) console.log('NO data');
+			return false;
+		}
+		return;
+	};
+
+	priv.ajaxLoadMoreFunctionsCell = function(rowPK) {
+		var dbg = priv.options.debug;
+		if (_popoverCellAjaxXhr) {
+			_popoverCellAjaxXhr.abort();
+		}
+
+		_popoverCellAjaxXhr = $.ajax({
+			type: 'GET',
+			url: SYNC_URL + '&_hash=' + HTML_ID + '&_task=moreFunctionsCellAjax&ID=' + rowPK,
+			dataType: 'json',
+			contentType: "application/json; charset=utf-8",
+			data: ''
+		})
+		.done(function(data, textStatus, jqXHR){
+			if (data && 'success' === data.type) {
+				var popoverCellContent,
+						rowFunctions = [],
+						funcNodesToUpdate = []
+				;
+				if (data.rowFunctions && data.rowFunctions.length > 0) {
+					rowFunctions = data.rowFunctions;
+				}
+				if (rowFunctions.length > 0) {
+					var popoverCellContent = $('<ul class="list-unstyled"></ul>');
+					rowFunctions.forEach(function(funObj) {
+						var funcNode = p5UI_TableAjax_generateFunctionNode(funObj, rowPK, {ico: true, label: true});
+						if (funObj.id) {
+							funcNodesToUpdate.push({id: funObj.id, node: funcNode});
+						} else {
+							var funcItemNode = $('<li></li>').appendTo(popoverCellContent);
+							funcItemNode.append(funcNode);
+						}
+					});
+				} else {
+					popoverCellContent = '<p class="text-muted">Brak dodatkowych funkcji</p>';
+				}
+				//_popoverCell.append(popoverCellContent);// cache
+				if (_popoverCellCurrent) {
+					var popoverNodeId = _popoverCellCurrent.attr('aria-describedby');
+					if (popoverNodeId) {
+						var popoverNode = jQuery('#' + popoverNodeId),
+								popoverCntNode = popoverNode.find('.popoverCellContent'),
+								popoverRowFuncNode = popoverNode.find('.popoverRowFunctions');
+						;
+						{// fix generated row Functions by loaded from ajax call
+							if(dbg) console.log('popoverRowFunctions. popoverRowFuncNode', popoverRowFuncNode);
+							if(dbg) console.log('popoverRowFunctions. funcNodesToUpdate', funcNodesToUpdate);
+							funcNodesToUpdate.forEach(function(funcNodeInfo) {
+								var className = 'func_name-' + funcNodeInfo.id,
+										foundRowFuncNode = null
+								;
+								if(dbg) console.log('popoverRowFunctions. funcNodesToUpdate loop:', funcNodeInfo);
+								popoverRowFuncNode.find('li > a').each(function(ind, n) {
+									var n$ = $(n);
+									if(dbg) console.log('popoverRowFunctions loop(',ind,'):', n, 'data(func_name)', n$.data('func_name'), 'data', n$.data());
+									if (n$.hasClass(className)) {
+										foundRowFuncNode = n$;
+									}
+								});
+								if (foundRowFuncNode) {
+									foundRowFuncNode.replaceWith(funcNodeInfo.node);
+								} else {
+									var funcItemNode = $('<li></li>').appendTo(popoverCellContent);
+									funcItemNode.append(foundRowFuncNode);
+								}
+							});
+						}
+						popoverCntNode.empty().append(popoverCellContent);
+						if (rowFunctions.length <= 0) {
+							popoverCntNode.find('p.text-muted').delay(600).hide(300);
+						}
+					}
+				}
+			}
+		});
+
+	};
+
+	priv.renderRowEmptyNode = function() {
+		var rowNode = $('<tr></tr>');
+
+		if (priv.options.rowFunctions || priv.options.filtersClean) {
+			$('<td class="text-right stickyCol1">&nbsp;</td>').appendTo(rowNode);
+		}
+
+		if (_state.primaryKey && priv.options.checkboxes) {
+			$('<td><input disabled type="checkbox" /></td>').appendTo(rowNode);
+		}
+		for (var i = 0; i < _state.colsSorted.length; i++) {
+			var columnName = _state.colsSorted[i],
+					columnProps = _data.cols[columnName]
+			;
+			if (!columnProps) return;
+			if (columnProps.hidden) continue;// "unique" is hidden - TODO: rm "unique" from _state.colsSorted ?
+
+			if (i == 1) {// TODO: 1 is pk - use better check
+				$('<td class="stickyCol2">&nbsp;</td>').appendTo(rowNode);
+			} else {
+				$('<td>&nbsp;</td>').appendTo(rowNode);
+			}
+		}
+		return rowNode;
+	};
+
+	priv.renderTableTheadSort = function() {
+		var nodeClass = 'tblAjax__' + 'head__sort'
+		var currentNode = _uiNode$Table.find('thead').find('.' + nodeClass)
+		var node = $('<tr class="sort ' + nodeClass + '"></tr>')
+		if (priv.options.tblFunctions || priv.options.filtersClean) {
+			var headCell = $('<th class="text-right head-info stickyCol1"></th>').appendTo(node)
+			$.map(priv.options.tblFunctions, function (funObj, funName) {
+				var funHtml = $('<a></a>')
+				funHtml.attr('href', funObj.href || '#')
+				if (funObj.title) funHtml.attr('title', funObj.title)
+				if (funObj.icon) funHtml.html('<span class="glyphicon glyphicon-' + funObj.icon + '"></span> ')
+				if (funObj.method && priv[funObj.method] && typeof priv[funObj.method] == 'function') {
+					funHtml.on('click', priv[funObj.method])
+				}
+				funHtml.appendTo(headCell)
+			})
+		}
+
+		if (_state.primaryKey && priv.options.checkboxes) {
+			var checked = _checkToggleChecked ? 'checked' : ''
+			var headCell = $('<th></th>').appendTo(node)
+			var elem = $(p5Utils__format('<input {0} class="checkToggle" type="checkbox" />', [checked])).appendTo(headCell)
+			elem.on('change', priv.checkToggleChanged)
+		}
+
+		for (var i = 0; i < _state.colsSorted.length; i++) {
+			var column = _state.colsSorted[i]
+			var props = _data.cols[column]
+			if (props.hidden) continue
+
+			var headCell = $('<th class="ta-ordering"></th>').appendTo(node)
+			if (i == 1) headCell.addClass('stickyCol2')
+
+			if (props.type != 'special' && props.type != 'geom') { // TODO: props.isSortable
+				headCell.on('click', {column: column}, priv.columnClicked)
+			}
+
+			var nodeLabel = (props.friendly) ? props.friendly : column
+			var nodeTitle = column
+			if (props.description && props.description.length > 0) {
+				nodeTitle = p5Utils__format("{0} ({1})", [props.description, column])
+			}
+			else if (props._tsRetId > 0) {
+				nodeTitle = p5Utils__format("Kliknij na pole i przejdź do powiązanych rekordów ({0})", [nodeTitle])
+			}
+			if ('ref' === props.type && props.xsdRefType) {
+				nodeLabel = '<i class="glyphicon glyphicon-export"></i> ' + props.xsdRefType
+				if (props.description && props.description.length > 0 && props.description !== column) {
+					nodeTitle = p5Utils__format("{0} (ref {1})", [props.description, column])
+				} else {
+					nodeTitle = p5Utils__format("(ref {0})", [column])
+				}
+			}
+			var headCnt = $(p5Utils__format('<span class="pull-left" title="{1}">{0}</span>', [nodeLabel, nodeTitle]))
+			headCnt.appendTo(headCell)
+
+			if (column == _state.filters.currSortCol) {
+				headCell.addClass((_state.filters.currSortFlip) ? 'ta-ordering-down' : 'ta-ordering-up')
+			}
+
+			if (column !== _state.primaryKey) {
+				var hideColBtn = $('<i class="glyphicon glyphicon-remove remove-cell"></i>')
+				hideColBtn.on('click', {column: column},  priv.columnHideClicked)
+				hideColBtn.appendTo(headCell)
+			}
+		}
+
+		currentNode.replaceWith(node)
+	};
+
+	priv.renderHeadSpecialFilters = function() {
+		// console.log("priv.renderHeadSpecialFilters state:", { 'priv.options.specialFilterFunctions': priv.options.specialFilterFunctions, '_state.specialFilters': _state.specialFilters });
+		var nodeClass = 'tblAjax__' + 'head__specialFilter',
+				currentNode = $(_uiNodeCont).prevAll('.' + nodeClass),
+				node;
+		if (!priv.options.specialFilterFunctions) {
+			return;
+		}
+		var node = $('<div class="btn-toolbar tblAjax__head__specialFilter"></div>');
+		$.map(priv.options.specialFilterFunctions, function(groupObj, groupName) {
+			var btnHtml,
+					btnSelected,
+					groupLabel = groupObj.label || groupName,
+					groupHtml = $('<div class="btn-group"></div>')
+			;
+			// _state.specialFilters[e.data.group] = e.data.value;
+			if (groupName in _state.specialFilters) {
+				btnSelected = _state.specialFilters[groupName];
+			}
+			// console.log("priv.renderHeadSpecialFilters loop:", {groupName: groupName, btnSelected: btnSelected});
+			if (groupObj.icon) {
+				$('<button class="btn btn-xs btn-default" title="' + groupLabel + '"><i class="' + groupObj.icon + '"></i></button>').appendTo(groupHtml);
+			}
+			$.map(groupObj.btns, function(btnObj, btnName) {
+				btnHtml = $('<button class="btn btn-xs btn-default' + ((btnSelected && btnSelected == btnObj.value)? ' active' : '') + '">' + btnName + '</button>').appendTo(groupHtml);
+				btnHtml.on('click', {group: groupName, value: btnObj.value}, priv.specialFilterChanged);
+			});
+			btnHtml = $('<button class="btn btn-xs btn-default' + ((!btnSelected)? ' disabled' : '') + '" title="Kasuj filtr"><i class="glyphicon glyphicon-remove"></i></button>').appendTo(groupHtml);
+			btnHtml.on('click', {group: groupName, value: ''}, priv.specialFilterChanged);
+			groupHtml.appendTo(node);
+		});
+
+		// console.log("priv.renderHeadSpecialFilters replace:", { 'priv.options.specialFilterFunctions': priv.options.specialFilterFunctions, '_state.specialFilters': _state.specialFilters });
+		// console.log("priv.renderHeadSpecialFilters replace node:", { 'currentNode': currentNode, 'node': node, '_uiNodeCont': _uiNodeCont });
+		currentNode.replaceWith(node);
+	};
+
+	priv.renderTableTheadFilter = function() {
+		var nodeClass = 'tblAjax__' + 'head__filter',
+				currentNode = _uiNode$Table.find('thead').find('.' + nodeClass),
+				node;
+		// currentNode.find('i').tooltip('hide');
+		node = $('<tr class="filter ' + nodeClass + '"></tr>');
+		//_head.find(".filter").remove();
+		//_headFilter = $('<tr class="filter"></tr>').appendTo(_head);
+		var headCell;
+		var elem;
+		var placeHolder = '';
+		var tooltip = '';
+
+		//create the functions column
+		if (priv.options.rowFunctions || priv.options.filtersClean) {
+			var headCell = $('<th class="text-right stickyCol1"></th>').appendTo(node);
+			headCell.css({padding: '0'});
+			if (priv.options.filtersClean) {
+				var elem = $('<button title="Kasuj filtry" class="btn btn-xs btn-link glyphicon glyphicon-remove"></button>').appendTo(headCell);
+				elem.on('click', priv.filtersClean);
+			}
+		}
+
+		//create the filter checkbox
+		if (_state.primaryKey && priv.options.checkboxes) {
+			tooltip = priv.options.types.bool.filterTooltip || 'Toggle between:<br/>indeterminate,<br/>checked,<br/>unchecked';
+			headCell = $('<th></th>').appendTo(node);
+			elem = $('<input class="filter indeterminate" checked type="checkbox" />').appendTo(headCell);
+			$.map(_state.filters.filterCols, function(colProps, col) {
+				if (col == "unique") {
+					if (colProps.filter) elem.prop('checked', true).removeClass('indeterminate');
+					else if (!colProps.filter) elem.prop('checked', false).removeClass('indeterminate');
+					else if (colProps.filter == '') elem.addClass('indeterminate');
+				}
+			});
+
+			if (tooltip) {
+				elem.tooltip({
+					title: tooltip.trim(),
+					html: true,
+					container: 'body',
+					trigger: 'hover',
+					placement: 'top',
+					delay: {
+						show: 500,
+						hide: 100
+					}
+				});
+			}
+			elem.on('click', {column: "unique"}, priv.filterChanged);
+		}
+
+		//create the column filters
+		for (var i = 0; i < _state.colsSorted.length; i++) {
+			var column = _state.colsSorted[i];
+			var props = _data.cols[column];
+			tooltip = props.filterTooltip === true ? undefined : props.filterTooltip === false ? '' : props.filterTooltip;
+			placeHolder = props.placeHolder === true ? undefined : props.placeHolder === false ? '' : props.placeHolder;
+			if (props.hidden) continue;
+
+			headCell = $('<th></th>').appendTo(node);
+			headCell.css({padding: '0'});
+			if (i == 1) headCell.addClass('stickyCol2');
+
+			// @mark MarkTableAjaxFilterColType
+			switch (props.type || 'string') {
+					case "number":
+							if (placeHolder == undefined) placeHolder = priv.options.types.number.placeHolder;
+							placeHolder = (placeHolder === true || placeHolder == undefined) ? '' : placeHolder === false ? '' : placeHolder;
+							if (tooltip == undefined) tooltip = priv.options.types.number.filterTooltip;
+							tooltip = (tooltip === true || tooltip == undefined) ? '' : tooltip === false ? '' : tooltip;
+							elem = $(p5Utils__format('<input placeholder="{0}" class="filter" type="text" />', [placeHolder]));
+							elem.on('keyup', {column: column}, priv.filterChanged);
+							break;
+					case "date":
+							if (placeHolder == undefined) placeHolder = priv.options.types.date.placeHolder;
+							placeHolder = (placeHolder === true || placeHolder == undefined) ? '' : placeHolder === false ? '' : placeHolder;
+							if (tooltip == undefined) tooltip = priv.options.types.date.filterTooltip;
+							tooltip = (tooltip === true || tooltip == undefined) ? '' : tooltip === false ? '' : tooltip;
+							elem = $(p5Utils__format('<div><input placeholder="{0}" class="filter" type="text" /></div>', [placeHolder]));
+
+							if (priv.options.types.date.datePicker === true || priv.options.types.date.datePicker == undefined)
+							{
+									if ($().datepicker)
+									{
+											elem.addClass('date-wrap');
+											var today = 0;//new priv.ext.XDate(false).setHours(0, 0, 0, 0).toString('yyyy-MM-dd');
+											var dp = $(p5Utils__format('<div style="float:right" class="date" data-date="{0}" data-date-format="{1}" />', [today, 'yyyy-mm-dd'])).appendTo(elem);
+											$('<input style="display:none" type="text"  />').appendTo(dp);
+											$('<span class="add-on"><i class="glyphicon glyphicon-chevron-right"></i></span>').on('click', {op: "l"}, priv.dpOpChanged).appendTo(dp);
+											$('<span class="add-on"><i class="glyphicon glyphicon-chevron-left"></i></span>').on('click', {op: "r"}, priv.dpOpChanged).appendTo(dp);
+											dp.datepicker({weekStart:1});
+											dp.on('changeDate', {column: column, input: $('input.filter', elem)}, priv.dpClicked);
+									}
+									else
+									if (priv.options.debug) console.log('datepicker plugin not found');
+							}
+							elem.on('keyup', 'input.filter', {column: column}, priv.filterChanged);
+							break;
+					case "bool":
+							if (tooltip == undefined) tooltip = priv.options.types.bool.filterTooltip;
+							tooltip = (tooltip === true || tooltip == undefined) ? 'Toggle between:<br/>indeterminate,<br/>checked,<br/>unchecked' : tooltip === false ? '' : tooltip;
+							elem = $('<input class="filter indeterminate" checked type="checkbox" />');
+							elem.on('click', {column: column}, priv.filterChanged);
+							break;
+					case "p5:price":
+					case "p5:www_link":
+					case "p5:string":
+					case "string":
+							if (placeHolder == undefined) placeHolder = priv.options.types.string.placeHolder;
+							placeHolder = (placeHolder === true || placeHolder == undefined) ? '%' : placeHolder === false ? '' : placeHolder;
+							if (tooltip == undefined) tooltip = priv.options.types.string.filterTooltip;
+							tooltip = (tooltip === true || tooltip == undefined) ? 'Find str: str<br/>Find all but str: !str<br/>Find str inside: %str%' : tooltip === false ? '' : tooltip;
+							elem = $(p5Utils__format('<input placeholder="{0}" class="filter" type="text" size="8" />', [placeHolder]));
+							elem.on('keyup', {column: column}, priv.filterChanged);
+							break;
+					case "special":
+							elem = $('<div>&nbsp;</div>');
+							break;
+					case "simpleLink":
+							elem = $('<div>&nbsp;</div>');
+							break;
+					case "geom":
+							if (placeHolder == undefined) placeHolder = priv.options.types.string.placeHolder;
+							placeHolder = (placeHolder === true || placeHolder == undefined) ? '%' : placeHolder === false ? '' : placeHolder;
+							elem = $(p5Utils__format('<input placeholder="{0}" class="filter" type="text" size="8" />', [placeHolder]));
+							elem.on('keyup', {column: column}, priv.filterChanged);
+							break;
+					case "none":
+							elem = $('<div>&nbsp;</div>');
+							break;
+					case "ref": // TODO: search for ref fields
+							elem = $('<div>&nbsp;</div>')
+							break;
+					default:
+							elem = $('<div>&nbsp;</div>')
+							break;
+			}
+
+			if (false) {// tooltip OFF - (tooltip)
+				elem.tooltip({
+					title: tooltip.trim(),
+					html: true,
+					container: 'body',
+					trigger: 'focus',
+					placement: 'top',
+					delay: {
+						show: 500,
+						hide: 100
+					}
+				});
+			}
+
+			if (elem && props.filter) {
+				$.map(_state.filters.filterCols, function(colProps, col) {
+					if (col == column) {
+						var columnSettings = _data.cols[col];
+						if (columnSettings.type == 'bool') {
+							if (colProps.filter) elem.prop('checked', true).removeClass('indeterminate');
+							else if (!colProps.filter) elem.prop('checked', false).removeClass('indeterminate');
+							else if (colProps.filter == '') elem.addClass('indeterminate');
+						}
+						else elem.val(colProps.filter);
+					}
+				});
+				if (priv.options.forceFilterInit && undefined !== priv.options.forceFilterInit[column]) {
+					elem.prop('disabled', true);
+				}
+				elem.appendTo(headCell);
+
+				_filterFields[column] = elem;
+			}
+		}
+
+		currentNode.replaceWith(node);
+	};
+
+	priv.renderTableTfoot = function() {
+		_uiNode$Table.find('tfoot').remove();
+		_foot = $('<tfoot></tfoot>').insertAfter(_bodyNode);
+		//$(_uiNodeCont).next('.foot').replaceWith(_foot);
+	};
+
+	priv.renderFooter = function() {
+		priv.renderFooterInfo();
+		priv.renderFooterPagination();
+		priv.renderFooterPageSizes();
+		priv.renderFooterColumnPicker();
+		priv.renderFooterFunctions();
+		priv.renderFooterExport();
+	};
+
+	priv.renderFooterInfo = function() {
+		var nodeClass = 'tblAjax__' + 'footer__toolbar__info',
+				currentNode = $(_uiNodeCont).next('.foot').find('.' + nodeClass),
+				node;
+		if (priv.options.debug) console.log('Render: ', nodeClass);
+		node = $('<div class="foot-info ' + nodeClass + '"></div>');
+		//console.log('renderFooterInfo total', _data.total, 'page', _state.page, 'priv.options.pageSize', priv.options.pageSize);
+		var fromRow = Math.max(_state.page - 1, 0) * _state.pageSize;
+		var total = _data.total;
+		var toRow = Math.min(fromRow + _state.pageSize, total);
+		if (_data.total > 0) {
+			$(p5Utils__format('<p>Wiersze od {0} do {1} z {2}</p>', [fromRow + 1, toRow, total])).appendTo(node);
+		} else {
+			$('<p>Brak wierszy pasujących do kryteriów wyszukiwania</p>').appendTo(node);
+		}
+		currentNode.replaceWith(node);
+	};
+	priv.renderFooterPagination = function() {
+		var nodeClass = 'tblAjax__' + 'footer__toolbar__pagination',
+				currentNode = $(_uiNodeCont).next('.foot').find('.' + nodeClass),
+				node;
+		var currPage = _state.page;
+		var totalPages = Math.ceil(_data.total / _state.pageSize);
+		if (priv.options.debug) console.log('Render: ', nodeClass, '_data.total', _data.total, 'currPage', currPage);
+		/// console.log('Render: ', nodeClass, '_data.total', _data.total, 'currPage', currPage, '_state.page', _state.page);
+		if (_data.total > 0) {
+			node = $('<div class="btn-group ' + nodeClass + '"></div>');
+			//create the pager.
+			var lowerPage = currPage - 2;
+			var upperPage = currPage + 2;
+			if (upperPage > totalPages) {
+				var diff = upperPage - totalPages;
+				upperPage = totalPages;
+				lowerPage -= diff;
+			}
+			if (lowerPage < 1) lowerPage = 1;
+			if (upperPage < 5) upperPage = 5;
+
+			//$(p5Utils__format('<li class="{0}"><a href="#">&lt;&lt;</a></li>', [currPage == 1 ? 'disabled' : '']))
+			$(p5Utils__format('<button type="button" class="btn btn-default{0}">&lt;&lt;</button>', [currPage == 1 ? ' disabled' : '']))
+				.on('click', {pageIndex: 1}, priv.pageChanged)
+				.appendTo(node);
+			//$(p5Utils__format('<li class="{0}"><a href="#">&lt;</a></li>', [currPage == 1 ? 'disabled' : '']))
+			$(p5Utils__format('<button type="button" class="btn btn-default{0}">&lt;</button>', [currPage == 1 ? ' disabled' : '']))
+				.on('click', {pageIndex: currPage - 1}, priv.pageChanged)
+				.appendTo(node);
+
+			for (var i = lowerPage; i <= upperPage; i++) {
+				var link;
+				//if (i != currPage) link = $(p5Utils__format('<li class="{1}"><a href="#">{0}</a></li>', [i, i > totalPages ? 'disabled' : '']));
+				if (i != currPage) link = $(p5Utils__format('<button type="button" class="btn btn-default{1}">{0}</button>', [i, i > totalPages ? ' disabled' : '']));
+				//if (i == currPage) link = $(p5Utils__format('<li class="active"><a href="#">{0}</a></li>', [i]));
+				if (i == currPage) link = $(p5Utils__format('<button type="button" class="btn btn-default active">{0}</button>', [i]));
+
+				if (link) {
+					link.on('click', {pageIndex: i}, priv.pageChanged);
+					link.appendTo(node);
+				}
+			}
+			//$(p5Utils__format('<li class="{0}"><a href="#">&gt;</a></li>', [currPage == totalPages ? 'disabled' : '']))
+			$(p5Utils__format('<button type="button" class="btn btn-default{0}">&gt;</button>', [currPage == totalPages ? ' disabled' : '']))
+				.on('click', {pageIndex: currPage + 1}, priv.pageChanged)
+				.appendTo(node);
+			//$(p5Utils__format('<li class="{0}"><a href="#">&gt;&gt;</a></li>', [currPage == totalPages ? 'disabled' : '']))
+			$(p5Utils__format('<button type="button" class="btn btn-default{0}">&gt;&gt;</button>', [currPage == totalPages ? ' disabled' : '']))
+				.on('click', {pageIndex: totalPages}, priv.pageChanged)
+				.appendTo(node);
+		} else {
+			node = $('<span class="' + nodeClass + '"></span>');
+		}
+		currentNode.replaceWith(node);
+	};
+	priv.renderFooterPageSizes = function() {
+		var nodeClass = 'tblAjax__' + 'footer__toolbar__pagesizes',
+				currentNode = $(_uiNodeCont).next('.foot').find('.' + nodeClass),
+				node;
+		if (priv.options.debug) console.log('Render: ', nodeClass);
+		if (_data.total > 0 && priv.options.pageSizes.length > 0) {
+			node = $('<div class="btn-group dropup pagesize ' + nodeClass + '"></div>');
+			var btn = $('<button class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" href="#">Liczba wierszy&nbsp;</button>').appendTo(node);
+			var span = $('<span class="caret"></span>').appendTo(btn);
+			var ul = $('<ul class="dropdown-menu" style="max-height:250px;overflow:auto;">').appendTo(node);
+
+			$.each(priv.options.pageSizes, function(index, val) {
+				var li = $('<li></li>').appendTo(ul);
+				if (val == priv.options.pageSize) {
+					$(p5Utils__format('<a style="color:#337AB7;">{0}</a>', [val])).appendTo(li);
+				} else {
+					$(p5Utils__format('<a href="#">{0}</a>', [val])).appendTo(li);
+				}
+			});
+			node.on('click', 'a', priv.pageSizeChanged);
+		} else {
+			node = $('<span class="' + nodeClass + '"></span>');
+		}
+		currentNode.replaceWith(node);
+	};
+	priv.renderFooterColumnPicker = function() {
+		var nodeClass = 'tblAjax__' + 'footer__toolbar__columnPicker',
+				currentNode = $(_uiNodeCont).next('.foot').find('.' + nodeClass),
+				node;
+		if (priv.options.debug) console.log('Render: ', nodeClass);
+		if (priv.options.columnPicker) {
+			node = $('<div class="btn-group dropup columnpicker ' + nodeClass + '"></div>');
+			var btn = $('<button class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" href="#">Kolumny&nbsp;</button>').appendTo(node);
+			var span = $('<span class="caret"></span>').appendTo(btn);
+			var ul = $('<ul class="dropdown-menu" style="max-height:250px;overflow:auto;">').appendTo(node);
+
+			var selectedFilter = priv.modelColFilter_getSelected();
+			priv.modelColFilter_getFilters().map(function(colFltr) {
+				var li = $('<li></li>').appendTo(ul),
+						selected = (selectedFilter && selectedFilter == colFltr.name)? 'checked="checked"' : '',
+						input = '<input ' + selected + ' type="radio" style="margin:0"/>',
+						a = $('<a href="#" data-col_filter="' + colFltr.name + '" style="padding:0px 20px;">' + input + ' ' + colFltr.label + '</a>').appendTo(li);
+						if ('all' != colFltr.name && 'most_used' != colFltr.name) {
+							arm = $('<button data-col_filter="' + colFltr.name + '" class="pull-right btn btn-xs btn-link" style="margin:0;padding:0;border:0;color:red">' + '&times;' + '</a>').appendTo(a);
+							arm.on('click', priv.modelColFilter_onClickRemoveFilter)
+						}
+			});
+
+			{// if (!selectedFilter) {// save current filter
+				var li = $('<li style="text-align:center; clear:both"></li>').appendTo(ul)
+				priv.modelColFilter_getSaveBtn().appendTo(li);
+			}
+
+			$('<li class="divider"></li>').appendTo(ul);
+			$.each(_data.cols, function(col, props) {
+				if (props.type != "unique" && col != _state.primaryKey) {
+					var li = $('<li></li>').appendTo(ul),
+							label = (props.friendly || col).replace(/<br\/?>/g, ' '),
+							input = p5Utils__format('<input {0} type="checkbox" title="{1}" value="{1}" style="margin:0"/>&nbsp;{2}', [(props.hidden) ? '' : 'checked', col, label]),
+							a = $('<a href="#" style="padding:0px 20px;">' + input + '</a>').appendTo(li);
+				}
+			});
+			node.on('click', 'input[type="checkbox"]', priv.columnPickerClicked);
+			node.on('click', 'a', priv.columnPickerLinkClicked);
+		} else {
+			node = $('<span class="' + nodeClass + '"></span>');
+		}
+		currentNode.replaceWith(node);
+	};
+	priv.renderFooterFunctions = function() {
+		var nodeClass = 'tblAjax__' + 'footer__toolbar__functions',
+				currentNode = $(_uiNodeCont).next('.foot').find('.' + nodeClass),
+				node;
+		if (priv.options.debug) console.log('Render: ', nodeClass);
+		if (priv.options.tblFunctions) {
+			node = $('<div class="btn-group ' + nodeClass + '"></div>');
+			$.map(priv.options.tblFunctions, function(funObj, funName){
+				var funHtml = $('<button class="btn btn-sm btn-default"></button>');
+				if (funObj.title) funHtml.attr('title', funObj.title);
+				if (funObj.method) funHtml.on('click', priv[funObj.method]);
+				else if (funObj.href) funHtml.on('click', {hash:funObj.href}, priv.routeChanged);
+
+				var funIconHtml = $('<i></i>');
+				if (funObj.icon) funIconHtml.attr('class', 'glyphicon glyphicon-' + funObj.icon);
+				funIconHtml.prependTo(funHtml);
+
+				funHtml.addClass('btn');
+				funHtml.appendTo(node);
+			});
+		} else {
+			node = $('<span class="' + nodeClass + '"></span>');
+		}
+		currentNode.replaceWith(node);
+	};
+	priv.renderFooterExport = function() {
+		var nodeClass = 'tblAjax__' + 'footer__toolbar__export',
+				currentNode = $(_uiNodeCont).next('.foot').find('.' + nodeClass),
+				node;
+		if (priv.options.debug) console.log('Render: ', nodeClass);
+		if (priv.options.exportFields && priv.options.exportFields.length) {
+			node = $('<div class="btn-group dropup pagesize ' + nodeClass + '"></div>');
+			var btn = $('<button class="btn btn-sm dropdown-toggle" data-toggle="dropdown" href="#">Export&nbsp;</button>').appendTo(node);
+			var span = $('<span class="caret"></span>').appendTo(btn);
+			var ul = $('<ul class="dropdown-menu" style="max-height:250px;padding:5px 20px 5px 8px;overflow:auto;">').appendTo(node);
+
+			$.each(_data.cols, function(col, props) {
+				if (-1 !== priv.options.exportFields.indexOf(col)) {
+					var li = $('<li></li>').appendTo(ul);
+					$(p5Utils__format('<input {0} type="checkbox" title="{1}" value="{1}" >&nbsp;{2}</input>', [(_exportFieldsSelect[col])? 'checked' : '', col, props.friendly || col])).appendTo(li);
+					li.on('click', 'input', priv.exportFieldChanged);
+				}
+			});
+
+			var li = $('<li></li>').appendTo(ul);
+			$('<a href="index.php" target="_blank" class=""><i class="glyphicon glyphicon-share"></i>Export do HTML</a>').appendTo(li);
+			li.on('click', 'a', priv.exportToHTML);
+			var li = $('<li></li>').appendTo(ul);
+			$('<a href="index.php" target="_blank" class=""><i class="glyphicon glyphicon-share"></i>Export do CSV</a>').appendTo(li);
+			li.on('click', 'a', priv.exportToCSV);
+			var li = $('<li></li>').appendTo(ul);
+			$('<a href="index.php" target="_blank" class=""><i class="glyphicon glyphicon-share" title="Export do pliku CSV w kodowaniu Windows-1250"></i>Export do CSV (Windows-1250)</a>').appendTo(li);
+			li.on('click', 'a', priv.exportToCSVWinCP1250);
+		} else {
+			node = $('<span class="' + nodeClass + '"></span>');
+		}
+		currentNode.replaceWith(node);
+	};
+
+	priv.renderInlineEditBox = function() {
+		var nodeClass = 'tblAjax__' + 'inlineEditBox',
+				currentNode = $(_uiNodeCont).parent().children('.' + nodeClass),
+				node;
+		var node = $('<div class="' + nodeClass + ' modal fade" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"></div>');
+		var modalWrap = $('<div class="modal-dialog"></div>').appendTo(node);
+		var modalWrap = $('<div class="modal-content"></div>').appendTo(modalWrap);
+		var frmInlineEdit = $('<form style="margin:0;padding:0;"></form>').appendTo(modalWrap);
+		var iebHead = $('<div class="modal-header" style="cursor:pointer">').appendTo(frmInlineEdit);
+		$('<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="glyphicon glyphicon-remove" style="color:red"></i></button>').appendTo(iebHead);
+		$('<h4 id="myModalLabel">Edytuj</h4>').appendTo(iebHead);
+		var iebBodyWrap = $('<div class="modal-body" style="padding:0"></div>').appendTo(frmInlineEdit);
+		var iebBody = $('<div style="padding:15px"></div>').appendTo(iebBodyWrap);
+		$('<input type="hidden" name="ID" value="">').appendTo(iebBody);
+		$('<input type="hidden" name="col" value="">').appendTo(iebBody);
+		$('<div class="inlineEditBox-cnt"></div>').appendTo(iebBody);
+		var iebFoot = $('<div class="modal-footer"></div>').appendTo(frmInlineEdit);
+		$('<button class="btn btn-close" data-dismiss="modal" aria-hidden="true">Zamknij</button>').appendTo(iebFoot);
+		var iebBtnSave = $('<input type="submit" value="Zapisz" class="btn btn-primary btn-save">').appendTo(iebFoot);
+
+		frmInlineEdit.on('submit', function() {
+			var inlineEditBox$Node = $(_uiNodeCont).parent().children('.tblAjax__inlineEditBox');
+			var data = inlineEditBox$Node.find('form').serialize();
+			inlineEditBox$Node.find('.inlineEditBox-cnt').html('<span class="loading-info"> loading ...</span>');
+			function notifyAjaxCallback(data) {
+				var notify = {};
+				notify.type = (data && data.type)? data.type : '';
+				notify.msg = (data && data.msg)? data.msg : '';
+				switch (notify.type) {
+					case 'success':
+						if (!notify.msg) notify.msg = 'Dane poprawnie zaktualizowane';
+						break;
+					case 'info':
+						if (!notify.msg) notify.msg = 'Nie wprowadzono żadnych zmian';
+						break;
+					case 'error':
+						if (!notify.msg) notify.msg = 'Wystąpiły błędy';
+						break;
+					case 'warning':
+						notify.type = 'warn';
+						if (!notify.msg) notify.msg = 'Wystąpiły błędy';
+						break;
+					default:
+						notify.msg = 'Nieznany błąd';
+						if (data && data.errorCode) notify.msg += ' ' + data.errorCode;
+						notify.type = '';
+				}
+				jQuery.notify(notify.msg, notify.type);
+			}
+
+			$.ajax({
+				data: data,
+				dataType: 'json',
+				type: "POST",
+				url: 'index-ajax.php?_zasobID=' + ZASOB_ID + '&_cls=' + CLASS_NAME + '&_hash=' + HTML_ID + '&_task=EDIT_INLINE_SAVE'
+			})
+			.done(function(data, textStatus, jqXHR){
+				notifyAjaxCallback(data);
+				publ.refresh();
+				inlineEditBox$Node.modal('hide');
+			})
+			.fail(function(jqXHR){// jqXHR.fail(function( jqXHR, textStatus, errorThrown ) {});
+				if (jqXHR.responseJSON) {
+					notifyAjaxCallback(jqXHR.responseJSON);
+				}
+				else {
+					var txt = jqXHR.responseText || 'Wystąpiły błędy';
+					if (jqXHR.status == 404) {
+						jQuery.notify(jqXHR.responseText, 'error');
+					} else {
+						jQuery.notify(jqXHR.responseText, 'warn');
+					}
+				}
+				inlineEditBox$Node.modal('hide');
+			});
+
+			return false;
+		});
+
+		currentNode.replaceWith(node);
+	};
+
+	priv.exportFieldChanged = function(e) {
+
+		e.stopPropagation();
+
+		var column = $(this).val();
+
+		_exportFieldsSelect[column] = !_exportFieldsSelect[column];
+	};
+
+	priv.exportToHTML = function(e) {
+		priv.exportData('html', $(this), e);
+	};
+	priv.exportToCSV = function(e) {
+		priv.exportData('csv', $(this), e);
+	};
+	priv.exportToCSVWinCP1250 = function(e) {
+		priv.exportData('csv_cp1250', $(this), e);
+	};
+	priv.exportData = function(format, node, e) {
+		var exportFields = [];
+		$.each(_exportFieldsSelect, function(col, selected) {
+			if (selected) {
+				exportFields.push(col);
+			}
+		});
+
+		if (!exportFields.length) {
+			alert('Nie wybrano żadnych pól do eksportu.');
+			e.preventDefault();
+			return false;
+		}
+
+		var limit = 10000;
+		if (!_data.total || _data.total <= 0) {
+			alert('Brak rekordów do eksportu.');
+			e.preventDefault();
+			return false;
+		}
+		if (_data.total > limit) {
+			if (!confirm('Za dużo rekordów. Wyeksportowane zostaną tylko pierwsze ' + limit + ' z ' + _data.total + ' rekordów.')) {
+				e.preventDefault();
+				return false;
+			}
+		}
+
+		var exportUrl = 'index.php?_route=ViewTableAjax&_task=export&namespace=' + NAMESPACE;
+		exportUrl += '&format=' + format;
+		exportUrl += '&flds=' + exportFields.join(',');
+		exportUrl += '&sortCol=' + (_state.filters.currSortCol || '');
+		exportUrl += '&sortDir=' + (_state.filters.currSortFlip ? "desc" : "asc");
+
+		if (Object.keys(_state.filters.filterCols).length > 0) {
+			$.each(_state.filters.filterCols, function(col, colProps) {
+				if (colProps.filter && colProps.filter.length > 0) {
+					exportUrl += '&f_' + col + '=' + colProps.filter;
+				}
+			});
+		}
+
+		// specialFilters
+		$.each(_state.specialFilters, function(groupName, btnValue) {
+			if (btnValue.length > 0) {
+				exportUrl += '&sf_' + groupName + '=' + btnValue;
+			}
+		});
+
+		if (priv.options.forceFilterInit) {
+			$.map(priv.options.forceFilterInit, function(fltrProps, fltr) {
+				exportUrl += '&sf_' + fltr + '=' + fltrProps;
+			});
+		}
+
+		node.attr('href', exportUrl);
+	};
+
+	/*
+	 calls the webservice(if defined).
+	 used only inside priv.init
+	 */
+	priv.update = function(callback) {
+		var skipCols, resetChecked;// undefined
+		if (!priv.options.url) {
+			if (priv.options.debug) console.log('no url found');
+			return;
+		}
+
+		if (priv.options.debug) console.log(p5Utils__format('requesting data from url:{0} data:{1}', [priv.options.url, JSON.stringify(priv.options.urlData) || '']));
+
+		var initUrlAdd = '';
+		var filtersInitSet = false;
+		initUrlAdd += '&currSortCol=' + _state.filters.currSortCol;
+		initUrlAdd += '&currSortFlip=' + ((_state.filters.currSortCol)? "desc" : "asc");
+		if (Object.keys(_state.filters.filterCols).length > 0) {
+			$.each(_state.filters.filterCols, function(col, colProps) {
+				if (colProps.filter && colProps.filter.length > 0) {
+					initUrlAdd += '&f_' + col + '=' + encodeURIComponent(colProps.filter);
+					filtersInitSet = true;
+				}
+			});
+		}
+		$.each(_state.specialFilters, function(groupName, btnValue) {
+			if (btnValue.length > 0) {
+				initUrlAdd += '&sf_' + groupName + '=' + encodeURIComponent(btnValue);
+				filtersInitSet = true;
+			}
+		});
+
+		if (priv.options.forceFilterInit) {
+			$.map(priv.options.forceFilterInit, function(fltrProps, fltr) {
+				initUrlAdd += '&f_' + fltr + '=' + encodeURIComponent(fltrProps);
+				filtersInitSet = true;
+			});
+		}
+
+		// p5UI__notifyAjaxCallback({type: 'info', msg: 'pobieranie danych (init) ...'});
+		window.fetch(priv.options.url + initUrlAdd, {
+			method: priv.options.urlPost ? 'POST' : 'GET',
+			credentials: 'same-origin',// add cookies
+		}).then(function (response) {
+			return response.json()
+		}).then(function (data) {
+			if (priv.options.debug) console.log('loadDataAjax:fetch:update: request finished response data:', data);
+			if ('success' == data.type) {
+				// p5UI__notifyAjaxCallback(data);
+				return data;
+			} else if ('error' == data.type) {
+				p5UI__notifyAjaxCallback(data);
+			} else {
+				p5UI__notifyAjaxCallback(data);
+			}
+			return null;
+		}).then(function (data) {
+			if (priv.options.debug) console.log('loadDataAjax:fetch:update: request finished data:', data);
+			if (!data) return;
+			if (data && data.cols) {
+				priv.setStateCols(data.cols, data.primaryKey);
+			}
+
+			// set initial filters (_state.filters.filterCols)
+			if (filtersInitSet && _data && _data.cols) {
+				$.map(reqData, function(fltrValue, fltr){
+					var colName = null;
+					if (fltr.substr(0, 3) == 'sf_') {
+						colName = fltr.substr(3);
+					}
+					else if (fltr.substr(0, 2) == 'f_') {
+						colName = fltr.substr(2);
+					}
+
+					if (colName && _data.cols[colName]) {
+						//add the filter to the filter array
+						_state.filters.filterCols[colName] = {
+							filter: fltrValue,
+							colName: colName
+						};
+					}
+				});
+			}
+
+			// assign the new state (data)
+			state = {data: {}};
+			if (!skipCols) state.data.cols = data.cols || {};
+			state.data.rows = data.rows || [];
+			state.data.total = data.total || 0;
+			state.data.primaryKey = data.primaryKey || 'ID';
+			state.page = data.page || 0;
+			state.pageSize = data.pageSize || priv.options.pageSize;
+			state.filters = data.filters || {};
+			priv.setState(state);
+
+			if (typeof callback == "function") {
+				callback.call(this);
+			}
+		}).catch(function (e) {
+			console.log('loadDataAjax:fetch: ERR:', e);
+		});
+	};
+
+	/*
+	 assigns the new data.
+	 */
+	priv.setState = function(state) {
+		var oldState = _state; // TODO: use to check what really changed (use extend!)
+		var renderParts = {};
+		if (state.data) {
+			if (state.data.cols && Object.keys(state.data.cols).length > 0) {// TODO: never happen, but if happend then rerender all
+				priv.setStateCols(state.data.cols, state.data.primaryKey);
+				priv.setStateData(state.data);
+			} else {
+				priv.setStateData(state.data);
+			}
+			renderParts['body'] = true;
+			renderParts['foot_pagination'] = true;
+		}
+		if (state.hasOwnProperty('specialFilters')) {
+			_state.specialFilters = state.specialFilters;
+			renderParts['head__specialFilters'] = true;
+		}
+		if (state.hasOwnProperty('page')) {
+			if (state.page != _state.page) {
+				_state.page = state.page;
+				renderParts['foot_pagination'] = true;
+			}
+		}
+		if (state.hasOwnProperty('pageSize')) {
+			if (state.pageSize != _state.pageSize) {
+				_state.pageSize = state.pageSize;
+				renderParts['foot_pagination'] = true;
+			}
+		}
+		if (state.hasOwnProperty('filters')) {
+			priv.setStateFilters(state.filters);
+			renderParts['head__specialFilters'] = true;
+			renderParts['foot_pagination'] = true;
+		}
+
+		renderParts = Object.keys(renderParts);
+
+		if (priv.options.debug) console.log('setState::renderParts: ', renderParts);//TODO:DBG:RMME
+		if (renderParts.length > 0) {
+			jQuery(_uiNodeCont).trigger('TableAjax:render', renderParts);
+		}
+	};
+
+	priv.setStateCols = function(cols, primaryKey) {
+		if (priv.options.debug) console.log('priv.setStateCols: ', {primaryKey: primaryKey, cols: cols});
+		// console.log("priv.setStateCols");
+		// console.trace();
+		_state.cols = cols;
+		_state.primaryKey = primaryKey || _state.primaryKey;
+		// fix col name - props.column
+		$.each(_state.cols, function(col, props) {
+			props.column = col;
+		});
+		// fix col types - default 'string'
+		$.each(_state.cols, function(col, props) {
+			if (!props.type) cols[col].type = "string";
+		});
+		// fix props.filter - set true if not set - TODO: allow filter this col?
+		$.each(_state.cols, function(col, props) {
+			if (props.filter == undefined) props.filter = true;
+		});
+		if (_state.primaryKey) {
+			//create a unique column definition
+			_state.cols["unique"] = {
+				column: "unique",
+				type: "unique",
+				index: -1,
+				hidden: true
+			};
+		}
+		_state.colsSorted = Object.keys(_state.cols).sort(function(a, b) {
+			return _state.cols[a].index - _state.cols[b].index;
+		});
+	};
+
+	priv.setStateFilters = function(stateFilters) {
+		var newFilterCols = {},
+				newSpecialFilters = {};
+		$.map(stateFilters, function(fltrProps, fltr) {
+			switch (fltr) {
+				case 'currSortCol': _state.filters.currSortCol = fltrProps; break;
+				case 'currSortFlip': _state.filters.currSortFlip = (fltrProps == 'desc')? true : false; break;
+				default:
+					if (fltr.substr(0, 2) == 'f_') {
+						newFilterCols[fltr.substr(2)] = {
+							filter: fltrProps,
+							colName: fltr.substr(2)
+						};
+					}
+					else if (fltr.substr(0, 3) == 'sf_') {
+						var groupName = fltr.substr(3);
+						if (priv.options.specialFilterFunctions.hasOwnProperty(groupName)) {
+							newSpecialFilters[groupName] = fltrProps;
+						}
+					}
+			}
+		});
+		_state.filters.filterCols = newFilterCols;
+		_state.specialFilters = newSpecialFilters;
+	};
+
+	priv.setStateData = function(pData, skipCols, resetChecked) {
+		var data = $.extend(true, {}, pData);
+		data.cols = _state.cols;// always use old cols - change cols mved to priv.setStateCols
+
+		_data = data;
+		_data.rowsOrg = _data.rows;
+
+		//we might have more/less data now. Recalculate stuff.
+		if (_state.page > 1) {
+			_totalPages = Math.ceil(_data.total / priv.options.pageSize);
+		}
+
+		//keep any previously checked rows around?
+		if (resetChecked === true || resetChecked === undefined)
+			_uniqueCols = {};
+		else {
+			for (var key in _uniqueCols)
+				_uniqueCols[key] = priv.getRow(key);
+		}
+
+		if (_state.primaryKey) {
+			//add rows that needs to be pre-checked
+			$.each(_data.rows, function(index, row) {
+				if (row["checked"] === true)
+					_uniqueCols[row[_state.primaryKey]] = row;
+			});
+		}
+	};
+
+	/*
+	 sorts the data on the current sorting column
+	 */
+	priv.sort = function() {
+		if (!_data.cols[_state.filters.currSortCol]) _state.filters.currSortCol = "";
+		if (!_state.filters.currSortCol) return;
+
+		publ.loadPage(0);
+	};
+
+	/*
+	 helper that returns the underlying data by the unique value
+	 */
+	priv.getRow = function(uniqueValue) {
+		var row;
+		$.each(_data.rowsOrg, function(i, r) {
+			if (r[_state.primaryKey] == uniqueValue) {
+				row = r;
+				return false;
+			}
+		});
+		return row;
+	};
+
+	/*
+	 when: typing a filter
+	 what: triggers filtering on the value
+	 */
+	priv.filterChanged = function(e) {
+		//clear old timer if we're typing fast enough
+		if (priv.options.debug) console.log('filterChanged::#1');
+		if (_filterTimeout) {
+			clearTimeout(_filterTimeout);
+			if (priv.options.debug) console.log('filterChanged::#2 previous filtering cancelled');
+		}
+
+		var filter = this.value
+			, fltr = $(this)
+			, col = _data.cols[e.data.column]
+			, timeout = 450;
+
+		if (this.value) {
+			if (!fltr.hasClass('filter-active')) {
+				fltr.addClass('filter-active');
+			}
+		} else {
+			if (fltr.hasClass('filter-active')) {
+				fltr.removeClass('filter-active');
+			}
+		}
+
+		//boolean filters needs some special care
+		if (col.type == "bool" || col.type == "unique") {
+			timeout = 0;
+			var elem = $(this);
+			var cssClass = 'indeterminate';
+			if (elem.hasClass(cssClass)) {
+				e.preventDefault();
+				elem.removeClass(cssClass);
+				filter = true;
+			} else {
+				if (!elem.is(':checked')) {
+					filter = false;
+				} else {
+					elem.addClass(cssClass);
+					filter = '';
+				}
+			}
+		}
+
+		//add the filter to the filter array
+		_state.filters.filterCols[col.column] = {
+			filter: filter,
+			colName: col.column
+		};
+		//wait a few deciseconds before filtering
+		_filterTimeout = setTimeout(function(filterValue, colName) {
+			_state.filters.filterCols[colName] = {
+				filter: filterValue,
+				colName: colName
+			};
+			priv.filter();
+		}, timeout, filter, col.column);
+	};
+
+	priv.filtersClean = function(e) {
+		if (_filterTimeout) {
+			clearTimeout(_filterTimeout);
+			if (priv.options.debug) console.log('filtering cancelled - filtersClean');
+		}
+
+		var filtersActive = false;
+		$.map(_state.filters.filterCols, function(colProps, col) {
+			if (colProps.filter.length > 0) {
+				filtersActive = true;
+				colProps.filter = '';
+			}
+		});
+
+		if (filtersActive) {
+			// TODO: dont remove value from forceFilterInit fields - no field names in search fields?
+			/// TODO: rerender _head (thead.filter)
+			var elems = _uiNode$Table.find('thead').find('.filter').find('input');
+			elems.val('').prop('disabled', false);
+			elems.removeClass('filter-active');
+			priv.filter();
+		}
+		return false;
+	},
+
+	/**
+	 * Filters the data.
+	 */
+	priv.filter = function() {
+		if (!priv.options.filter) return;
+		if (Object.keys(_state.filters.filterCols).length == 0) return;
+		priv.loadPage(0);
+	};
+
+	/**
+	 * Filters the data by specialFilter.
+	 * when: click on special filter
+	 * what: triggers filtering on the value
+	 */
+	priv.specialFilterChanged = function(e) {
+		if (priv.options.debug) console.log('specialFilterChanged e.data:', e.data);
+		var state = {};
+		state.specialFilters = _state.specialFilters;
+		state.specialFilters[e.data.group] = e.data.value;
+		priv.setState(state);
+
+		publ.loadPage(0);
+	};
+
+	priv.longTextChanged = function(e) {
+		priv.options.longDesc = !priv.options.longDesc;
+		_bodyNode.find('td').each(function(ind, el){
+			var $el = jQuery(el);
+			if (!$el.attr('class') || $el.attr('class') == 'tbl-short-txt') {
+				//console.log(el.attr('class') + ': ' + el.text());
+				if (el.firstChild && el.firstChild.nodeName == 'SPAN') {
+					if (priv.options.longDesc) {
+						$el.addClass('tbl-short-txt');
+						var $elSpan = jQuery(el.firstChild);
+						$elSpan.tooltip({title: $elSpan.text(), placement: 'left'});
+					} else {
+						$el.removeClass('tbl-short-txt');
+					}
+				}
+			}
+		});
+		return false;
+	};
+
+	priv.mapEditorHide = function() {
+		priv.options.mapEditor = false;
+		if ('window' == priv.options.mapEditorContainer) {
+			//_mapEditorDialog.dialog("close");
+			_mapEditorDialog.dialog("destroy");
+		}
+		_mapEditorWrap.hide();
+		_mapEditor.hide();
+	};
+
+	priv.mapEditorShow = function() {
+		//console.log('TODO:mapEditorShow():', priv.options.mapEditorContainer, 'visible:', priv.options.mapEditor);
+		priv.options.mapEditor = true;
+		_mapEditor.show();
+		_mapEditorWrap.show();
+		_mapEditor.TableAjaxMap({
+			//debug: true,
+			wpsUrl: 'http://biuro.biall-net.pl/wps',
+			wfsUrl: 'http://biuro.biall-net.pl/wps',
+			showAddLayerWidget: (HAS_ADDITIONAL_LAYERS) ? true : false,
+			zoomStrategyActivate: 14,
+			layerName: LABEL_HTML,
+			addBtn: {
+				title: 'Przenieś mapę do okna',
+				displayClass: 'mapEditor-btnBackToWindow',
+				trigger: function() {
+					priv.mapEditorHide();
+					priv.options.mapEditorContainer = 'window';
+					priv.mapEditorShow();
+					priv.resizableMapDockNode('destroy');
+				}
+			},
+			onSaveFeature: function(selectedRecordId, selectedFeatureExtent) {
+				if (priv.options.debug) console.log('onSaveFeature (',selectedRecordId,'/',selectedFeatureExtent,') ', this);
+				if (!selectedRecordId) {
+					alert('Brak zaznaczonego rekordu - wybierz rekord z tabeli');
+					return;
+				}
+
+				function notifyAjaxCallback(data) {
+					var notify = {};
+					notify.type = (data && data.type)? data.type : '';
+					notify.msg = (data && data.msg)? data.msg : '';
+					switch (notify.type) {
+						case 'success':
+							if (!notify.msg) notify.msg = 'Aktualizacja danych dla rekordu ' + selectedRecordId;
+							break;
+						case 'info':
+							if (!notify.msg) notify.msg = 'Nie wprowadzono żadnych zmian';
+							break;
+						case 'error':
+							if (!notify.msg) notify.msg = 'Wystąpiły błędy';
+							break;
+						case 'warning':
+							notify.type = 'warn';
+							if (!notify.msg) notify.msg = 'Wystąpiły błędy';
+							break;
+						default:
+							notify.msg = 'Nieznany błąd';
+							if (data && data.errorCode) notify.msg += ' ' + data.errorCode;
+							notify.type = '';
+					}
+					jQuery.notify(notify.msg, notify.type);
+				}
+
+				$.ajax({
+					data: {polygon: selectedFeatureExtent},
+					dataType: 'json',
+					type: "POST",
+					url: 'index-ajax.php?_zasobID=' + ZASOB_ID + '&_cls=' + CLASS_NAME + '&_hash=' + HTML_ID + '&_task=THE_GEOM_SAVE&ID=' + selectedRecordId
+				})
+				.done(function(data, textStatus, jqXHR){
+					notifyAjaxCallback(data);
+					//jQuery.notify('Aktualizacja danych dla rekordu ' + selectedRecordId, 'success');
+					publ.loadPage(0);// TODO: reload table data
+				})
+				.fail(function(jqXHR){// jqXHR.fail(function( jqXHR, textStatus, errorThrown ) {});
+					if (jqXHR.responseJSON) {
+						notifyAjaxCallback(jqXHR.responseJSON);
+					}
+					else {
+						var txt = jqXHR.responseText || 'Wystąpiły błędy';
+						if (jqXHR.status == 404) {
+							jQuery.notify(jqXHR.responseText, 'error');
+						} else {
+							jQuery.notify(jqXHR.responseText, 'warn');
+						}
+					}
+				});
+
+			},
+			onSelectBox: function(bounds) {
+				if (undefined !== OpenLayers.Bounds && bounds instanceof OpenLayers.Bounds) {
+					var column = 'the_geom';
+					if (undefined !== _filterFields['the_geom']) {
+						var filter = 'BBOX:' + bounds.top + ',' + bounds.right + ',' + bounds.bottom + ',' + bounds.left;
+						_filterFields[column].val(filter);
+						_state.filters.filterCols[column] = {
+							filter: filter,
+							colName: column
+						};
+						priv.filter();
+					}
+				}
+			}
+		});
+
+		priv.mapEditorShowElement();
+	};
+
+	priv.mapEditorShowElement = function() {
+		if ('window' == priv.options.mapEditorContainer) {
+			var mapEditor = _mapEditorWrap.children('.mapEditor-map');
+			if (!mapEditor || !mapEditor.length) {
+				var tblCont = jQuery(_uiNodeCont).parent('.AjaxTableCont');
+				var mapEditor = tblCont.children('.AjaxTableCont-mapEditorContainer').children('.mapEditor-map');
+				if (!mapEditor || !mapEditor.length) {
+					// TODO: create new map
+				}
+				_mapEditorWrap.append(mapEditor);
+			}
+
+			_mapEditorWrap.css({padding:'5px'});
+			_mapEditorDialog = _mapEditorWrap.dialog({
+				width: 540,
+				minWidth: 400,
+				minHeight: 400,
+				open: function(e, ui) {
+					var n = jQuery(this),
+							mapWrap = n.children(":first"),
+							map = mapWrap.children(":first");
+					map.css({width: n.width() - 5, height: n.height() - 5});
+					_mapEditor.TableAjaxMapUpdateSize();
+				},
+				resizeStart: function(e, ui) {
+					jQuery(this).children(":first").children(":first").css({display:'none'});
+				},
+				resizeStop: function(e, ui){
+					var n = jQuery(this),
+							map = n.children(":first").children(":first");
+					n.css({width: n.parent().width()});
+					map.css({display: 'block', height: n.height(), width: n.width()});
+					_mapEditor.TableAjaxMapUpdateSize();
+				},
+				dragStop: function(e, ui){
+					_mapEditor.TableAjaxMapUpdateSize();// to prevent drag-zoom error
+				}
+			});
+			_mapEditorWrap.bind('dialogclose', function(e) {
+				priv.options.mapEditor = false;
+			});
+			var dialogTitleBar = _mapEditorDialog.parent().parent().find('.ui-dialog-titlebar');
+			var dialogTitleBarCloseBtn = dialogTitleBar.find('.ui-dialog-titlebar-close');
+			if (dialogTitleBarCloseBtn) {
+				var btnToggle = jQuery('<span style="width:18px;height:18px;position:absolute;top:50%;right:25px;margin-top:-9px;" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-icon-only"><i style="margin-top:3px;" class="glyphicon glyphicon-fullscreen"></i></span>');
+				btnToggle.on('click', function(e) {
+					priv.options.mapEditorContainer = 'dock';
+					_mapEditorWrap.dialog('close');
+					priv.mapEditorShowElement();
+				});
+				btnToggle.insertBefore(dialogTitleBarCloseBtn);
+			}
+		}
+		else if ('dock' == priv.options.mapEditorContainer) {
+			priv.resizableMapDockNode('prepare');
+			_mapEditor.TableAjaxMapUpdateSize();
+		}
+	};
+
+	priv.resizableMapDockNode = function(task) {
+		if ('prepare' == task) {
+			var mapEditor = _mapEditorWrap.children('.mapEditor-map');
+			if (mapEditor && mapEditor.length) {// map is inside _mapEditorWrap (window)
+				var mapDockNode = jQuery('<div class="AjaxTableCont-mapEditorContainer"></div>');
+				var tblCont = jQuery(_uiNodeCont).parent('.AjaxTableCont');
+				tblCont.children('.AjaxTableCont-mapEditorContainer').remove();
+				var breadcrumb = tblCont.children('.breadcrumb');
+				if (!breadcrumb || !breadcrumb.length) {
+					tblCont.prepend(mapDockNode);
+				} else {
+					mapDockNode.insertAfter(breadcrumb);
+				}
+				mapDockNode.append(mapEditor);
+			}
+			var mapDockResizable = mapEditor.parent();
+			mapDockResizable.resizable({handles: 's', minHeight: 300, minWidth: 300});
+			var resizeLineHeigth = 6;
+			mapDockResizable.children(":first").css({height: 'auto'});
+			mapDockResizable.css({marginBottom: resizeLineHeigth+'px'});
+			mapEditor.css({marginBottom: resizeLineHeigth+'px'});
+			mapEditor.children(":first").css({width: mapEditor.width()});
+			mapDockResizable.find('.ui-resizable-s').css({height:resizeLineHeigth+'px', bottom: '-'+resizeLineHeigth+'px'});
+			mapDockResizable.on('resizestart', function(event, ui) {
+				ui.element.children(":first").children(":first").css({display:'none'})
+			});
+			mapDockResizable.on('resizestop', function(event, ui) {
+				ui.element.children(":first").children(":first").css({display:'block', height: ui.element.height()});
+			});
+		} else if ('destroy' == task) {
+			var mapDockResizable = jQuery(_uiNodeCont).parent('.AjaxTableCont').children('.AjaxTableCont-mapEditorContainer');
+			mapDockResizable.resizable('destroy');
+			mapDockResizable.remove();
+		}
+	};
+
+	priv.mapEditorChanged = function(e) {
+		if (priv.options.debug) console.log('mapEditorChanged option(',priv.options.mapEditor,')');
+		priv.options.mapEditor = !priv.options.mapEditor;
+		if (priv.options.mapEditor) {
+			priv.mapEditorShow();
+		}
+		else {
+			priv.mapEditorHide();
+		}
+		return false;
+	};
+
+	/*
+	 when: changing page in pager
+	 what: triggers table to be created with new page
+	 */
+	priv.pageChanged = function(e) {
+		e.preventDefault();
+		var totalPages = Math.ceil(_data.total / _state.pageSize);
+		if (e.data.pageIndex < 1 || e.data.pageIndex > totalPages) return;
+
+		//set the new page
+		_state.page = e.data.pageIndex;
+
+		publ.loadPage(_state.page);
+	};
+
+	/*
+	 when: changing pagesize in pagesize dropdown
+	 what: triggers table to be created with new pagesize
+	 */
+	priv.pageSizeChanged = function(e) {
+		e.preventDefault();
+		var val = $(this).text();
+		if (priv.options.debug) console.log(p5Utils__format('pagesize changed to:{0}', [val]));
+
+		if (parseInt(val) == priv.options.pageSize) {
+			return false;
+		}
+		priv.options.pageSize = parseInt(val);
+
+		publ.loadPage(1, priv.options.pageSize);
+
+		jQuery(_uiNodeCont).trigger('TableAjax:render', ['body', 'foot_pagination']);
+
+		priv.saveProfilePageSize(priv.options.pageSize);
+	};
+
+	/*
+	 when: clicking a column
+	 what: triggers table to be sorted by the column
+	 */
+	priv.columnClicked = function(e) {
+		e.preventDefault();
+		if (priv.options.debug) console.log(p5Utils__format('col:{0} clicked', [e.data.column]));
+
+		//set the new sorting column
+		if (_state.filters.currSortCol == e.data.column) _state.filters.currSortFlip = !_state.filters.currSortFlip;
+		_state.filters.currSortCol = e.data.column;
+		/// TODO: var state = {}; state.currSortCol = ''; state.currSortFlip = ''; priv.setState(state);
+
+		_headSort = undefined;
+		_body = undefined;
+		priv.sort();
+	};
+
+	priv.saveProfilePageSize = function(pageSize) {
+		var reqData = {};
+		reqData.pageSize = pageSize;
+
+		$.ajax({
+			data: reqData,
+			type: "POST",
+			dataType: 'json',
+			// async: true,
+			url: 'index-ajax.php?_zasobID=' + ZASOB_ID + '&_cls=' + CLASS_NAME + '&_hash=' + HTML_ID + '&_task=PAGE_SIZE_SAVE'
+		})
+		.done(function(data, textStatus, jqXHR){
+			if (data && data.type && data.type == 'info') {
+				jQuery.notify('Nie wprowadzono żadnych zmian', 'info');
+			} else {
+				jQuery.notify('Zapisano ustawienia', 'success');
+			}
+		})
+		.fail(function(){
+			jQuery.notify('Wystąpił błąd podczas zapisywania ustawień', 'error');
+		});
+	};
+
+	priv.saveHiddenCols = function() {
+		var reqData = {};
+
+		$.each(_data.cols, function(col, props) {
+			if (props.type != "unique" && col != 'unique' && col != _state.primaryKey) {
+				reqData[col] = (props.hidden)? 'HIDE' : 'SHOW';
+			}
+		});
+
+		$.ajax({
+			data: reqData,
+			type: "POST",
+			dataType: 'json',
+			// async: true,
+			url: 'index-ajax.php?_zasobID=' + ZASOB_ID + '&_cls=' + CLASS_NAME + '&_hash=' + HTML_ID + '&_task=HIDDEN_COLS_SAVE'
+		})
+		.done(function(data, textStatus, jqXHR){
+			if (data && data.type && data.type == 'info') {
+				jQuery.notify('Nie wprowadzono żadnych zmian', 'info');
+			} else {
+				jQuery.notify('Zapisano ustawienia', 'success');
+			}
+		})
+		.fail(function(){
+			jQuery.notify('Wystąpił błąd podczas zapisywania ustawień', 'error');
+		});
+	};
+
+	/*
+	 when: clicking a column in columnpicker
+	 what: triggers table to show/hide the column
+	 */
+	priv.columnPickerClicked = function(e) {
+		e.stopPropagation();
+
+		var column = $(this).val();
+
+		//toggle column visibility
+		priv.modelColFilter_toggleColumn(column);
+
+		priv.saveHiddenCols();
+
+		//_data.cols[column].index = new priv.ext.XDate();
+		jQuery(_uiNodeCont).trigger('TableAjax:render', ['head', 'body']);
+	};
+
+	priv.modelColFilter_init = function() {// run only once, set _state._modelColFilter {selected, filters: [ {name, label, visibleCols} ]}
+		var isAllSelected = true;
+		if (undefined === _state._modelColFilter) {
+			var columnFilters = [];
+			{
+				var fltrAll = {'name': 'all', 'label': 'Wszystkie', visibleCols: []};
+				$.each(_data.cols, function(col, props) {
+					if ("unique" == props.type) return;
+					fltrAll.visibleCols.push(col);
+					if (_data.cols[col].hidden) isAllSelected = false;
+				});
+				columnFilters.push(fltrAll);
+			}
+			// {
+			// 	var fltrMostUsed = {'name': 'most_used', 'label': 'Najczęściej używane', visibleCols: []};
+			// 	var fltrMostUsedLimit = 10;
+			// 	$.each(_data.cols, function(col, props) {
+			// 		if ("unique" == props.type) return;
+			// 		if (fltrMostUsedLimit-- > 0) {
+			// 			fltrMostUsed.visibleCols.push(col);
+			// 		}
+			// 	});
+			// 	columnFilters.push(fltrMostUsed);
+			// }
+
+			_state._modelColFilter = {
+				selected: (isAllSelected)? 'all' : null,
+				filters: columnFilters,
+				saveBtn: $('<button class="btn btn-xs btn-primary" disabled="disabled">Zapisz widoczne kolumny</button>')
+			};
+			_state._modelColFilter.saveBtn.on('click', priv.modelColFilter_saveBtnClicked)
+
+			if (priv.options.userTableFilterUrl) {
+				window.fetch(priv.options.userTableFilterUrl, {
+					method: 'POST',
+					headers: {
+						'Content-Type': 'application/json'
+					},
+					credentials: 'same-origin',// add cookies
+					body: JSON.stringify({
+						namespace: priv.options.namespace,
+					})
+				}).then(function (response) {
+					return response.json()
+				}).then(function (result) {
+					if ('success' == result.type) {
+						// p5UI__notifyAjaxCallback(result)
+						_state._modelColFilter.filters = _state._modelColFilter.filters.filter(function (filter) {
+							return ('all' === filter.name || 'most_used' == filter.name)
+						}).concat(Object.keys(result.data).map(function (fltr) {
+							return {
+								name: fltr,
+								label: fltr,
+								visibleCols: result.data[fltr].split(',')
+							}
+						}))
+						jQuery(_uiNodeCont).trigger('TableAjax:render', ['foot__columnPicker']);
+					} else {
+						p5UI__notifyAjaxCallback(result)
+					}
+				}).catch(function (e) {
+					// TODO: show error ("ajax response error: " + e)
+				});
+			}
+		}
+	};
+	priv.modelColFilter_onClickRemoveFilter = function (e) {
+		e.preventDefault()
+		e.stopPropagation()
+		var filtrName = $(this).data('col_filter')
+		if (!filtrName) return
+		window.fetch(URI_BASE + 'index.php?_route=ViewTableAjax&_task=rmUserTableFilterAjax', {
+			method: 'POST',
+			headers: {
+				'Content-Type': 'application/json'
+			},
+			credentials: 'same-origin',// add cookies
+			body: JSON.stringify({
+				namespace: NAMESPACE,
+				filtrName: filtrName,
+			})
+		}).then(function (response) {
+			return response.json()
+		}).then(function (result) {
+			if ('success' == result.type) {
+				p5UI__notifyAjaxCallback(result)
+				var userTableFilters = result.data;// resolve(result.data)
+				_state._modelColFilter.filters = _state._modelColFilter.filters.filter(function (filter) {
+					return ('all' === filter.name || 'most_used' == filter.name)
+				}).concat(Object.keys(userTableFilters).map(function (fltr) {
+					return {
+						name: fltr,
+						label: fltr,
+						visibleCols: userTableFilters[fltr].split(',')
+					}
+				}))
+				jQuery(_uiNodeCont).trigger('TableAjax:render', ['foot__columnPicker']);
+			} else {
+				p5UI__notifyAjaxCallback(result)
+			}
+		}).catch(function (e) {
+			p5UI__notifyAjaxCallback({ type: 'error', msg: new String(e) })
+		});
+	};
+	priv.modelColFilter_saveBtnClicked = function (e) {
+		swal({
+			title: 'Zapisz widoczne kolumny',
+			html: '',
+			animation: true,
+			input: 'text',
+			inputPlaceholder: 'nazwa filtra',
+			// inputValue: null,
+			// inputAttributes: {'step': '0.01'},
+			showCancelButton: true,
+			confirmButtonText: 'Zapisz',
+			showLoaderOnConfirm: true,
+			showCloseButton: true,
+			preConfirm: function(filtrName) {
+				return new Promise(function(resolve, reject) {
+					if (!filtrName) reject('Proszę podać nazwę filtra')
+					if (filtrName.length > 255) reject('Nazwa za długa')
+					window.fetch(URI_BASE + 'index.php?_route=ViewTableAjax&_task=addUserTableFilterAjax', {
+						method: 'POST',
+						headers: {
+							'Content-Type': 'application/json'
+						},
+						credentials: 'same-origin',// add cookies
+						body: JSON.stringify({
+							namespace: NAMESPACE,
+							filtrName: filtrName,
+							visibleCols: Object.keys(_data.cols).filter(function(col) {
+								return !_data.cols[col].hidden
+							}).join(','),
+						})
+					}).then(function (response) {
+						return response.text()
+					}).then(function (responseText) {
+						try {
+							// json = response.json() // BUG: error trafia do catch promisów, zamiast aktualnego bloku
+							// -- obiekt response pewnie jest powiązany z Promisem z fetch
+							// -- response.json() zwraca Promise tak samo jak response.text()
+							return JSON.parse(responseText)
+						} catch (e) {
+							throw responseText
+						}
+					}).then(function (result) {
+						if ('success' == result.type) {
+							p5UI__notifyAjaxCallback(result)
+							resolve(result.data)
+						} else {
+							p5UI__notifyAjaxCallback(result)
+							reject(result.msg || "Wystąpił błąd!")
+						}
+					}).catch(function (e) {
+						reject("ajax response error: " + e)
+					});
+		    })
+		  },
+		  allowOutsideClick: false
+		}).then(function(userTableFilters) {
+			_state._modelColFilter.filters = _state._modelColFilter.filters.filter(function (filter) {
+				return ('all' === filter.name || 'most_used' == filter.name)
+			}).concat(Object.keys(userTableFilters).map(function (fltr) {
+				return {
+					name: fltr,
+					label: fltr,
+					visibleCols: userTableFilters[fltr].split(',')
+				}
+			}))
+			jQuery(_uiNodeCont).trigger('TableAjax:render', ['head', 'body', 'foot__columnPicker']);
+		}).catch(function(e) {
+			// eg. hit Cancel
+		})
+	}
+	priv.modelColFilter_getSaveBtn = function () {
+		priv.modelColFilter_init();
+		return _state._modelColFilter.saveBtn
+	}
+	priv.modelColFilter_getFilters = function() {
+		priv.modelColFilter_init();
+		return _state._modelColFilter.filters;
+	};
+	priv.modelColFilter_getSelected = function() {
+		priv.modelColFilter_init();
+		return _state._modelColFilter.selected;
+	};
+	priv.modelColFilter_setFilter = function(filterKey) {
+		priv.modelColFilter_init();
+		_state._modelColFilter.selected = filterKey;
+
+		priv.modelColFilter_uiUncheckAllColFilters();
+		priv.modelColFilter_uiCheckColFilter(filterKey);
+	};
+	priv.modelColFilter_toggleColumn = function(column, value) {
+		priv.modelColFilter_init();
+		_state._modelColFilter.selected = null;
+		if (!_data.cols[column] || !_data.cols[column]) return;
+		_data.cols[column].hidden = (undefined !== value)? value : !_data.cols[column].hidden;
+
+		priv.modelColFilter_uiUncheckAllColFilters();
+		{
+			var isAllSelected = true;
+			$.each(_data.cols, function(col, props) {
+				if ("unique" == props.type) return;
+				if (_data.cols[col].hidden) isAllSelected = false;
+			});
+			if (isAllSelected) {
+				_state._modelColFilter.selected = 'all';
+				priv.modelColFilter_uiCheckColFilter('all');
+			}
+		}
+	};
+	priv.modelColFilter_uiUncheckAllColFilters = function() {
+		var nodeClass = 'tblAjax__' + 'footer__toolbar__columnPicker',
+				currentNode = $(_uiNodeCont).next('.foot').find('.' + nodeClass)
+		;
+		currentNode.find('input[type="radio"]').prop("checked", false);
+	};
+	priv.modelColFilter_uiCheckColFilter = function(filterKey) {
+		var nodeClass = 'tblAjax__' + 'footer__toolbar__columnPicker',
+				currentNode = $(_uiNodeCont).next('.foot').find('.' + nodeClass)
+		;
+		currentNode.find('input[type="radio"]').each(function(idx, input) {
+			var input$ = jQuery(input);
+			if (filterKey == jQuery(input$).parent().data('col_filter')) {
+				input$.prop("checked", true);
+			} else {
+				input$.prop("checked", false);
+			}
+		});
+	};
+
+	priv.columnPickerLinkClicked = function(e) {
+		e.stopPropagation();
+		var input$ = $(this).find('input'),
+				col_filter = $(this).data('col_filter'),
+				columnFilters = priv.modelColFilter_getFilters()
+		;
+
+		if (col_filter) {
+			var filter = null;
+			$.each(columnFilters, function(idx, colFltr) {
+				if (col_filter == colFltr.name) {
+					filter = colFltr;
+				}
+			});
+			if (!filter) return;
+			$.each(_data.cols, function(col, props) {
+				var isHidden = (-1 === filter.visibleCols.indexOf(col));
+				priv.modelColFilter_toggleColumn(col, isHidden);
+			});
+			priv.modelColFilter_setFilter(col_filter);
+			priv.saveHiddenCols();
+			jQuery(_uiNodeCont).trigger('TableAjax:render', ['head', 'body', 'foot__columnPicker']);
+		} else {
+			e.preventDefault();
+			if (input$.length != 1) return;
+			var column = input$.val();
+			if (!column) return;
+			//toggle column visibility
+			priv.modelColFilter_toggleColumn(column);
+			input$.prop("checked", !input$.is(':checked'));// update view
+			priv.saveHiddenCols();
+			//_data.cols[column].index = new priv.ext.XDate();
+			jQuery(_uiNodeCont).trigger('TableAjax:render', ['head', 'body']);
+		}
+	};
+
+	priv.columnHideClicked = function(e) {
+		e.stopPropagation();
+
+		var column;
+		if (e.data && e.data.column) {
+			column = e.data.column;
+		} else {
+			return;
+		}
+
+		//toggle column visibility
+		priv.modelColFilter_toggleColumn(column);
+
+		priv.saveHiddenCols();
+
+		//_data.cols[column].index = new priv.ext.XDate();
+		jQuery(_uiNodeCont).trigger('TableAjax:render', ['head', 'body', 'foot__columnPicker']);
+	};
+
+	/*
+	 when: clicking a row checkbox
+	 what: toggles checked state on row, and add/removes it from checked array
+	 */
+	priv.rowChecked = function(e) {
+		var elem = $(this);
+
+		//get the row's unique value
+		var unique = elem.closest('tr').data('unique');
+		if (priv.options.debug) console.log(p5Utils__format('row({0}) {1}', [unique, elem.is(':checked') ? 'checked' : 'unchecked']));
+
+		//store the row in checked array
+		if (elem.is(':checked')) _uniqueCols[unique] = priv.getRow(unique);
+		else delete _uniqueCols[unique];
+	};
+
+	/*
+	 when: clicking anywhere on a row
+	 what: row data and other info is returned to caller
+	 */
+	// priv.rowClicked = function(e) {// TODO: not used
+	// 	if (!_state.primaryKey) {
+	// 		if (priv.options.debug) console.log('no unique column specified');
+	// 		return;
+	// 	}
+	//
+	// 	//gather callback data
+	// 	var elem = $(this);
+	// 	var column = _data.cols[elem.data('column')];
+	// 	var unique = elem.closest('tr').data('unique');
+	// 	var row = priv.getRow(unique);
+	// 	var isChecked = elem.closest('tr').find('.unique').is(':checked');
+	//
+	// 	//trigger callback
+	// 	if (typeof priv.options.rowClicked == 'function') {
+	// 		priv.options.rowClicked.call(e.target, {
+	// 			event: e,
+	// 			row: row,
+	// 			column: column,
+	// 			checked: isChecked
+	// 		});
+	// 	}
+	// };
+
+	/**
+	 * Inline edit.
+	 */
+	priv.rowDblClicked = function(e) {
+		var inlineEditBox$Node = $(_uiNodeCont).parent().children('.tblAjax__inlineEditBox');
+
+		// hide popover for typespecial fld on click
+		if (_popoverCellCurrent) {
+			_popoverCellCurrent.popover('hide');
+		}
+
+		// e.clientX: 1002; e.clientY: 245
+		if ('id' in e.data && 'col' in e.data && e.data.id > 0) {
+			inlineEditBox$Node.modal();
+			inlineEditBox$Node.show();
+			inlineEditBox$Node.on('shown.bs.modal', function(e) {
+				var dialogBox = jQuery(this).find('.modal-dialog'),
+						modalBody = dialogBox.find('.modal-body'),
+						boudingRect = dialogBox.get(0).getBoundingClientRect();
+				dialogBox.css({position: 'absolute', margin:0, top: boudingRect.top, left: boudingRect.left});
+				modalBody.css({overflow: 'scroll'});
+				dialogBox.resizable({minHeight: 300, minWidth: 300, alsoResize: modalBody});
+				dialogBox.draggable({handle: '.modal-header'});
+			});
+			inlineEditBox$Node.on('hidden.bs.modal', function(e) {
+				var dialogBox = jQuery(this).find('.modal-dialog'),
+						modalBody = dialogBox.find('.modal-body');
+				dialogBox.removeAttr('style');
+				modalBody.removeAttr('style');
+				modalBody.css({padding: '0'});
+				dialogBox.resizable();
+				dialogBox.draggable();
+				dialogBox.resizable('destroy');
+				dialogBox.draggable('destroy');
+			});
+			inlineEditBox$Node.find('input[name=ID]').val(e.data.id);
+			inlineEditBox$Node.find('input[name=col]').val(e.data.col);
+			inlineEditBox$Node.find('.inlineEditBox-cnt').html('<span class="loading-info"> loading ...</span>');
+			$.ajax({
+				url: 'index-ajax.php?_zasobID=' + ZASOB_ID + '&_cls=' + CLASS_NAME + '&_hash=' + HTML_ID + '&_task=EDIT_INLINE&ID=' + e.data.id + '&col=' + e.data.col,
+				type: 'GET',
+				dataType: 'json',
+				success: function(data) {
+					if ('p5:www_link' == _.get(data, 'simpleType')) {
+						inlineEditBox$Node.find('.inlineEditBox-cnt').empty();
+						var label = jQuery('<label>');// for="' + data.htmlFieldName + '">');// "<label for="f22579" class="AjaxTableEdit-label"> ... </label>
+						label.attr('for', data.htmlFieldName);
+						label.attr('class', 'AjaxTableEdit-label');
+						inlineEditBox$Node.find('.inlineEditBox-cnt').append(label);
+						var inLabel = jQuery('<strong title="[' + data.idZasob + '] ' + data.fieldLabel + '">' + data.fieldLabel + '</strong>');
+						label.append(inLabel);
+						var frmItem = jQuery('<input>');
+						frmItem.attr('type', 'text');
+						frmItem.attr('name', data.htmlFieldName);
+						frmItem.attr('value', data.formItem.value);
+						frmItem.attr('maxlength', _.get(data, 'restrictions.maxLength', 255));
+						frmItem.attr('style', 'width:98%');
+						frmItem.attr('class', 'form-control');
+						frmItem.insertAfter(label);
+					} else {
+						var content = _.get(data, 'legacy_html', "Nieznany błąd");
+						inlineEditBox$Node.find('.inlineEditBox-cnt').html(content);
+					}
+					inlineEditBox$Node.find('.btn-save').show();
+
+					initDateTimePicker(inlineEditBox$Node);
+
+					inlineEditBox$Node.find('textarea').autosize();
+
+					var fld = inlineEditBox$Node.find('input[name^="f"]');
+					if (fld.length > 0 && !fld.hasClass('se_type-date')) {
+						fld.keydown(function(event) {
+							if (event.which == 13) {
+								event.preventDefault();
+								inlineEditBox$Node.find('form').submit();
+							}
+						});
+					}
+
+					(function setFocusAtFirstInlineEditFormFld(modal$node){
+						var fld, dbg = false;
+						if(dbg)console.log('focus...');
+						fld = modal$node.find('input[name^="f"]:first');
+						if (fld.length > 0 && !fld.hasClass('se_type-date')) {
+							if(dbg)console.log('set focus to first input element');
+							fld.focus(); return;
+						}
+						fld = modal$node.find('select[name^="f"]:first');
+						if (fld.length) {
+							if (fld.get(0).selectize) {
+								if(dbg)console.log('set focus to first typepecial element');
+							} else {
+								if(dbg)console.log('set focus to first select element');
+								fld.focus(); return;
+							}
+						}
+						if(dbg)console.log('set focus to close btn');
+						modal$node.find('.btn-close').focus();
+					})(inlineEditBox$Node);
+				},
+				error: function(err) {
+					if (priv.options.debug) console.log('err');
+				}
+			});
+		} else {
+			if (priv.options.debug) console.log('NO data');
+			return false;
+		}
+	};
+
+	priv.ajaxLoadTypeSpeciallCell = function(id, col) {
+		if (_popoverCellAjaxXhr) {
+			_popoverCellAjaxXhr.abort();
+		}
+
+		_popoverCellAjaxXhr = $.ajax({
+			type: 'GET',
+			url: 'index.php?_route=ViewTableAjax&_task=typeSpecialCell&namespace=' + NAMESPACE + '&ID=' + id + '&col=' + col,
+			dataType: 'json',
+			contentType: "application/json; charset=utf-8",
+			data: '',
+			success: function(req){
+				if (_.get(req, 'data.tbl_id') > 0) {
+					var addHtml = '';
+					for (var i in req.data.items) {
+						var url = 'index.php?_route=ViewTableAjax';
+						url += '&namespace=' + req.namespace;
+						url += '&f_' + req.data.fld_name + '=' + req.data.items[i].id;
+						url += '&_hash=' + Math.random().toString(36).substring(2);
+						addHtml += '<a href="' + url + '">Wyszukaj ' + req.data.items[i].id + '</a>: ' + req.data.items[i].label;
+						addHtml += '<br>';
+					}
+					//_popoverCell.append(addHtml);// cache
+					if (_popoverCellCurrent) {
+						var popoverNodeId = _popoverCellCurrent.attr('aria-describedby');
+						if (popoverNodeId) {
+							jQuery('#' + popoverNodeId).find('.popoverCellContent').append(addHtml);
+						}
+					}
+				}
+			}
+		});
+
+	};
+
+	priv.popoverCellTypeSpecial = function(e) {
+		e.preventDefault();
+		e.stopPropagation();
+		if ('id' in e.data && 'col' in e.data && e.data.id > 0) {
+			var lastId = _popoverCell.data('rowid'),
+					lastCol = _popoverCell.data('col'),
+					rowPK = e.data.id,
+					colName = e.data.col
+			;
+
+			if (lastId == rowPK && lastCol == colName) {
+				//_popoverCellCurrent.popover('toggle');
+			}
+			else {
+				if (_popoverCellCurrent) {
+					_popoverCellCurrent.popover('destroy');
+				}
+
+				_popoverCell.data('rowid', rowPK);
+				_popoverCell.data('col', colName);
+				_popoverCell.html(e.data.value + '<div class="popoverCellContent loading" style="white-space:normal"></div>');
+
+				_popoverCellCurrent = jQuery(e.currentTarget);
+				// title : '<span class="text-info"><strong>title</strong></span> <button type="button" id="close" class="close">&times;</button>'
+				var opts = {
+					placement: 'left'
+					, trigger: 'click'
+				//	, title: e.data.col + '<a href="#" class="glyphicon glyphicon-remove pull-right" onclick="return hidePopover();"></a>'
+					, title: '<div style="display:block;position:relative;padding:0 20px 0 0;">' + (e.data.friendly || colName) + ' <button type="button" class="close" onclick="return hidePopover();" style="position:absolute;right:0;top:0;">&times;</button>' + '</div>'
+					, html: true
+					, content: _popoverCell.html()
+				}
+
+				_popoverCellCurrent.popover(opts);
+				if (_data.cols[colName]) {
+					if (_data.cols[colName]._tsRetId > 0) {
+						_popoverCellCurrent.on('shown.bs.popover', function() {
+							priv.ajaxLoadTypeSpeciallCell(rowPK, colName);
+						});
+					}
+				}
+				_popoverCellCurrent.popover('show');
+			}
+		} else {
+			if (priv.options.debug) console.log('NO data');
+			return false;
+		}
+		return;
+	};
+
+	priv.routeChanged = function(e) {
+		hash = e.data.hash || '';
+		if (hash.length == 0) {
+			return;
+		}
+		if (hash.substring(0, 1) != '#') {
+			hash = '#' + hash;
+		}
+		location.hash = hash;
+	};
+
+	priv.refresh = function(e) {
+		e.preventDefault();
+		publ.loadPage(_state.page);
+	};
+
+	publ.init = function(options) {
+		if (priv.options.debug) console.log('TableAjax initialization...');
+		//merge supplied options with defaults
+		$.extend(priv.options, defaults, options);
+		priv.init();
+		return publ;
+	};
+
+	publ.refresh = function() {
+		publ.loadPage(_state.page);
+	};
+
+	publ.loadPage = function(page, pageSize) {
+		priv.loadPage(page, pageSize);
+	}
+	priv.loadPage = function(page, pageSize) {
+		var resetChecked = false;
+		var reqData = {
+			page: page,
+			pageSize: (pageSize || priv.options.pageSize),
+			currSortCol: (_state.filters.currSortCol || ''),
+			currSortFlip: _state.filters.currSortFlip ? "desc" : "asc"
+		};
+		var urlAdd = '';
+		urlAdd += '&page=' + page;
+		urlAdd += '&pageSize=' + (pageSize || priv.options.pageSize);
+		urlAdd += '&currSortCol=' + (_state.filters.currSortCol || '');
+		urlAdd += '&currSortFlip=' + (_state.filters.currSortFlip ? "desc" : "asc");
+
+		if (Object.keys(_state.filters.filterCols).length > 0) {
+			$.each(_state.filters.filterCols, function(col, colProps) {
+				if (colProps.filter && colProps.filter.length > 0) {
+					urlAdd += '&f_' + col + '=' + encodeURIComponent(colProps.filter);
+				}
+			});
+		}
+
+		// specialFilters
+		$.each(_state.specialFilters, function(groupName, btnValue) {
+			if (btnValue.length > 0) {
+				urlAdd += '&sf_' + groupName + '=' + encodeURIComponent(btnValue);
+			}
+		});
+
+		if (priv.options.forceFilterInit) {
+			$.map(priv.options.forceFilterInit, function(fltrProps, fltr) {
+				urlAdd += '&f_' + fltr + '=' + encodeURIComponent(fltrProps);
+				filtersInitSet = true;
+			});
+		}
+
+		_uiNode$Table.parent().parent().addClass('AjaxTable-loading');
+
+		// p5UI__notifyAjaxCallback({type: 'info', msg: 'pobieranie danych...'});
+		window.fetch(priv.options.url + urlAdd, {
+		  method: priv.options.urlPost ? 'POST' : 'GET',
+			credentials: 'same-origin',// add cookies
+		}).then(function (response) {
+			return response.json()
+		}).then(function (data) {
+			if (priv.options.debug) console.log('loadDataAjax:fetch:loadPage: request finished, data:', data);
+			// p5UI__notifyAjaxCallback(data);
+			if ('success' == data.type) {
+				state = {data: {}};
+				state.data.cols = data.cols || {};
+				state.data.rows = data.rows || [];
+				state.data.total = data.total || 0;
+				state.data.primaryKey = data.primaryKey || 'ID';
+				state.page = data.page || 0;
+				state.pageSize = data.pageSize || priv.options.pageSize;
+				state.filters = data.filters || {};
+				priv.setState(state);
+				_uiNode$Table.parent().parent().removeClass('AjaxTable-loading');
+			} else if ('error' == data.type) {
+				p5UI__notifyAjaxCallback(data);
+			} else {
+				p5UI__notifyAjaxCallback(data);
+			}
+		}).catch(function (e) {
+			console.log('loadDataAjax:fetch: ERR:', e);
+		});
+	};
+
+	publ.getCurrentPage = function() {
+		return _state.page;
+	};
+
+	publ.popoverCellRemove = function() {
+		if (_popoverCellCurrent) {
+			_popoverCellCurrent.popover('destroy');
+		}
+		_popoverCell.data('rowid', -1);
+		_popoverCell.data('col', -1);
+		_popoverCell.html('');
+	};
+
+	return publ;
+}
+
+$.fn.TableAjax = function(options) {
+	options = options || {};
+	return this.each(function() {
+		options.id = this;
+		$(this).data('TableAjax', new TableAjax().init(options));
+	});
+};
+
+$.fn.TableAjaxLoadPage = function(page, pageSize) {
+	return this.each(function() {
+		var tblAjax = jQuery(this).data('TableAjax');
+		var curPage = page || tblAjax.getCurrentPage();
+		if (tblAjax) tblAjax.loadPage(curPage, pageSize);
+	});
+};
+
+$.fn.TableAjaxRefresh = function(page, pageSize) {
+	return this.each(function() {
+		var tblAjax = jQuery(this).data('TableAjax');
+		if (tblAjax) tblAjax.refresh();
+	});
+};