/**
 * @license
 * webix UI v.6.3.1
 * This software is allowed to use under GPL or you need to obtain Commercial License
 * to use it in non-GPL project. Please contact sales@webix.com for details
 */

(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
	typeof define === 'function' && define.amd ? define(['exports'], factory) :
	(factory((global.webix = {})));
}(this, (function (exports) { 'use strict';

	var state = {
	  codebase: "./",
	  cdn: "//cdn.webix.com",
	  top_views: [],
	  _global_scope: null,
	  _global_collection: null,
	  _child_sizing_active: null,
	  _responsive_exception: null,
	  _responsive_tinkery: null,
	  _freeze_resize: null,
	  _parent_cell: null,
	  _focus_time: null,
	  _ui_creation: 0,
	  _edit_open_time: null,
	  _final_destruction: null,
	  _events: [],
	  destructors: [],
	  securityKey: "",
	  _noselect_element: null,
	  _modality: 0,
	  _wait_animate: null,
	  noanimate: false
	};

	var env = {};
	env.cdn = "//cdn.webix.com/";
	env.codebase = "";
	env.zIndexBase = 100;
	env.scrollSize = 17;
	env.strict = !!window.webix_strict;
	env.https = document.location.protocol === "https:";
	var agent = navigator.userAgent;
	env.isMac = agent.indexOf("Mac") != -1;
	if (agent.indexOf("Mobile") != -1 || agent.indexOf("Windows Phone") != -1) env.mobile = true;
	if (env.mobile || agent.indexOf("iPad") != -1 || agent.indexOf("Android") != -1) env.touch = true;
	if (agent.indexOf("Opera") != -1) env.isOpera = true;else {
	  //very rough detection, but it is enough for current goals
	  env.isIE = !!document.all || agent.indexOf("Trident") !== -1;

	  if (env.isIE) {
	    var version = parseFloat(navigator.appVersion.split("MSIE")[1]);
	    if (version == 8) env.isIE8 = true;
	  }

	  env.isEdge = agent.indexOf("Edge") != -1;
	  env.isFF = agent.indexOf("Firefox") != -1;
	  env.isWebKit = agent.indexOf("KHTML") != -1;
	  env.isSafari = env.isWebKit && env.isMac && agent.indexOf("Chrome") == -1; //maximum height/width for HTML elements in pixels (rough), bigger values will be ignored by browser

	  if (env.isIE || env.isEdge || env.isFF) env.maxHTMLElementSize = 10000000;
	  if (env.isSafari) env.maxHTMLElementSize = 100000000;
	}

	if (agent.toLowerCase().indexOf("android") != -1) {
	  env.isAndroid = true;

	  if (agent.toLowerCase().indexOf("trident") != -1) {
	    env.isAndroid = false;
	    env.isIEMobile = true;
	  }
	}

	env.transform = false;
	env.transition = false;
	var found_index = -1;
	var js_list = ["", "webkit", "Moz", "O", "ms"];
	var css_list = ["", "-webkit-", "-Moz-", "-o-", "-ms-"];
	var d = document.createElement("DIV");

	for (var j = 0; j < js_list.length; j++) {
	  var name = js_list[j] ? js_list[j] + "Transform" : "transform";

	  if (typeof d.style[name] != "undefined") {
	    found_index = j;
	    break;
	  }
	}

	if (found_index > -1) {
	  env.cssPrefix = css_list[found_index];
	  var jp = env.jsPrefix = js_list[found_index];
	  env.transform = jp ? jp + "Transform" : "transform";
	  env.transition = jp ? jp + "Transition" : "transition";
	  env.transitionDuration = jp ? jp + "TransitionDuration" : "transitionDuration";
	  d.style[env.transform] = "translate3d(0,0,0)";
	  env.translate = d.style[env.transform] ? "translate3d" : "translate";
	  env.transitionEnd = env.cssPrefix == "-Moz-" ? "transitionend" : jp ? jp + "TransitionEnd" : "transitionend";
	}

	env.pointerevents = !env.isIE || new RegExp("Trident/.*rv:11").exec(agent) !== null; //touch events that can be prevented

	env.passiveEventListeners = false;

	try {
	  var opts = Object.defineProperty({}, "passive", {
	    get: function () {
	      // eslint-disable-line
	      env.passiveEventListeners = true;
	    }
	  });
	  window.addEventListener("testPassive", null, opts);
	  window.removeEventListener("testPassive", null, opts);
	} catch (e) {} // eslint-disable-line


	env.svg = function () {
	  return document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1");
	}();

	env.svganimation = function () {
	  return document.implementation.hasFeature("https://www.w3.org/TR/SVG11/feature#SVG-animation", "1.1");
	}();

	function _typeof(obj) {
	  if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
	    _typeof = function (obj) {
	      return typeof obj;
	    };
	  } else {
	    _typeof = function (obj) {
	      return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
	    };
	  }

	  return _typeof(obj);
	}

	// resolves circular dependencies
	// quick solution, must be removed in the next versions
	var services = {};
	function define(name, value) {
	  services[name] = value;
	}
	function use(name) {
	  return services[name];
	}

	function assert(test, msg) {
	  if (!test) {
	    log("error", msg);
	    var message = use("message");
	    if (message && typeof msg == "string") message({
	      type: "debug",
	      text: msg,
	      expire: -1
	    });
	    debugger; // eslint-disable-line
	  }
	} //show log message

	function log(type, message, details) {
	  if (arguments.length == 1) {
	    message = type;
	    type = "log";
	  }

	  if (window.console && window.console.log) {
	    type = type.toLowerCase();
	    if (window.console[type]) window.console[type](message || "unknown error");else window.console.log(type + ": " + message);
	    if (details) window.console.log(details);
	  }
	}
	var debug_mode = {};
	function debug(mode) {
	  if (!mode) debug_mode = {};else if (_typeof(mode) !== "object") debug_mode = {
	    events: true
	  };else for (var key in mode) {
	    debug_mode[key] = mode[key];
	  }
	}
	var debug_size_indent = 0;

	function debug_size_step() {
	  var str = "";

	  for (var i = 0; i < debug_size_indent; i++) {
	    str += "|  ";
	  }

	  return str;
	}

	function debug_size_box_start(comp, get) {
	  if (!debug_mode.size) return;
	  if (!debug_size_indent) log(get ? "--- get sizes ---" : "--- set sizes ---");
	  log(debug_size_step() + comp.name + "@" + comp.config.id);
	  debug_size_indent++;
	}
	function debug_size_box_end(comp, sizes) {
	  if (!debug_mode.size) return;
	  debug_size_indent--;
	  log(debug_size_step() + sizes.join(","));
	}
	function debug_size_box(comp, sizes, get) {
	  if (!debug_mode.size) return;
	  if (!debug_size_indent) log(get ? "--- get sizes ---" : "--- set sizes ---");
	  log(debug_size_step() + comp.name + "@" + comp.config.id + " " + sizes.join(","));
	}

	var level = 0;
	function level_in() {
	  level++;
	  assert(level !== 100, "Attempt to copy object with self reference");
	}
	function level_out() {
	  level--;
	} //coding helpers

	function clone(source) {
	  var f = clone._function;
	  f.prototype = source;
	  return new f();
	}

	clone._function = function () {}; //copies methods and properties from source to the target


	exports.extend = function (base, source, force) {
	  assert(base, "Invalid mixing target");
	  assert(source, "Invalid mixing source");

	  if (base.$protoWait) {
	    PowerArray.insertAt.call(base.$protoWait, source, 1);
	    return base;
	  } //copy methods, overwrite existing ones in case of conflict


	  for (var method in source) {
	    if (!(method in base) || force) base[method] = source[method];
	  } //in case of defaults - preffer top one


	  if (source.defaults) exports.extend(base.defaults, source.defaults); //if source object has init code - call init against target

	  if (source.$init) source.$init.call(base);
	  return base;
	}; //copies methods and properties from source to the target from all levels


	function copy(source) {
	  assert(source, "Invalid mixing target");
	  level_in();
	  var esModern = !!window.Map && !!window.Set && !!window.WeakMap && !!window.WeakSet;
	  var target;

	  if (arguments.length > 1) {
	    target = arguments[0];
	    source = arguments[1];
	  } else target = isArray(source) ? [] : {};

	  for (var method in source) {
	    var from = source[method];

	    if (from && _typeof(from) == "object" && !(from instanceof RegExp)) {
	      if (isDate(from)) target[method] = new Date(from);
	      /* jshint ignore:start */
	      else if (esModern && (from instanceof Map || from instanceof Set || from instanceof WeakMap || from instanceof WeakSet)) target[method] = from;
	        /* jshint ignore:end */
	        else {
	            target[method] = isArray(from) ? [] : {};
	            copy(target[method], from);
	          }
	    } else {
	      target[method] = from;
	    }
	  }

	  level_out();
	  return target;
	}
	function single(source) {
	  var instance = null;

	  var t = function () {
	    if (!instance) instance = new source({});
	    if (instance._reinit) instance._reinit.apply(instance, arguments);
	    return instance;
	  };

	  return t;
	} //creates function with specified "this" pointer

	function bind(functor, object) {
	  return function () {
	    return functor.apply(object, arguments);
	  };
	} //evaluate javascript code in the global scoope

	function exec(code) {
	  if (window.execScript) //special handling for IE
	    window.execScript(code);else window.eval(code);
	}
	function wrap(code, wrap) {
	  if (!code) return wrap;
	  return function () {
	    var result = code.apply(this, arguments);
	    wrap.apply(this, arguments);
	    return result;
	  };
	} //check === undefined

	function isUndefined(a) {
	  return typeof a == "undefined";
	} //delay call to after-render time

	function delay(method, obj, params, delay) {
	  return window.setTimeout(function () {
	    if (!(obj && obj.$destructed)) {
	      var ret = method.apply(obj, params || []);
	      method = obj = params = null;
	      return ret;
	    }
	  }, delay || 1);
	}
	function once(method) {
	  var flag = true;
	  return function () {
	    if (flag) {
	      flag = false;
	      method.apply(this, arguments);
	    }
	  };
	} //common helpers
	//generates unique ID (unique per window, nog GUID)

	var _seed = new Date().valueOf();

	function uid() {
	  _seed++;
	  return _seed;
	} //resolve ID as html object

	function toNode(node) {
	  if (typeof node == "string") return document.getElementById(node);
	  return node;
	} //adds extra methods for the array

	function toArray(array) {
	  return exports.extend(array || [], PowerArray, true);
	} //resolve function name

	function toFunctor(str, scope) {
	  if (typeof str == "string") {
	    var method = str.replace("()", "");
	    if (scope && scope[method]) return scope[method];
	    return window[method] || window.eval(str);
	  }

	  return str;
	}
	/*checks where an object is instance of Array*/

	function isArray(obj) {
	  return Array.isArray ? Array.isArray(obj) : Object.prototype.toString.call(obj) === "[object Array]";
	}
	function isDate(obj) {
	  return obj instanceof Date;
	} //can be used by toArray()

	var PowerArray = {
	  //remove element at specified position
	  removeAt: function (pos, len) {
	    if (pos >= 0) this.splice(pos, len || 1);
	  },
	  //find element in collection and remove it 
	  remove: function (value) {
	    this.removeAt(this.find(value));
	  },
	  //add element to collection at specific position
	  insertAt: function (data, pos) {
	    if (!pos && pos !== 0) //add to the end by default
	      this.push(data);else {
	      this.splice(pos, 0, data);
	    }
	  },
	  //return index of element, -1 if it doesn't exists
	  find: function (data) {
	    for (var i = 0; i < this.length; i++) {
	      if (data == this[i]) return i;
	    }

	    return -1;
	  },
	  //execute some method for each element of array
	  each: function (functor, master) {
	    for (var i = 0; i < this.length; i++) {
	      functor.call(master || this, this[i]);
	    }
	  },
	  //create new array from source, by using results of functor 
	  map: function (functor, master) {
	    for (var i = 0; i < this.length; i++) {
	      this[i] = functor.call(master || this, this[i]);
	    }

	    return this;
	  },
	  filter: function (functor, master) {
	    for (var i = 0; i < this.length; i++) {
	      if (!functor.call(master || this, this[i])) {
	        this.splice(i, 1);
	        i--;
	      }
	    }

	    return this;
	  }
	}; //hook for documentation generator

	{
	  if (window.webix_on_core_ready) {
	    exports.extend = window.webix_on_core_ready({
	      extend: exports.extend
	    }).extend;
	  }
	}

	var EventSystem = {
	  $init: function () {
	    if (!this._evs_events) {
	      this._evs_events = {}; //hash of event handlers, name => handler

	      this._evs_handlers = {}; //hash of event handlers, ID => handler

	      this._evs_map = {};
	    }
	  },
	  //temporary block event triggering
	  blockEvent: function () {
	    this._evs_events._block = true;
	  },
	  //re-enable event triggering
	  unblockEvent: function () {
	    this._evs_events._block = false;
	  },
	  mapEvent: function (map) {
	    exports.extend(this._evs_map, map, true);
	  },
	  on_setter: function (config) {
	    if (config) {
	      for (var i in config) {
	        var method = toFunctor(config[i], this.$scope);
	        var sub = i.indexOf("->");

	        if (sub !== -1) {
	          this[i.substr(0, sub)].attachEvent(i.substr(sub + 2), bind(method, this));
	        } else this.attachEvent(i, method);
	      }
	    }
	  },
	  //trigger event
	  callEvent: function (type, params) {
	    var master = this._event_master || this;
	    if (this._evs_events._block) return true;
	    type = type.toLowerCase();

	    var event_stack = this._evs_events[type.toLowerCase()]; //all events for provided name


	    var return_value = true;
	    if ((debug_mode.events || this.debug) && type !== "onmousemoving") //can slowdown a lot
	      log("info", "[" + this.name + "@" + (this._settings || {}).id + "] event:" + type, params);
	    if (event_stack) for (var i = 0; i < event_stack.length; i++) {
	      /*
	      	Call events one by one
	      	If any event return false - result of whole event will be false
	      	Handlers which are not returning anything - counted as positive
	      */
	      if (event_stack[i].apply(master, params || []) === false) return_value = false;
	    }

	    if (this._evs_map[type]) {
	      var target = this._evs_map[type];
	      target.$eventSource = this;
	      if (!target.callEvent(type, params)) return_value = false;
	      target.$eventSource = null;
	    }

	    return return_value;
	  },
	  //assign handler for some named event
	  attachEvent: function (type, functor, id) {
	    assert(functor, "Invalid event handler for " + type);
	    type = type.toLowerCase();
	    id = id || uid(); //ID can be used for detachEvent

	    functor = toFunctor(functor, this.$scope); //functor can be a name of method

	    var event_stack = this._evs_events[type] || toArray(); //save new event handler

	    if (arguments[3]) event_stack.unshift(functor);else event_stack.push(functor);
	    this._evs_events[type] = event_stack;
	    this._evs_handlers[id] = {
	      f: functor,
	      t: type
	    };
	    return id;
	  },
	  //remove event handler
	  detachEvent: function (id) {
	    if (!this._evs_handlers[id]) {
	      var name = (id + "").toLowerCase();

	      if (this._evs_events[name]) {
	        this._evs_events[name] = toArray();
	      }

	      return;
	    }

	    var type = this._evs_handlers[id].t;
	    var functor = this._evs_handlers[id].f; //remove from all collections

	    var event_stack = this._evs_events[type];
	    event_stack.remove(functor);
	    delete this._evs_handlers[id];
	  },
	  hasEvent: function (type) {
	    type = type.toLowerCase();
	    var stack = this._evs_events[type];
	    if (stack && stack.length) return true;
	    var parent = this._evs_map[type];
	    if (parent) return parent.hasEvent(type);
	    return false;
	  }
	};

	var evs = {};
	exports.extend(evs, EventSystem, true);
	var callEvent = function (a, b) {
	  return evs.callEvent(a, b);
	};
	var attachEvent = function (a, b, c, d) {
	  return evs.attachEvent(a, b, c, d);
	};
	var detachEvent = function (a) {
	  return evs.detachEvent(a);
	};
	var blockEvent = function () {
	  return evs.blockEvent();
	};
	var unblockEvent = function () {
	  return evs.unblockEvent();
	};
	var mapEvent = function (map) {
	  return evs.mapEvent(map);
	};
	var hasEvent = function (type) {
	  return evs.hasEvent(type);
	};

	var _native_on_selectstart = 0;
	var _style_element = {};
	var _style_cache = {};
	function denySelect() {
	  if (!_native_on_selectstart) _native_on_selectstart = document.onselectstart;
	  document.onselectstart = stopEvent;
	}
	function allowSelect() {
	  if (_native_on_selectstart !== 0) {
	    document.onselectstart = _native_on_selectstart || null;
	  }

	  _native_on_selectstart = 0;
	}
	function index(node) {
	  var k = 0; //must be =, it is not a comparation!

	  while (node = node.previousSibling) {
	    k++;
	  }

	  return k;
	}
	function createCss(rule, sufix) {
	  var text = "";
	  sufix = sufix || "";

	  for (var key in rule) {
	    text += key + ":" + rule[key] + ";";
	  }

	  var name = _style_cache[text + sufix];

	  if (!name) {
	    name = "s" + uid();
	    addStyle("." + name + (sufix || "") + "{" + text + "}");
	    _style_cache[text + sufix] = name;
	  }

	  return name;
	}
	function addStyle(rule, group) {
	  var style = group ? _style_element[group] : _style_element["default"];

	  if (!style) {
	    style = document.createElement("style");
	    style.setAttribute("type", "text/css");
	    style.setAttribute("media", "screen,print");
	    document.getElementsByTagName("head")[0].appendChild(style);
	    if (group) _style_element[group] = style;else _style_element["default"] = style;
	  }
	  /*IE8*/


	  if (style.styleSheet) style.styleSheet.cssText += rule;else style.appendChild(document.createTextNode(rule));
	}
	function removeStyle(group) {
	  var box = _style_element[group || "default"];
	  if (box) box.innerHTML = "";
	}
	function create(name, attrs, html) {
	  attrs = attrs || {};
	  var node = document.createElement(name);

	  for (var attr_name in attrs) {
	    node.setAttribute(attr_name, attrs[attr_name]);
	  }

	  if (attrs.style) node.style.cssText = attrs.style;
	  if (attrs["class"]) node.className = attrs["class"];
	  if (html) node.innerHTML = html;
	  return node;
	} //return node value, different logic for different html elements

	function getValue(node) {
	  node = toNode(node);
	  if (!node) return "";
	  return isUndefined(node.value) ? node.innerHTML : node.value;
	} //remove html node, can process an array of nodes at once

	function remove(node) {
	  if (node instanceof Array) for (var i = 0; i < node.length; i++) {
	    remove(node[i]);
	  } else if (node && node.parentNode) node.parentNode.removeChild(node);
	} //insert new node before sibling, or at the end if sibling doesn't exist

	function insertBefore(node, before, rescue) {
	  if (!node) return;
	  if (before && before.parentNode) before.parentNode.insertBefore(node, before);else rescue.appendChild(node);
	} //return custom ID from html element 
	//will check all parents starting from event's target

	function locate(e, id) {
	  var trg;
	  if (e.tagName) trg = e;else {
	    e = e || event;
	    trg = e.target || e.srcElement;
	  }

	  while (trg) {
	    if (trg.getAttribute) {
	      //text nodes has not getAttribute
	      var test = trg.getAttribute(id);
	      if (test) return test;
	    }

	    trg = trg.parentNode;
	  }

	  return null;
	} //returns position of html element on the page

	function offset(elem) {
	  if (elem.getBoundingClientRect) {
	    //HTML5 method
	    var box = elem.getBoundingClientRect();
	    var body = document.body;
	    var docElem = document.documentElement;
	    var scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop;
	    var scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft;
	    var clientTop = docElem.clientTop || body.clientTop || 0;
	    var clientLeft = docElem.clientLeft || body.clientLeft || 0;
	    var top = box.top + scrollTop - clientTop;
	    var left = box.left + scrollLeft - clientLeft;
	    return {
	      y: Math.round(top),
	      x: Math.round(left),
	      width: elem.offsetWidth,
	      height: elem.offsetHeight
	    };
	  } else {
	    //fallback to naive approach
	    var _top = 0,
	        _left = 0;

	    while (elem) {
	      _top = _top + parseInt(elem.offsetTop, 10);
	      _left = _left + parseInt(elem.offsetLeft, 10);
	      elem = elem.offsetParent;
	    }

	    return {
	      y: _top,
	      x: _left,
	      width: elem.offsetHeight,
	      height: elem.offsetWidth
	    };
	  }
	} //returns relative position of event

	function posRelative(ev) {
	  ev = ev || event;
	  if (!isUndefined(ev.offsetX)) return {
	    x: ev.offsetX,
	    y: ev.offsetY
	  }; //ie, webkit
	  else return {
	      x: ev.layerX,
	      y: ev.layerY
	    }; //firefox
	} //returns position of event

	function pos(ev) {
	  ev = ev || event;
	  if (ev.touches && ev.touches[0]) ev = ev.touches[0];
	  if (ev.pageX || ev.pageY) //FF, KHTML
	    return {
	      x: ev.pageX,
	      y: ev.pageY
	    }; //IE

	  var d = env.isIE && document.compatMode != "BackCompat" ? document.documentElement : document.body;
	  return {
	    x: ev.clientX + d.scrollLeft - d.clientLeft,
	    y: ev.clientY + d.scrollTop - d.clientTop
	  };
	} //prevent event action

	function preventEvent(e) {
	  if (e && e.preventDefault) e.preventDefault();
	  if (e) e.returnValue = false;
	  return stopEvent(e);
	} //stop event bubbling

	function stopEvent(e) {
	  e = e || event;
	  if (e.stopPropagation) e.stopPropagation();
	  e.cancelBubble = true;
	  return false;
	}
	function triggerEvent(node, type, name) {
	  if (document.createEventObject) {
	    var ev = document.createEventObject();
	    if (node.fireEvent) node.fireEvent("on" + name, ev);
	  } else {
	    var _ev = document.createEvent(type);

	    _ev.initEvent(name, true, true);

	    if (node.dispatchEvent) node.dispatchEvent(_ev);
	  }
	} //add css class to the node

	function addCss(node, name, check) {
	  if (!check || node.className.indexOf(name) === -1) node.className += " " + name;
	} //remove css class from the node

	function removeCss(node, name) {
	  node.className = node.className.replace(RegExp(" " + name, "g"), "");
	}
	function getTextSize(text, css, basewidth) {
	  var d = create("DIV", {
	    "class": "webix_view webix_measure_size " + (css || "")
	  }, "");
	  d.style.cssText = "height:auto;visibility:hidden; position:absolute; top:0px; left:0px; overflow:hidden;" + (basewidth ? "width:" + basewidth + "px;" : "width:auto;white-space:nowrap;");
	  document.body.appendChild(d);
	  var all = _typeof(text) !== "object" ? [text] : text;
	  var width = 0;
	  var height = 0;

	  for (var i = 0; i < all.length; i++) {
	    d.innerHTML = all[i];
	    width = Math.max(width, d.offsetWidth);
	    height = Math.max(height, d.offsetHeight);
	  }

	  remove(d);
	  return {
	    width: width,
	    height: height
	  };
	}
	function download(data, filename) {
	  var objUrl = false;

	  if (_typeof(data) == "object") {
	    //blob
	    if (window.navigator.msSaveBlob) return window.navigator.msSaveBlob(data, filename);else {
	      data = window.URL.createObjectURL(data);
	      objUrl = true;
	    }
	  } //data url or blob url


	  var link = document.createElement("a");
	  link.href = data;
	  link.download = filename;
	  document.body.appendChild(link);
	  link.click();
	  delay(function () {
	    if (objUrl) window.URL.revokeObjectURL(data);
	    document.body.removeChild(link);
	    link.remove();
	  });
	}
	function _getClassName(node) {
	  if (!node) return "";
	  var className = node.className || "";
	  if (className.baseVal) //'className' exist but not a string - IE svg element in DOM
	    className = className.baseVal;
	  if (!className.indexOf) className = "";
	  return className;
	}
	function setSelectionRange(node, start, end) {
	  start = start || 0;
	  end = end || start;
	  node.focus();
	  if (node.setSelectionRange) node.setSelectionRange(start, end);else {
	    //ie8
	    var textRange = node.createTextRange();
	    textRange.collapse(true);
	    textRange.moveEnd("character", end);
	    textRange.moveStart("character", start);
	    textRange.select();
	  }
	}
	function getSelectionRange(node) {
	  if ("selectionStart" in node) return {
	    start: node.selectionStart || 0,
	    end: node.selectionEnd || 0
	  };else {
	    //ie8
	    node.focus();
	    var selection = document.selection.createRange();
	    var bookmark = selection.getBookmark();
	    var textRange = node.createTextRange();
	    textRange.moveToBookmark(bookmark);
	    var length = textRange.text.length;
	    textRange.collapse(true);
	    textRange.moveStart("character", -node.value.length);
	    var start = textRange.text.length;
	    return {
	      start: start,
	      end: start + length
	    };
	  }
	}
	function addMeta(name, value) {
	  document.getElementsByTagName("head").item(0).appendChild(create("meta", {
	    name: name,
	    content: value
	  }));
	}

	var i18n = {
	  parseFormat: "%Y-%m-%d %H:%i:%s",
	  parseTimeFormat: "%H:%i:%s"
	};

	function stringify(obj) {
	  var origin = Date.prototype.toJSON;

	  Date.prototype.toJSON = function () {
	    return i18n.parseFormatStr(this);
	  };

	  var result;
	  if (obj instanceof Date) result = obj.toJSON();else result = JSON.stringify(obj);
	  Date.prototype.toJSON = origin;
	  return result;
	}

	var global = window;
	var queueId = 1;
	var queue = {};
	var isRunningTask = false;
	if (!global.setImmediate && global.addEventListener) global.addEventListener("message", function (e) {
	  if (e.source == global) {
	    if (isRunningTask) nextTick(queue[e.data]);else {
	      isRunningTask = true;

	      try {
	        queue[e.data]();
	      } catch (e) {// eslint-disable-line
	      }

	      delete queue[e.data];
	      isRunningTask = false;
	    }
	  }
	});

	function nextTick(fn) {
	  if (global.setImmediate) global.setImmediate(fn); // if inside of web worker
	  else if (global.importScripts || !global.addEventListener) setTimeout(fn);else {
	      queueId++;
	      queue[queueId] = fn;
	      global.postMessage(queueId, "*");
	    }
	}

	Deferred.resolve = function (value) {
	  if (!(this._d == 1)) throw TypeError();
	  if (value instanceof Deferred) return value;
	  return new Deferred(function (resolve) {
	    resolve(value);
	  });
	};

	Deferred.reject = function (value) {
	  if (!(this._d == 1)) throw TypeError();
	  return new Deferred(function (resolve, reject) {
	    reject(value);
	  });
	};

	Deferred.all = function (arr) {
	  if (!(this._d == 1)) throw TypeError();
	  if (!(arr instanceof Array)) return Deferred.reject(TypeError());
	  var d = new Deferred();

	  function done(e, v) {
	    if (v) return d.resolve(v);
	    if (e) return d.reject(e);
	    var unresolved = arr.reduce(function (cnt, v) {
	      if (v && v.then) return cnt + 1;
	      return cnt;
	    }, 0);
	    if (unresolved == 0) d.resolve(arr);
	    arr.map(function (v, i) {
	      if (v && v.then) v.then(function (r) {
	        arr[i] = r;
	        done();
	        return r;
	      }, done);
	    });
	  }

	  done();
	  return d;
	};

	Deferred.race = function (arr) {
	  if (!(this._d == 1)) throw TypeError();
	  if (!(arr instanceof Array)) return Deferred.reject(TypeError());
	  if (arr.length == 0) return new Deferred();
	  var d = new Deferred();

	  function done(e, v) {
	    if (v) return d.resolve(v);
	    if (e) return d.reject(e);
	    var unresolved = arr.reduce(function (cnt, v) {
	      if (v && v.then) return cnt + 1;
	      return cnt;
	    }, 0);
	    if (unresolved == 0) d.resolve(arr);
	    arr.map(function (v) {
	      if (v && v.then) v.then(function (r) {
	        done(null, r);
	      }, done);
	    });
	  }

	  done();
	  return d;
	};

	Deferred._d = 1;
	/**
	 * @constructor
	 */

	function Deferred(resolver) {

	  if (typeof resolver != "function" && resolver != undefined) throw TypeError();
	  if (_typeof(this) != "object" || this && this.then) throw TypeError(); // states
	  // 0: pending
	  // 1: resolving
	  // 2: rejecting
	  // 3: resolved
	  // 4: rejected

	  var self = this,
	      state = 0,
	      val = 0,
	      next = [],
	      fn,
	      er;
	  self["promise"] = self;

	  self["resolve"] = function (v) {
	    fn = self.fn;
	    er = self.er;

	    if (!state) {
	      val = v;
	      state = 1;
	      nextTick(fire);
	    }

	    return self;
	  };

	  self["reject"] = function (v) {
	    fn = self.fn;
	    er = self.er;

	    if (!state) {
	      val = v;
	      state = 2;
	      nextTick(fire);
	    }

	    return self;
	  };

	  self["_d"] = 1;

	  self["then"] = function (_fn, _er) {
	    if (!(this._d == 1)) throw TypeError();
	    var d = new Deferred();
	    d.fn = _fn;
	    d.er = _er;

	    if (state == 3) {
	      d.resolve(val);
	    } else if (state == 4) {
	      d.reject(val);
	    } else {
	      next.push(d);
	    }

	    return d;
	  };

	  self["finally"] = function (_handler) {
	    var _value;

	    var handler = function (value) {
	      _value = value;
	      return _handler();
	    };

	    var value = function () {
	      return _value;
	    };

	    return self.then(handler, handler).then(value, value);
	  };

	  self["catch"] = function (_er) {
	    return self["then"](null, _er);
	  }; //compatibility with old version of promiz lib


	  self["fail"] = function (_er) {
	    return self["then"](null, _er);
	  };

	  var finish = function (type) {
	    state = type || 4;

	    for (var i = 0; i < next.length; i++) {
	      var p = next[i];
	      state == 3 && p.resolve(val) || p.reject(val);
	    }
	  };

	  try {
	    if (typeof resolver == "function") resolver(self["resolve"], self["reject"]);
	  } catch (e) {
	    self["reject"](e);
	  }

	  return self; // ref : reference to 'then' function
	  // cb, ec, cn : successCallback, failureCallback, notThennableCallback

	  function thennable(ref, cb, ec, cn) {
	    // Promises can be rejected with other promises, which should pass through
	    if (state == 2) {
	      return cn();
	    }

	    if ((_typeof(val) == "object" || typeof val == "function") && typeof ref == "function") {
	      try {
	        // cnt protects against abuse calls from spec checker
	        var cnt = 0;
	        ref.call(val, function (v) {
	          if (cnt++) return;
	          val = v;
	          cb();
	        }, function (v) {
	          if (cnt++) return;
	          val = v;
	          ec();
	        });
	      } catch (e) {
	        val = e;
	        ec();
	      }
	    } else {
	      cn();
	    }
	  }

	  function fire() {
	    // check if it's a thenable
	    var ref;

	    try {
	      ref = val && val.then;
	    } catch (e) {
	      val = e;
	      state = 2;
	      return fire();
	    }

	    thennable(ref, function () {
	      state = 1;
	      fire();
	    }, function () {
	      state = 2;
	      fire();
	    }, function () {
	      try {
	        if (state == 1 && typeof fn == "function") {
	          val = fn(val);
	        } else if (state == 2 && typeof er == "function") {
	          val = er(val);
	          state = 1;
	        }
	      } catch (e) {
	        val = e;
	        return finish();
	      }

	      if (val == self) {
	        val = TypeError();
	        finish();
	      } else thennable(ref, function () {
	        finish(3);
	      }, finish, function () {
	        finish(state == 1 && 3);
	      });
	    });
	  }
	} // promise factory


	Deferred.defer = function () {
	  return new Deferred(null);
	};

	var xml = {
	  _isValidXML: function (data) {
	    if (!data || !data.documentElement) return null;
	    if (data.getElementsByTagName("parsererror").length) return null;
	    return data;
	  },
	  //convert xml string to xml object if necessary
	  toObject: function (text, response) {
	    var data = response ? response.rawxml ? response.rawxml() : response : null;
	    if (this._isValidXML(data)) return data;
	    if (typeof text == "string") data = this.fromString(text.replace(/^[\s]+/, ""));else data = text;
	    if (this._isValidXML(data)) return data;
	    return null;
	  },
	  //get array of records
	  getRecords: function (data) {
	    return this.xpath(data, this.records);
	  },
	  records: "/*/item",
	  child: "item",
	  config: "/*/config",
	  //get hash of properties for single record
	  getDetails: function (data) {
	    return this.tagToObject(data, {});
	  },
	  getOptions: function () {
	    return false;
	  },
	  //get count of data and position at which new data_loading need to be inserted
	  getInfo: function (data) {
	    var config = this.xpath(data, this.config);
	    if (config.length) config = this.assignTypes(this.tagToObject(config[0], {}));else config = null;
	    return {
	      size: data.documentElement.getAttribute("total_count") || 0,
	      from: data.documentElement.getAttribute("pos"),
	      parent: data.documentElement.getAttribute("parent") || 0,
	      config: config,
	      key: data.documentElement.getAttribute("webix_security") || null
	    };
	  },
	  //xpath helper
	  xpath: function (xml, path) {
	    if (window.XPathResult) {
	      //FF, KHTML, Opera
	      var node = xml;
	      if (xml.nodeName.indexOf("document") == -1) xml = xml.ownerDocument;
	      var res = [];
	      var col = xml.evaluate(path, node, null, XPathResult.ANY_TYPE, null);
	      var temp = col.iterateNext();

	      while (temp) {
	        res.push(temp);
	        temp = col.iterateNext();
	      }

	      return res;
	    } else {
	      var test = true;

	      try {
	        if (typeof xml.selectNodes == "undefined") test = false;
	      } catch (e) {}
	      /*IE7 and below can't operate with xml object*/
	      //IE


	      if (test) return xml.selectNodes(path);else {
	        //there is no interface to do XPath
	        //use naive approach
	        var name = path.split("/").pop();
	        return xml.getElementsByTagName(name);
	      }
	    }
	  },
	  assignTypes: function (obj) {
	    for (var k in obj) {
	      var test = obj[k];
	      if (_typeof(test) == "object") this.assignTypes(test);else if (typeof test == "string") {
	        if (test === "") continue;
	        if (test == "true") obj[k] = true;else if (test == "false") obj[k] = false;else if (test == test * 1) obj[k] = obj[k] * 1;
	      }
	    }

	    return obj;
	  },
	  //convert xml tag to js object, all subtags and attributes are mapped to the properties of result object
	  tagToObject: function (tag, z) {
	    var isArray = tag.nodeType == 1 && tag.getAttribute("stack");
	    var hasSubTags = 0;

	    if (!isArray) {
	      z = z || {}; //map attributes

	      var a = tag.attributes;
	      if (a && a.length) for (var i = 0; i < a.length; i++) {
	        z[a[i].name] = a[i].value;
	        hasSubTags = 1;
	      } //map subtags

	      var b = tag.childNodes;

	      for (var _i = 0; _i < b.length; _i++) {
	        if (b[_i].nodeType == 1) {
	          var name = b[_i].tagName;

	          if (z[name]) {
	            if (typeof z[name].push != "function") z[name] = [z[name]];
	            z[name].push(this.tagToObject(b[_i], {}));
	          } else z[name] = this.tagToObject(b[_i], {}); //sub-object for complex subtags


	          hasSubTags = 2;
	        }
	      }

	      if (!hasSubTags) return this.nodeValue(tag); //each object will have its text content as "value" property
	      //only if has not sub tags

	      if (hasSubTags < 2) z.value = z.value || this.nodeValue(tag);
	    } else {
	      z = [];
	      var _b = tag.childNodes;

	      for (var _i2 = 0; _i2 < _b.length; _i2++) {
	        if (_b[_i2].nodeType == 1) z.push(this.tagToObject(_b[_i2], {}));
	      }
	    }

	    return z;
	  },
	  //get value of xml node 
	  nodeValue: function (node) {
	    if (node.firstChild) {
	      return node.firstChild.wholeText || node.firstChild.data;
	    }

	    return "";
	  },
	  //convert XML string to XML object
	  fromString: function (xmlString) {
	    try {
	      if (window.DOMParser) // FF, KHTML, Opera
	        return new DOMParser().parseFromString(xmlString, "text/xml");
	      /* global ActiveXObject */

	      if (window.ActiveXObject) {
	        // IE, utf-8 only 
	        var temp = new ActiveXObject("Microsoft.xmlDOM");
	        temp.loadXML(xmlString);
	        return temp;
	      }
	    } catch (e) {
	      assert(0, e);
	      return null;
	    }

	    assert(0, "Load from xml string is not supported");
	  }
	};

	var json = {
	  //convert json string to json object if necessary
	  toObject: function (data) {
	    if (!data) return null;

	    if (typeof data == "string") {
	      try {
	        if (this.parseDates) {
	          var isodate = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(.\d{1-3})?Z/;
	          data = JSON.parse(data, function (key, value) {
	            if (typeof value == "string") {
	              if (isodate.test(value)) return new Date(value);
	            }

	            return value;
	          });
	        } else {
	          data = JSON.parse(data);
	        }
	      } catch (e) {
	        log(e);
	        log(data);
	        assert(0, "Invalid JSON data for parsing");
	        return null;
	      }
	    }

	    return data;
	  },
	  //get array of records
	  getRecords: function (data) {
	    if (data && data.data) data = data.data;
	    if (data && !isArray(data)) return [data];
	    return data;
	  },
	  //get hash of properties for single record
	  getDetails: function (data) {
	    if (typeof data == "string") return {
	      id: data || uid(),
	      value: data
	    };
	    return data;
	  },
	  getOptions: function (data) {
	    return data.collections;
	  },
	  //get count of data and position at which new data need to be inserted
	  getInfo: function (data) {
	    return {
	      size: data.total_count || 0,
	      from: data.pos,
	      parent: data.parent || 0,
	      config: data.config,
	      key: data.webix_security
	    };
	  },
	  child: "data",
	  parseDates: false
	};

	function ajax(url, params, call) {
	  //if parameters was provided - made fast call
	  if (arguments.length !== 0) {
	    return new ajax().get(url, params, call);
	  }

	  if (!this || !this.getXHR) return new ajax(); //allow to create new instance without direct new declaration

	  return this;
	}
	ajax.count = 0;
	ajax.prototype = {
	  master: null,
	  //creates xmlHTTP object
	  getXHR: function () {
	    return new XMLHttpRequest();
	  },
	  stringify: function (obj) {
	    return stringify(obj);
	  },

	  /*
	  	send data to the server
	  	params - hash of properties which will be added to the url
	  	call - callback, can be an object with success and error functions
	  */
	  _send: function (url, params, call, mode) {
	    var master; //webix.ajax(url, callback) - can be called only by user

	    if (params && (isArray(params) || typeof (params.success || params.error || params) == "function")) {
	      master = call;
	      call = params;
	      params = null;
	    }

	    var defer = Deferred.defer();
	    var x = this.getXHR();
	    var headers = this._header || {};

	    if (!callEvent("onBeforeAjax", [mode, url, params, x, headers, null, defer])) {
	      return defer.reject(x);
	    } //add content-type to POST|PUT|DELETE


	    var json_mode = false;

	    if (mode !== "GET") {
	      var found = false;

	      for (var key in headers) {
	        if (key.toString().toLowerCase() == "content-type") {
	          found = true;
	          if (headers[key] == "application/json") json_mode = true;
	        }
	      }

	      if (!found && !(window.FormData && params instanceof window.FormData)) headers["Content-Type"] = "application/x-www-form-urlencoded";
	    } //add extra params to the url


	    if (_typeof(params) == "object" && !(window.FormData && params instanceof window.FormData)) {
	      if (json_mode) params = this.stringify(params);else {
	        var t = [];

	        for (var a in params) {
	          var value = params[a];
	          if (value === null || value === undefined) value = "";
	          if (_typeof(value) === "object") value = this.stringify(value);
	          t.push(a + "=" + encodeURIComponent(value)); // utf-8 escaping
	        }

	        params = t.join("&");
	      }
	    }

	    if (params && mode === "GET") {
	      url = url + (url.indexOf("?") != -1 ? "&" : "?") + params;
	      params = null;
	    }

	    x.open(mode, url, !this._sync);
	    var type = this._response;
	    if (type) x.responseType = type; //if header was provided - use it

	    for (var _key in headers) {
	      x.setRequestHeader(_key, headers[_key]);
	    } //async mode, define loading callback


	    var self = this;
	    this.master = this.master || master;

	    x.onreadystatechange = function () {
	      if (!x.readyState || x.readyState == 4) {
	        ajax.count++;
	        var is_error = x.status >= 400 || x.status === 0;
	        var text, data;

	        if (x.responseType == "blob" || x.responseType == "arraybuffer") {
	          text = "";
	          data = x.response;
	        } else {
	          text = x.responseText || "";
	          data = self._data(x);
	        }

	        if (is_error) {
	          callEvent("onAjaxError", [x]);
	          defer.reject(x);
	          if (call) ajax.$callback(self.master || window, call, text, data, x, is_error);
	        } else {
	          defer.resolve(data);
	          if (call) ajax.$callback(self.master || window, call, text, data, x, is_error);
	        }
	      }
	    };

	    if (this._timeout) x.timeout = this._timeout; //IE can use sync mode sometimes, fix it

	    if (!this._sync) setTimeout(function () {
	      x.send(params || null);
	    }, 1);else x.send(params || null);

	    if (this.master && !this._sync) {
	      defer.then(function (data) {
	        //anti-leak
	        self.master = null;
	        call = self = master = null;
	        return data;
	      });
	    }

	    return this._sync ? x : defer; //return XHR, which can be used in case of sync. mode
	  },
	  _data: function (x) {
	    return {
	      xml: function () {
	        try {
	          return xml.tagToObject(xml.toObject(x.responseText, this));
	        } catch (e) {
	          log(x.responseText);
	          log(e.toString());
	          assert(0, "Invalid xml data for parsing");
	        }
	      },
	      rawxml: function () {
	        if (!window.XPathResult) return xml.fromString(x.responseText);
	        return x.responseXML;
	      },
	      text: function () {
	        return x.responseText;
	      },
	      json: function () {
	        return json.toObject(x.responseText, false);
	      }
	    };
	  },
	  //GET request
	  get: function (url, params, call) {
	    return this._send(url, params, call, "GET");
	  },
	  //POST request
	  post: function (url, params, call) {
	    return this._send(url, params, call, "POST");
	  },
	  //PUT request
	  put: function (url, params, call) {
	    return this._send(url, params, call, "PUT");
	  },
	  //DELETE request
	  del: function (url, params, call) {
	    return this._send(url, params, call, "DELETE");
	  },
	  //PATCH request
	  patch: function (url, params, call) {
	    return this._send(url, params, call, "PATCH");
	  },
	  sync: function () {
	    this._sync = true;
	    return this;
	  },
	  timeout: function (num) {
	    this._timeout = num;
	    return this;
	  },
	  response: function (value) {
	    this._response = value;
	    return this;
	  },
	  headers: function (header) {
	    this._header = exports.extend(this._header || {}, header);
	    return this;
	  },
	  bind: function (master) {
	    this.master = master;
	    return this;
	  }
	};

	ajax.$callback = function (owner, call, text, data, x, is_error) {
	  if (owner.$destructed) return;
	  if (is_error) callEvent("onAjaxError", [x]);

	  if (call) {
	    var method = call.success || call;
	    if (is_error) method = call.error;
	    if (method && method.call) method.call(owner, text, data, x);
	  }
	};

	var CodeParser = {
	  //converts a complex object into an object with primitives properties
	  collapseNames: function (base, prefix, data, filter) {
	    data = data || {};
	    prefix = prefix || "";

	    filter = filter || function () {
	      return true;
	    };

	    if (!base || _typeof(base) != "object") return null;

	    for (var prop in base) {
	      var value = base[prop];
	      var name = prefix + prop;

	      if (value && _typeof(value) == "object" && !isDate(value) && !isArray(value) && filter(name, value)) {
	        CodeParser.collapseNames(value, name + ".", data, filter);
	      } else {
	        data[name] = value;
	      }
	    }

	    return data;
	  },
	  //converts an object with primitive properties into an object with complex properties
	  expandNames: function (base) {
	    var data = {},
	        i,
	        lastIndex,
	        name,
	        obj,
	        prop;

	    for (prop in base) {
	      name = prop.split(".");
	      lastIndex = name.length - 1;
	      obj = data;

	      for (i = 0; i < lastIndex; i++) {
	        if (!obj[name[i]]) obj[name[i]] = {};
	        obj = obj[name[i]];
	      }

	      obj[name[lastIndex]] = base[prop];
	    }

	    return data;
	  }
	};

	/*
		Template - handles html templates
	*/
	var _cache = {};
	var _csp_cache = {};
	var newlines = new RegExp("(\\r\\n|\\n)", "g");
	var quotes = new RegExp("(\\\")", "g");
	var slashes = new RegExp("(\\\\)", "g");
	var escape$1 = {
	  "&": "&amp;",
	  "<": "&lt;",
	  ">": "&gt;",
	  "\"": "&quot;",
	  "'": "&#x27;",
	  "`": "&#x60;"
	};
	var badChars = /[&<>"'`]/g;

	var escapeChar = function (chr) {
	  return escape$1[chr] || "&amp;";
	};

	function template(str) {
	  if (typeof str == "function") return str;
	  if (_cache[str]) return _cache[str];
	  str = (str || "").toString();

	  if (str.indexOf("->") != -1) {
	    var teststr = str.split("->");

	    switch (teststr[0]) {
	      case "html":
	        //load from some container on the page
	        str = getValue(teststr[1]);
	        break;

	      case "http":
	        //load from external file
	        str = new ajax().sync().get(teststr[1], {
	          uid: uid()
	        }).responseText;
	        break;

	      default:
	        //do nothing, will use template as is
	        break;
	    }
	  } //supported idioms
	  // {obj.attr} => named attribute or value of sub-tag in case of xml


	  str = (str || "").toString(); // Content Security Policy enabled

	  if (env.strict) {
	    if (!_csp_cache[str]) {
	      _csp_cache[str] = []; // get an array of objects (not sorted by position)

	      var temp_res = [];
	      str.replace(/\{obj\.([^}?]+)\?([^:]*):([^}]*)\}/g, function (search, s1, s2, s3, pos$$1) {
	        temp_res.push({
	          pos: pos$$1,
	          str: search,
	          fn: function (obj) {
	            return obj[s1] ? s2 : s3;
	          }
	        });
	      });
	      str.replace(/\{common\.([^}(]*)\}/g, function (search, s, pos$$1) {
	        temp_res.push({
	          pos: pos$$1,
	          str: search,
	          fn: function (_, common) {
	            return common[s] || "";
	          }
	        });
	      });
	      str.replace(/\{common\.([^}(]*)\(\)\}/g, function (search, s, pos$$1) {
	        temp_res.push({
	          pos: pos$$1,
	          str: search,
	          fn: function (obj, common) {
	            return common[s] ? common[s].apply(this, arguments) : "";
	          }
	        });
	      });
	      str.replace(/\{obj\.([^:}]*)\}/g, function (search, s, pos$$1) {
	        temp_res.push({
	          pos: pos$$1,
	          str: search,
	          fn: function (obj) {
	            return obj[s];
	          }
	        });
	      });
	      str.replace("{obj}", function (search, pos$$1) {
	        temp_res.push({
	          pos: pos$$1,
	          str: search,
	          fn: function (obj) {
	            return obj;
	          }
	        });
	      });
	      str.replace(/#([^#'";, ]+)#/gi, function (search, s, pos$$1) {
	        if (s.charAt(0) == "!") {
	          s = s.substr(1);
	          temp_res.push({
	            pos: pos$$1,
	            str: search,
	            fn: function (obj) {
	              if (s.indexOf(".") != -1) obj = CodeParser.collapseNames(obj); // apply complex properties

	              return template.escape(obj[s]);
	            }
	          });
	        } else {
	          temp_res.push({
	            pos: pos$$1,
	            str: search,
	            fn: function (obj) {
	              if (s.indexOf(".") != -1) obj = CodeParser.collapseNames(obj); // apply complex properties

	              return obj[s];
	            }
	          });
	        }
	      }); // sort template parts by position

	      temp_res.sort(function (a, b) {
	        return a.pos > b.pos ? 1 : -1;
	      }); // create an array of functions that return parts of html string

	      if (temp_res.length) {
	        var lastPos = 0;

	        var addStr = function (str, n0, n1) {
	          _csp_cache[str].push(function () {
	            return str.slice(n0, n1);
	          });
	        };

	        for (var i = 0; i < temp_res.length; i++) {
	          var pos$$1 = temp_res[i].pos;
	          addStr(str, lastPos, pos$$1);

	          _csp_cache[str].push(temp_res[i].fn);

	          lastPos = pos$$1 + temp_res[i].str.length;
	        }

	        addStr(str, lastPos, str.length);
	      } else _csp_cache[str].push(function () {
	        return str;
	      });
	    }

	    return function () {
	      var s = "";

	      for (var i = 0; i < _csp_cache[str].length; i++) {
	        s += _csp_cache[str][i].apply(this, arguments);
	      }

	      return s;
	    };
	  }

	  var helpers = false;
	  str = str.replace(slashes, "\\\\");
	  str = str.replace(newlines, "\\n");
	  str = str.replace(quotes, "\\\"");
	  str = str.replace(/\{obj\.([^}?]+)\?([^:]*):([^}]*)\}/g, "\"+(obj.$1?\"$2\":\"$3\")+\"");
	  str = str.replace(/\{common\.([^}(]*)\}/g, "\"+(common.$1||'')+\"");
	  str = str.replace(/\{common\.([^}(]*)\(\)\}/g, "\"+(common.$1?common.$1.apply(this, arguments):\"\")+\"");
	  str = str.replace(/\{obj\.([^}]*)\}/g, "\"+(obj.$1)+\"");
	  str = str.replace("{obj}", "\"+obj+\"");
	  str = str.replace(/#([^#'";, ]+)#/gi, function (str, key) {
	    if (key.charAt(0) == "!") {
	      helpers = true;
	      return "\"+template.escape(obj." + key.substr(1) + ")+\"";
	    } else return "\"+(obj." + key + ")+\"";
	  });

	  try {
	    if (helpers) {
	      var temp = Function("obj", "common", "marks", "value", "template", "return \"" + str + "\";");

	      _cache[str] = function (a, b, c, d) {
	        return temp(a, b, c, d, template);
	      };
	    } else {
	      _cache[str] = Function("obj", "common", "return \"" + str + "\";");
	    }
	  } catch (e) {
	    assert(0, "Invalid template:" + str);
	  }

	  return _cache[str];
	}

	template.escape = function (str) {
	  if (str === undefined || str === null) return "";
	  return (str.toString() || "").replace(badChars, escapeChar);
	};

	template.empty = function () {
	  return "";
	};

	/*
		adds new template-type
		obj - object to which template will be added
		data - properties of template
	*/

	function type(obj, data) {
	  if (obj.$protoWait) {
	    if (!obj._webix_type_wait) obj._webix_type_wait = [];

	    obj._webix_type_wait.push(data);

	    return;
	  } //auto switch to prototype, if name of class was provided


	  if (typeof obj == "function") obj = obj.prototype;

	  if (!obj.types) {
	    obj.types = {
	      "default": obj.type
	    };
	    obj.type.name = "default";
	  }

	  var name = data.name;
	  var type = obj.type;
	  if (name) type = obj.types[name] = clone(data.baseType ? obj.types[data.baseType] : obj.type);

	  for (var key in data) {
	    if (key.indexOf("template") === 0) type[key] = template(data[key]);else type[key] = data[key];
	  }

	  return name;
	}

	var views = {};

	function ui(config, parent, id) {
	  var res;
	  state._ui_creation++; // save old value of global scope

	  var temp = state._global_scope; // set global scope to the scope of new UI or to previous value
	  // as result inner webix.ui calls will have access the scope of master view
	  // mainly necessary for suggests

	  state._global_scope = config.$scope || temp;

	  try {
	    res = _ui_creator(config, parent, id);
	  } finally {
	    state._ui_creation--; // restore global scope

	    state._global_scope = temp;
	  }

	  return res;
	}

	ui.views = views;

	function _ui_creator(config, parent, id) {
	  var multiset = isArray(config);
	  var node = toNode(config.container || parent || document.body); // solve problem with non-unique ids

	  if (node._settings) id = _correctId(node, multiset, id);
	  var top_node;
	  var moving = false;
	  var body_child = node == document.body;

	  if (config._settings || node && multiset) {
	    top_node = config;
	    moving = true;
	  } else {
	    if (node && body_child) config.$topView = true;
	    if (!config._inner) config._inner = {};

	    if (parent && parent.getParentView) {
	      state._parent_cell = !id && id !== 0 ? parent.getParentView() : parent;
	    }

	    top_node = _view(config);
	  }

	  if (body_child && !top_node.setPosition && !top_node.$apiOnly) use("_fixHeight")();

	  if (top_node._settings && top_node._settings._hidden && !node.$view) {
	    top_node._settings._container = node;
	  } else if (!top_node.$apiOnly) {
	    if (node.appendChild) _appendDom(node, top_node, config);else if (node.destructor) {
	      var target = node; //addView or view moving with target id

	      if (!id && id !== 0 && !isArray(top_node)) {
	        id = node;
	        node = node.getParentView();
	      } //if target supports view adding


	      if (node && node._replace) {
	        if (moving && top_node.getParentView) {
	          //if source supports view removing
	          var _parent = top_node.getParentView();

	          if (_parent && _parent._remove) {
	            _parent._remove(top_node);
	          } //adjust parent link and scope


	          top_node._parent_cell = node;
	          top_node.$scope = node.$scope;
	        }

	        node._replace(top_node, id);
	      } else {
	        var _parent2 = target.$view.parentNode;
	        target.destructor();

	        _appendDom(_parent2, top_node, config);
	      }
	    } else assert(0, "Not existing parent:" + config.container);
	  }

	  return top_node;
	}

	function _appendDom(node, top_node, config) {
	  node.appendChild(top_node._viewobj);
	  if (top_node.getParentView()) return; //resize window with position center or top
	  //do not resize other windows and elements
	  // which are attached to custom html containers

	  if ((!top_node.setPosition || top_node._settings.fullscreen) && node == document.body || top_node._settings.position) state.top_views.push(top_node._destructor_handler);
	  if (!config.skipResize) top_node.adjust();
	}

	function _correctId(target, multiset, id) {
	  //replace view
	  var views = [target]; //replace content of layout

	  if (multiset) views = target.getChildViews(); //replace content of window
	  else if (target._body_cell) views = [target._body_cell]; //add cell in layout by number
	    else if (typeof id == "number") {
	        return id; //replace cell in layout by id
	      } else if (id) {
	        views = [$$(id)];

	        _deleteIds(views);

	        return views[0].config.id;
	      }

	  _deleteIds(views);

	  return id;
	}

	function _deleteIds(uis) {
	  for (var i = uis.length - 1; i >= 0; i--) {
	    var current = uis[i]; //remove original id

	    delete views[current.config.id]; //create temp id

	    current.config.id = "x" + uid();
	    views[current.config.id] = current; //process childs

	    if (current.getChildViews) _deleteIds(current.getChildViews()); //process related UI

	    if (current._destroy_with_me) _deleteIds(current._destroy_with_me);
	  }
	}

	function hasMethod(view, method) {
	  var obj = ui[view];
	  if (!obj) return false;
	  if (obj.$protoWait) obj = obj.call(-1);
	  return !!ui[view].prototype[method];
	}

	function _view(config) {
	  {
	    // check for trailing comma
	    var coll = config.cells || config.rows || config.elements || config.cols;
	    if (coll) for (var i = 0; i < coll.length; i++) {
	      if (coll[i] === null || typeof coll[i] === "undefined") assert(0, "You have trailing comma or Null element in collection's configuration");
	    }
	  }

	  if (config.view) {
	    var view = config.view;
	    assert(ui[view], "unknown view:" + view);
	    return new ui[view](config);
	  } else if (config.rows || config.cols) {
	    var cells = config.rows || config.cols;
	    var accordion = false;

	    for (var _i = 0; _i < cells.length; _i++) {
	      if (cells[_i].body && !cells[_i].view && !cells[_i].align) accordion = true;
	    }

	    if (accordion) {
	      return new ui.headerlayout(config);
	    } else return new ui.layout(config);
	  } else if (config.cells) return new ui.multiview(config);else if (config.template || config.content) return new ui.template(config);else if (config.align && config.body) {
	    return new ui.align(config);
	  } else return new ui.spacer(config);
	} //FIXME


	ui._view = _view;

	function $$(id) {
	  if (!id) return null;
	  if (views[id]) return views[id];
	  var name = id;

	  if (_typeof(id) == "object") {
	    if (id._settings) return id;
	    name = id.target || id.srcElement || id;
	  }

	  return views[locate({
	    target: toNode(name)
	  },
	  /*@attr*/
	  "view_id")];
	}

	if (typeof window.$$ === "undefined") window.$$ = $$;

	exports.protoUI = function () {
	  var origins = arguments;
	  var selfname = origins[0].name;

	  var t = function (data) {
	    if (!t) return ui[selfname].prototype;
	    var origins = t.$protoWait;

	    if (origins) {
	      var params = [origins[0]];

	      for (var i = 1; i < origins.length; i++) {
	        params[i] = origins[i];
	        if (params[i].$protoWait) params[i] = params[i].call(-1, params[i].name);
	        if (params[i].prototype && params[i].prototype.name) ui[params[i].prototype.name] = params[i];
	      }

	      ui[selfname] = exports.proto.apply(-1, params);
	      if (t._webix_type_wait) for (var _i2 = 0; _i2 < t._webix_type_wait.length; _i2++) {
	        type(ui[selfname], t._webix_type_wait[_i2]);
	      }
	      t = origins = null;
	    }

	    if (this != -1) return new ui[selfname](data);else return ui[selfname];
	  };

	  t.$protoWait = Array.prototype.slice.call(arguments, 0);
	  return ui[selfname] = t;
	};

	exports.proto = function () {
	  var origins = arguments;
	  var compilation = origins[0];
	  var has_constructor = !!compilation.$init;
	  var construct = [];
	  assert(compilation, "Invalid mixing target");

	  for (var i = origins.length - 1; i > 0; i--) {
	    assert(origins[i], "Invalid mixing source");
	    if (typeof origins[i] == "function") origins[i] = origins[i].prototype;
	    if (origins[i].$init) construct.push(origins[i].$init);

	    if (origins[i].defaults) {
	      var defaults = origins[i].defaults;
	      if (!compilation.defaults) compilation.defaults = {};

	      for (var def in defaults) {
	        if (isUndefined(compilation.defaults[def])) compilation.defaults[def] = defaults[def];
	      }
	    }

	    if (origins[i].type && compilation.type) {
	      for (var _def in origins[i].type) {
	        if (!compilation.type[_def]) compilation.type[_def] = origins[i].type[_def];
	      }
	    }

	    for (var key in origins[i]) {
	      if (!compilation[key] && compilation[key] !== false) compilation[key] = origins[i][key];
	    }
	  }

	  if (has_constructor) construct.push(compilation.$init);

	  compilation.$init = function () {
	    for (var i = 0; i < construct.length; i++) {
	      construct[i].apply(this, arguments);
	    }
	  };

	  if (compilation.$skin) compilation.$skin();

	  var result = function (config) {
	    this.$ready = [];
	    assert(this.$init, "object without init method");
	    this.$init(config);
	    if (this._parseSettings) this._parseSettings(config, this.defaults);

	    for (var i = 0; i < this.$ready.length; i++) {
	      this.$ready[i].call(this);
	    }
	  };

	  result.prototype = compilation;
	  compilation = origins = null;
	  return result;
	};

	attachEvent("onClick", function (e) {
	  var element = $$(e);

	  if (element && element.touchable) {
	    use("UIManager").applyChanges(element); //for inline elements - restore pointer to the master element

	    element.getNode(e); //reaction on custom css elements in buttons

	    var trg = e.target || e.srcElement;
	    if (trg.className == "webix_disabled") return;
	    var css = "";
	    if (trg.className && trg.className.toString().indexOf("webix_view") === 0) return;
	    if (element) use("UIManager")._focus_action(element); //loop through all parents

	    while (trg && trg.parentNode) {
	      if (trg.getAttribute) {
	        if (trg.getAttribute(
	        /*@attr*/
	        "view_id")) break;
	        css = trg.className;

	        if (css) {
	          css = css.toString().split(" ");

	          for (var i = 0; i < css.length; i++) {
	            if (element.on_click[css[i]]) {
	              var res = element.on_click[css[i]].call(element, e, element._settings.id, trg);
	              if (res === false) return;
	            }
	          }
	        }
	      }

	      trg = trg.parentNode;
	    }

	    if (element._settings.click) {
	      var code = toFunctor(element._settings.click, element.$scope);
	      if (code && code.call) code.call(element, element._settings.id, e);
	    }

	    var popup = element._settings.popup;

	    if (element._settings.popup && !element._settings.readonly) {
	      if (_typeof(popup) == "object" && !popup.name) popup = element._settings.popup = ui(popup)._settings.id;
	      popup = $$(popup);
	      assert(popup, "Unknown popup");

	      if (!popup.isVisible()) {
	        popup._settings.master = element._settings.id;
	        popup.show(element.getInputNode() || element.getNode(), null, true);
	      }
	    }

	    element.callEvent("onItemClick", [element._settings.id, e]);
	  }
	}); //hook for documentation generator

	{
	  if (window.webix_on_core_ready) {
	    var mod = window.webix_on_core_ready({
	      proto: exports.proto,
	      protoUI: exports.protoUI
	    });
	    exports.proto = mod.proto;
	    exports.protoUI = mod.protoUI;
	  }
	}

	var _events = {};
	function _events_final_destructor() {
	  //detach all known DOM events
	  for (var a in _events) {
	    eventRemove(a);
	  }
	} //private version of API, do not register ID for event detaching

	function _event(a, b, c, d) {
	  d = d || {};
	  d.inner = true;
	  event$1(a, b, c, d);
	} //attach event to the DOM element

	function event$1(node, event, handler, context) {
	  context = context || {};
	  node = toNode(node);
	  assert(node, "Invalid node as target for webix.event");
	  var id = context.id || uid();
	  if (context.bind) handler = bind(handler, context.bind);
	  var info = [node, event, handler, context.capture];
	  if (!context.inner) _events[id] = info; //store event info, for detaching

	  var capture = !!context.capture;
	  if (!isUndefined(context.passive) && env.passiveEventListeners) //blockable touch events
	    capture = {
	      passive: context.passive,
	      capture: capture
	    }; //use IE's of FF's way of event's attaching

	  if (node.addEventListener) node.addEventListener(event, handler, capture);else if (node.attachEvent) node.attachEvent("on" + event, info[2] = function () {
	    return handler.apply(node, arguments); //IE8 fix
	  });
	  return id; //return id of newly created event, can be used in eventRemove
	} //remove previously attached event

	function eventRemove(id) {
	  if (!id) return;
	  assert(_events[id], "Removing non-existing event");
	  var ev = _events[id]; //browser specific event removing

	  if (ev[0].removeEventListener) ev[0].removeEventListener(ev[1], ev[2], !!ev[3]);else if (ev[0].detachEvent) ev[0].detachEvent("on" + ev[1], ev[2]);
	  delete _events[id]; //delete all traces
	}

	var ActiveContent = {
	  $init: function (config) {
	    if (config.activeContent) {
	      this.$ready.push(this._init_active_content_list);
	      this._active_holders = {};
	      this._active_holders_item = {};
	      this._active_holders_values = {};
	      this._active_references = {};

	      for (var key in config.activeContent) {
	        this[key] = this._bind_active_content(key);

	        if (config.activeContent[key].earlyInit) {
	          var temp = state._parent_cell;
	          state._parent_cell = null;
	          this[key].call(this, {}, this, config.activeContent);
	          state._parent_cell = temp;
	        }
	      }
	    }
	  },
	  _destructActiveContent: function () {
	    for (var key in this._active_references) {
	      var elem = this._active_references[key];
	      if (elem.destructor) elem.destructor();
	    }
	  },
	  _init_active_content_list: function () {
	    this.attachEvent("onDestruct", bind(this._destructActiveContent, this));

	    _event(this.$view, "blur", function (ev) {
	      var target = ev.target || ev.srcElement; // for inputs only

	      if (target.tagName != "BUTTON") {
	        var el = $$(ev);

	        if (el && el !== this && el.getValue && el.setValue) {
	          el.getNode(ev);
	          var newvalue = el.getValue();
	          if (newvalue != el._settings.value) el.setValue(newvalue);
	        }
	      }
	    }, {
	      bind: this,
	      capture: true
	    });

	    if (this.filter) {
	      for (var key in this._settings.activeContent) {
	        this.type[key] = this[key];
	        this[key] = this._locate_active_content_by_id(key);
	      } //really bad!


	      this.attachEvent("onBeforeRender", function () {
	        this.type.masterUI = this;
	      });
	      this.type.masterUI = this;
	    }
	  },
	  _locate_active_content_by_id: function (key) {
	    return function (id) {
	      var button = this._active_references[key];
	      var button_id = button._settings.id;
	      var html = this.getItemNode(id).getElementsByTagName("DIV");

	      for (var i = 0; i < html.length; i++) {
	        if (html[i].getAttribute(
	        /*@attr*/
	        "view_id") == button_id) {
	          button._viewobj = button._dataobj = html[i];
	          break;
	        }
	      }

	      return button;
	    };
	  },
	  _get_active_node: function (el, key, master) {
	    return function (e) {
	      if (e) {
	        var trg = e.target || e.srcElement;

	        while (trg) {
	          if (trg.getAttribute && trg.getAttribute(
	          /*@attr*/
	          "view_id")) {
	            master._setActiveContentView(el, trg);

	            if (master.locate) {
	              var id = master.locate(trg.parentNode);
	              var value = master._active_holders_values[key][id];
	              el._settings.value = value;
	              el._settings.$masterId = id;
	            }

	            return trg;
	          }

	          trg = trg.parentNode;
	        }
	      }

	      return el._viewobj;
	    };
	  },
	  _set_new_active_value: function (key, master) {
	    return function (value) {
	      var data = master.data;

	      if (master.filter) {
	        var id = master.locate(this._viewobj.parentNode);
	        data = master.getItem(id); //XMLSerializer - FF "feature"

	        this.refresh();
	        master._active_holders_item[key][id] = this._viewobj.outerHTML || new XMLSerializer().serializeToString(this._viewobj);
	        master._active_holders_values[key][id] = value;
	      }

	      if (data) data[key] = value;
	    };
	  },
	  _bind_active_content: function (key) {
	    return function (obj, common, active) {
	      var object = common._active_holders ? common : common.masterUI;

	      if (!object._active_holders[key]) {
	        var d = document.createElement("DIV");
	        active = active || object._settings.activeContent;
	        var el = ui(active[key], d);

	        if (env.isIE8) {
	          d.firstChild.setAttribute("onclick", "event.processed = true; event.srcElement.w_view = '" + el._settings.id + "';");
	        } else {
	          d.firstChild.setAttribute("onclick", "event.processed = true; ");
	        }

	        el.getNode = object._get_active_node(el, key, object);
	        el.attachEvent("onChange", object._set_new_active_value(key, object));
	        object._active_references[key] = el;
	        object._active_holders[key] = d.innerHTML;
	        object._active_holders_item[key] = {};
	        object._active_holders_values[key] = {};
	        el.$activeEl = el.$view;
	      }

	      if (object.filter && obj[key] != object._active_holders_values[key] && !isUndefined(obj[key])) {
	        var _el = object._active_references[key];

	        _el.blockEvent();

	        object._setActiveContentView(_el, _el.$activeEl); //in IE we can lost content of active element during parent repainting


	        if (!_el.$view.firstChild) _el.refresh();

	        _el.setValue(obj[key]);

	        _el.refresh();

	        _el.unblockEvent();

	        object._active_holders_values[key][obj.id] = obj[key];
	        object._active_holders_item[key][obj.id] = _el._viewobj.outerHTML || new XMLSerializer().serializeToString(_el._viewobj);
	      }

	      return object._active_holders_item[key][obj.id] || object._active_holders[key];
	    };
	  },
	  _setActiveContentView: function (el, view) {
	    el._dataobj = el._viewobj = el.$view = view;
	  }
	};

	var proxy = {
	  $proxy: true,
	  load: function () {
	    var parts = this.source.split("@");
	    var ext = parts[0].split(".").pop();
	    return ajax().response("arraybuffer").get(parts[0]).then(function (res) {
	      var options = {
	        ext: ext,
	        dataurl: parts[1]
	      };
	      return {
	        data: res,
	        options: options
	      };
	    });
	  }
	};

	var proxy$1 = {
	  $proxy: true,
	  connectorName: "!nativeeditor_status",
	  load: function () {
	    return ajax(this.source);
	  },
	  saveAll: function (view, updates) {
	    var url = this.source;
	    var data = {};
	    var ids = [];

	    for (var i = 0; i < updates.length; i++) {
	      var action = updates[i];
	      ids.push(action.id);

	      for (var j in action.data) {
	        if (j.indexOf("$") !== 0) data[action.id + "_" + j] = action.data[j];
	      }

	      data[action.id + "_" + this.connectorName] = action.operation;
	    }

	    data.ids = ids.join(",");
	    data.webix_security = state.securityKey;
	    url += url.indexOf("?") == -1 ? "?" : "&";
	    url += "editing=true";
	    return ajax().post(url, data).then(function (data) {
	      data = data.xml();
	      if (!data) throw "Data loading error";
	      var actions = data.data.action;
	      if (!actions.length) actions = [actions];
	      var hash = [];

	      for (var i = 0; i < actions.length; i++) {
	        var obj = actions[i];
	        obj.status = obj.type;
	        obj.id = obj.sid;
	        obj.newid = obj.tid;
	        hash.push(obj);
	      }

	      return hash;
	    });
	  }
	};

	var proxy$2 = {
	  $proxy: true,
	  load: function () {},
	  save: function (v, u, d) {
	    delay(function () {
	      window.console.log("[DP] " + u.id + " -> " + u.operation, u.data);
	      var data = {
	        id: u.data.id,
	        newid: u.data.id,
	        status: u.data.operation
	      };
	      d.processResult(data, data);
	    });
	  }
	};

	var proxy$10 = {
	  $proxy: true,
	  init: function () {
	    this.clientId = this.clientId || uid();
        this.client.emit('join channel',1);
	  },
	  load: function (view) {
	    var selfid = this.clientId;
	    this.client.on('chat message', function (update) {
	      if (update.clientId == selfid) return;
	      use("dp")(view).ignore(function () {
	        if (update.operation == "delete") {
                view.remove(update.data.id);
            } else if (update.operation == "insert") {
                view.add(update.data);
            } else if (update.operation == "update") {
	          var item = view.getItem(update.data.id);
	          if (item) {
                if (update.data.date) update.data.date = new Date(update.data.date);
	            exports.extend(item, update.data, true);
	            view.refresh(item.id);
	          }
	        }
	      });
	    });
	    this.client.on('error', function (update) {
            if (update.id) {
	            use("dp")(view).ignore(function () {
                    view.remove(update.id);
	            });
            }
            message({type: 'error', text: update.error});
	    });
	    this.client.on('delete message', function (update) {
	      use("dp")(view).ignore(function () {
	          view.remove(update);
	      });
	    });
	  },
	  save: function (view, update) {
	    update.clientId = this.clientId;
        this.client.emit('chat message', update);
	  }
	};

	var proxy$3 = {
	  $proxy: true,
	  init: function () {
	    this.clientId = this.clientId || uid();
	  },
	  load: function (view) {
	    var selfid = this.clientId;
	    this.client.subscribe(this.source, function (update) {
	      if (update.clientId == selfid) return;
	      use("dp")(view).ignore(function () {
	        if (update.operation == "delete") view.remove(update.data.id);else if (update.operation == "insert") view.add(update.data);else if (update.operation == "update") {
	          var item = view.getItem(update.data.id);

	          if (item) {
	            exports.extend(item, update.data, true);
	            view.refresh(item.id);
	          }
	        }
	      });
	    });
	  },
	  save: function (view, update) {
	    update.clientId = this.clientId;
	    this.client.publish(this.source, update);
	  }
	};

	var indexdb = {
	  $proxy: true,
	  create: function (db, config, version, callback) {
	    this.source = db + "/";

	    this._get_db(callback, version, function (e) {
	      var db = e.target.result;

	      for (var key in config) {
	        var data = config[key];
	        var store = db.createObjectStore(key, {
	          keyPath: "id",
	          autoIncrement: true
	        });

	        for (var i = 0; i < data.length; i++) {
	          store.put(data[i]);
	        }
	      }
	    });
	  },
	  _get_db: function (callback, version, upgrade) {
	    if (this.source.indexOf("/") != -1) {
	      var parts = this.source.split("/");
	      this.source = parts[1];
	      version = version || parts[2];

	      var _index = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB;

	      var db;
	      if (version) db = _index.open(parts[0], version);else db = _index.open(parts[0]);
	      if (upgrade) db.onupgradeneeded = upgrade;

	      db.onerror = function () {};

	      db.onblocked = function () {};

	      db.onsuccess = bind(function (e) {
	        this.db = e.target.result;
	        if (callback) callback.call(this);
	      }, this);
	    } else if (this.db) callback.call(this);else delay(this._get_db, this, [callback], 50);
	  },
	  load: function () {
	    var waitData = Deferred.defer();

	    this._get_db(function () {
	      var store = this.db.transaction(this.source).objectStore(this.source);
	      var data = [];
	      var req = store.openCursor();

	      req.onsuccess = function (e) {
	        var result = e.target.result;

	        if (result) {
	          data.push(result.value);
	          result["continue"]();
	        } else {
	          waitData.resolve(data);
	        }
	      };

	      req.onerror = function (e) {
	        waitData.reject(e);
	      };
	    });

	    return waitData;
	  },
	  save: function (view, update) {
	    var waitData = Deferred.defer();

	    this._get_db(function () {
	      var mode = update.operation;
	      var data = update.data;
	      var id = update.id;
	      var store = this.db.transaction([this.source], "readwrite").objectStore(this.source);
	      var req;
	      if (mode == "delete") req = store["delete"](id);else if (mode == "update") req = store.put(data);else if (mode == "insert") {
	        delete data.id;
	        req = store.add(data);
	      }

	      req.onsuccess = function (e) {
	        var result = {
	          status: mode,
	          id: update.id
	        };
	        if (mode == "insert") result.newid = e.target.result;
	        waitData.resolve(result);
	      };

	      req.onerror = function (e) {
	        waitData.reject(e);
	      };
	    });

	    return waitData;
	  }
	};

	var proxy$4 = {
	  $proxy: true,
	  load: function () {
	    return ajax(this.source);
	  },
	  save: function (view, update) {
	    return proxy$4._save_logic.call(this, update, ajax());
	  },
	  _save_logic: function (update, ajax$$1) {
	    var url = this.source;
	    var query = "";
	    var mark = url.indexOf("?");

	    if (mark !== -1) {
	      query = url.substr(mark);
	      url = url.substr(0, mark);
	    }

	    url += url.charAt(url.length - 1) == "/" ? "" : "/";
	    var mode = update.operation;
	    var data = update.data;
	    if (mode == "insert") delete data.id; //call rest URI

	    if (mode == "update") {
	      return ajax$$1.put(url + data.id + query, data);
	    } else if (mode == "delete") {
	      return ajax$$1.del(url + data.id + query, data);
	    } else {
	      return ajax$$1.post(url + query, data);
	    }
	  }
	};

	var proxy$5 = {
	  $proxy: true,
	  load: function () {
	    return ajax(this.source);
	  },
	  save: function (view, update) {
	    var xhr = ajax().headers({
	      "Content-Type": "application/json"
	    });
	    return proxy$4._save_logic.call(this, update, xhr);
	  }
	};

	var proxy$6 = {
	  $proxy: true,
	  load: function (view, params) {
	    params = exports.extend(params || {}, this.params || {}, true);
	    return ajax().post(this.source, params);
	  }
	};

	var jsarray = {
	  //parse jsarray string to jsarray object if necessary
	  toObject: function (data) {
	    if (typeof data == "string") return JSON.parse(data);
	    return data;
	  },
	  //get array of records
	  getRecords: function (data) {
	    if (data && data.data) data = data.data;
	    return data;
	  },
	  //get hash of properties for single record, in case of array they will have names as "data{index}"
	  getDetails: function (data) {
	    var result = {};

	    for (var i = 0; i < data.length; i++) {
	      result["data" + i] = data[i];
	    }

	    if (this.idColumn !== null) result.id = data[this.idColumn];
	    return result;
	  },
	  getOptions: function () {
	    return false;
	  },
	  //dyn loading is not supported by js-array data source
	  getInfo: function () {
	    return {
	      size: 0
	    };
	  },
	  idColumn: null
	};

	var csv = {
	  //incoming data always a string
	  toObject: function (data) {
	    return data;
	  },
	  //get array of records
	  getRecords: function (data) {
	    return data.split(this.row);
	  },
	  //get hash of properties for single record, data named as "data{index}"
	  getDetails: function (data) {
	    data = this.stringToArray(data);
	    var result = {};

	    for (var i = 0; i < data.length; i++) {
	      result["data" + i] = data[i];
	    }

	    if (this.idColumn !== null) result.id = data[this.idColumn];
	    return result;
	  },
	  getOptions: function () {
	    return false;
	  },
	  //dyn loading is not supported by csv data source
	  getInfo: function () {
	    return {
	      size: 0
	    };
	  },
	  //split string in array, takes string surrounding quotes in account
	  stringToArray: function (data) {
	    data = data.split(this.cell);

	    for (var i = 0; i < data.length; i++) {
	      data[i] = data[i].replace(/^[ \t\n\r]*("|)/g, "").replace(/("|)[ \t\n\r]*$/g, "");
	    }

	    return data;
	  },
	  idColumn: null,
	  row: "\n",
	  //default row separator
	  cell: "," //default cell separator

	};

	var html = {
	  /*
	  	incoming data can be
	  	 - ID of parent container
	  	 - HTML text
	  */
	  toObject: function (data) {
	    if (typeof data == "string") {
	      var t = null;
	      if (data.indexOf("<") == -1) //if no tags inside - probably its an ID
	        t = toNode(data);

	      if (!t) {
	        t = document.createElement("DIV");
	        t.innerHTML = data;
	      }

	      return t.firstChild;
	    }

	    return data;
	  },
	  //get array of records
	  getRecords: function (node) {
	    return node.getElementsByTagName(this.tag);
	  },
	  //get hash of properties for single record
	  getDetails: function (data) {
	    return xml.tagToObject(data);
	  },
	  getOptions: function () {
	    return false;
	  },
	  //dyn loading is not supported by HTML data source
	  getInfo: function () {
	    return {
	      size: 0
	    };
	  },
	  tag: "LI"
	};

	var htmltable = {
	  //convert json string to json object if necessary
	  toObject: function (data) {
	    data = toNode(data);
	    assert(data, "table is not found");
	    assert(data.tagName.toLowerCase() === "table", "Incorrect table object");
	    var tr = data.rows;
	    remove(data);
	    return tr;
	  },
	  //get array of records
	  getRecords: function (data) {
	    var new_data = []; //skip header rows if necessary

	    var i = data[0] && data[0]._webix_skip ? 1 : 0;

	    for (; i < data.length; i++) {
	      new_data.push(data[i]);
	    }

	    return new_data;
	  },
	  //get hash of properties for single record
	  getDetails: function (data) {
	    var td = data.getElementsByTagName("td");
	    data = {}; //get hash of properties for single record, data named as "data{index}"

	    for (var i = 0; i < td.length; i++) {
	      data["data" + i] = td[i].innerHTML;
	    }

	    return data;
	  },
	  //get count of data and position at which new data need to be inserted
	  getInfo: function () {
	    // dyn loading is not supported for htmltable
	    return {
	      size: 0
	    };
	  },
	  getOptions: function () {},

	  /*! gets header from first table row
	   **/
	  getConfig: function (data) {
	    var columns = [];
	    var td = data[0].getElementsByTagName("th");
	    if (td.length) data[0]._webix_skip = true;

	    for (var i = 0; i < td.length; i++) {
	      var col = {
	        id: "data" + i,
	        header: this._de_json(td[i].innerHTML)
	      };

	      var attrs = this._get_attrs(td[i]);

	      col = exports.extend(col, attrs);
	      columns.push(col);
	    }

	    return columns;
	  },
	  _de_json: function (str) {
	    var pos$$1 = str.indexOf("json://");
	    if (pos$$1 != -1) str = JSON.parse(str.substr(pos$$1 + 7));
	    return str;
	  },

	  /*! gets hash of html-element attributes
	   **/
	  _get_attrs: function (el) {
	    var attr = el.attributes;
	    var hash = {};

	    for (var i = 0; i < attr.length; i++) {
	      hash[attr[i].nodeName] = this._de_json(attr[i].nodeValue);
	    }

	    hash.width = parseInt(hash.width, 10);
	    return hash;
	  }
	};

	var _modules = {}; //hash of already loaded modules
	//loads module from external js file

	function require(module, callback, master) {
	  var promise = Deferred.defer();
	  if (callback && callback !== true) promise = promise.then(function () {
	    callback.call(master || this);
	  });

	  if (require.disabled) {
	    promise.resolve();
	    return promise;
	  } //multiple files required at once


	  if (typeof module != "string") {
	    var count = module.length || 0;

	    if (!count) {
	      // { file: true, other: true }
	      for (var file in module) {
	        count++;
	      } // eslint-disable-line


	      var callback2 = function () {
	        count--;
	        if (count === 0) promise.resolve();
	      };

	      for (var _file in module) {
	        require(_file, callback2, master);
	      }
	    } else {
	      // [ file, other ]
	      var _callback = function () {
	        if (count) {
	          count--;

	          require(module[module.length - count - 1], _callback, master);
	        } else {
	          promise.resolve();
	        }
	      };

	      _callback();
	    }

	    return promise;
	  }

	  if (_modules[module] !== true) {
	    var fullpath = module; //css, async, no waiting

	    var parts = module.split("?");

	    if (parts[0].substr(parts[0].length - 4) == ".css") {
	      var link = create("LINK", {
	        type: "text/css",
	        rel: "stylesheet",
	        href: fullpath
	      });
	      document.getElementsByTagName("head")[0].appendChild(link);
	      promise.resolve();
	      return promise;
	    } //js, async, waiting


	    if (callback === true) {
	      //sync mode
	      exec(ajax().sync().get(fullpath).responseText);
	      _modules[module] = true;
	    } else {
	      if (!_modules[module]) {
	        //first call
	        _modules[module] = [promise];
	        var newScript = document.createElement("script");
	        var calls = _modules[module]; //callbacks

	        newScript.onerror = function () {
	          _modules[module] = false;

	          for (var i = 0; i < calls.length; i++) {
	            calls[i].reject();
	          }
	        };

	        newScript.onload = function () {
	          _modules[module] = true;

	          for (var i = 0; i < calls.length; i++) {
	            calls[i].resolve();
	          }
	        };

	        document.getElementsByTagName("head")[0].appendChild(newScript);
	        newScript.src = fullpath;
	      } else //module already loading
	        _modules[module].push(promise);
	    }
	  } else promise.resolve();

	  return promise;
	}

	var excel = exports.extend({
	  toObject: function (data) {
	    if (!data.excel) {
	      var opts = data.options || {};
	      if (opts.dataurl) exports.extend(opts, this._urlToOptions(opts.dataurl));
	      data = data.data || data;
	      var promise = Deferred.defer();

	      if (data.name) {
	        //file
	        opts.ext = data.name.split(".").pop();
	        var reader = new FileReader();
	        reader.onload = bind(function (e) {
	          promise.resolve(this.parseData(e.target.result, opts));
	        }, this);
	        reader.readAsArrayBuffer(data);
	      } else //arraybuffer
	        promise.resolve(this.parseData(data, opts));

	      return promise;
	    } //plain jsarray or hash


	    return data;
	  },
	  parseData: function (data, options) {
	    data = new Uint8Array(data);
	    var arr = [];

	    for (var i = 0; i != data.length; ++i) {
	      arr[i] = String.fromCharCode(data[i]);
	    }

	    var ext = (options.ext || options).toLowerCase();
	    if (ext != "xls") ext = "xlsx";
	    return require(env.cdn + "/extras/xlsx.core.styles.min.js").then(bind(function () {
	      /* global XLS, XLSX */
	      var wb = ext == "xls" ? XLS.read(arr.join(""), {
	        type: "binary",
	        cellStyles: true,
	        cellDates: true
	      }) : XLSX.read(arr.join(""), {
	        type: "binary",
	        cellStyles: true,
	        cellDates: true
	      });
	      var res = {
	        sheets: wb.Sheets,
	        names: wb.SheetNames,
	        options: options,
	        ranges: wb.Workbook ? wb.Workbook.Names || [] : []
	      };
	      return exports.extend(this.getSheet(res, options), res);
	    }, this));
	  },
	  getSheet: function (data, options) {
	    var name = options.name || data.names[0];
	    data = this.sheetToArray(data.sheets[name], options);
	    if (options.rows && options.rows.length) data.data = data.data.splice(options.rows[0], Math.min(options.rows[1], data.data.length) - options.rows[0]);
	    return data;
	  },
	  sheetToArray: function (sheet, options) {
	    var all = [];
	    var spans = [];
	    var styles = [];
	    var sizes = [];

	    if (sheet && sheet["!ref"]) {
	      var range = XLS.utils.decode_range(sheet["!ref"]),
	          row,
	          col,
	          cellCoord,
	          cell,
	          xCorrection = range.s.c,
	          yCorrection = range.s.r + (options.rows ? options.rows[0] : 0);

	      for (row = range.s.r; row <= range.e.r; row++) {
	        var nrow = [];

	        for (col = range.s.c; col <= range.e.c; col++) {
	          cellCoord = XLS.utils.encode_cell({
	            r: row,
	            c: col
	          });
	          cell = sheet[cellCoord];
	          if (!cell) nrow.push("");else {
	            var ncell = "";
	            if (options.math && cell.f) // get formula
	              ncell = cell.f.charAt(0) == "=" ? cell.f : "=" + cell.f;else if (cell.t == "d" && isDate(cell.v)) ncell = i18n.dateFormatStr(cell.v);else ncell = cell.v;
	            nrow.push(ncell);
	            if (cell.s) styles.push([row - yCorrection, col - xCorrection, cell.s]);
	          }
	        }

	        all.push(nrow);
	      }

	      if (sheet["!merges"]) {
	        var merges = sheet["!merges"];

	        for (var i = 0; i < merges.length; i++) {
	          var s = merges[i].s;
	          var e = merges[i].e;
	          if (!options.rows || s.r - yCorrection >= 0 && e.r - yCorrection <= options.rows[1]) spans.push([s.r - yCorrection, s.c - xCorrection, e.c - s.c + 1, e.r - s.r + 1]);
	        }
	      }

	      if (sheet["!cols"]) {
	        var widths = sheet["!cols"];

	        for (var _i = 0; _i < widths.length; _i++) {
	          if (widths[_i]) sizes.push(["column", _i - xCorrection, Math.round(widths[_i].wch / (8.43 / 70))]);
	        } //mode, colind, value

	      }

	      if (sheet["!rows"]) {
	        var heights = sheet["!rows"];

	        for (var _i2 = 0; _i2 < heights.length; _i2++) {
	          if (heights[_i2]) sizes.push(["row", _i2 - yCorrection, heights[_i2].hpx]);
	        } //mode ("row", "column"), rowind, value

	      }
	    }

	    return {
	      data: all,
	      spans: spans,
	      styles: styles,
	      sizes: sizes,
	      excel: true
	    };
	  },
	  _urlToOptions: function (details) {
	    var parts = details.split("[");
	    var options = {};
	    options.name = parts[0];

	    if (parts[1]) {
	      var rows = parts[1].split(/[^0-9]+/g);
	      rows[0] = rows[0] * 1 || 0;
	      rows[1] = rows[1] * 1 || 9999999;
	      options.rows = rows;
	    }

	    return options;
	  }
	}, jsarray);

	var DataDriver = {
	  json: json,
	  jsarray: jsarray,
	  xml: xml,
	  csv: csv,
	  html: html,
	  htmltable: htmltable,
	  excel: excel
	};

	var storage = {};

	storage.prefix = function (scope, storage) {
	  scope = scope + ".";
	  return {
	    put: function (name, data) {
	      return storage.put(scope + name, data);
	    },
	    get: function (name) {
	      return storage.get(scope + name);
	    },
	    remove: function (name) {
	      return storage.remove(scope + name);
	    }
	  };
	};

	storage.local = {
	  put: function (name, data) {
	    if (name && window.JSON && window.localStorage) {
	      window.localStorage.setItem(name, stringify(data));
	    }
	  },
	  get: function (name) {
	    if (name && window.JSON && window.localStorage) {
	      var json = window.localStorage.getItem(name);
	      if (!json) return null;
	      return DataDriver.json.toObject(json);
	    } else return null;
	  },
	  remove: function (name) {
	    if (name && window.JSON && window.localStorage) {
	      window.localStorage.removeItem(name);
	    }
	  },
	  clear: function () {
	    window.localStorage.clear();
	  }
	};
	storage.session = {
	  put: function (name, data) {
	    if (name && window.JSON && window.sessionStorage) {
	      window.sessionStorage.setItem(name, stringify(data));
	    }
	  },
	  get: function (name) {
	    if (name && window.JSON && window.sessionStorage) {
	      var json = window.sessionStorage.getItem(name);
	      if (!json) return null;
	      return DataDriver.json.toObject(json);
	    } else return null;
	  },
	  remove: function (name) {
	    if (name && window.JSON && window.sessionStorage) {
	      window.sessionStorage.removeItem(name);
	    }
	  },
	  clear: function () {
	    window.sessionStorage.clear();
	  }
	};
	storage.cookie = {
	  put: function (name, data, domain, expires) {
	    if (name && window.JSON) {
	      document.cookie = name + "=" + escape(stringify(data)) + (expires && expires instanceof Date ? ";expires=" + expires.toUTCString() : "") + (domain ? ";domain=" + domain : "") + (env.https ? ";secure" : "");
	    }
	  },
	  getRaw: function (check_name) {
	    // first we'll split this cookie up into name/value pairs
	    // note: document.cookie only returns name=value, not the other components
	    var a_all_cookies = document.cookie.split(";");
	    var a_temp_cookie = "";
	    var cookie_name = "";
	    var cookie_value = "";
	    var b_cookie_found = false; // set boolean t/f default f

	    for (var i = 0; i < a_all_cookies.length; i++) {
	      // now we'll split apart each name=value pair
	      a_temp_cookie = a_all_cookies[i].split("="); // and trim left/right whitespace while we're at it

	      cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, ""); // if the extracted name matches passed check_name

	      if (cookie_name == check_name) {
	        b_cookie_found = true; // we need to handle case where cookie has no value but exists (no = sign, that is):

	        if (a_temp_cookie.length > 1) {
	          cookie_value = unescape(a_temp_cookie[1].replace(/^\s+|\s+$/g, ""));
	        } // note that in cases where cookie is initialized but no value, null is returned


	        return cookie_value;
	      }

	      a_temp_cookie = null;
	      cookie_name = "";
	    }

	    if (!b_cookie_found) {
	      return null;
	    }

	    return null;
	  },
	  get: function (name) {
	    if (name && window.JSON) {
	      var json = this.getRaw(name);
	      if (!json) return null;
	      return DataDriver.json.toObject(unescape(json));
	    } else return null;
	  },
	  remove: function (name, domain) {
	    if (name && this.getRaw(name)) document.cookie = name + "=" + (domain ? ";domain=" + domain : "") + ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
	  },
	  clear: function (domain) {
	    var cookies = document.cookie.split(";");

	    for (var i = 0; i < cookies.length; i++) {
	      document.cookie = /^[^=]+/.exec(cookies[i])[0] + "=" + (domain ? ";domain=" + domain : "") + ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
	    }
	  }
	};

	var proxy$7 = {
	  $proxy: true,
	  storage: storage.local,
	  cache: false,
	  local: false,
	  data: "",
	  _is_offline: function () {
	    if (!this.cache && !env.offline) {
	      callEvent("onOfflineMode", []);
	      env.offline = true;
	    }
	  },
	  _is_online: function () {
	    if (!this.cache && env.offline) {
	      env.offline = false;
	      callEvent("onOnlineMode", []);
	    }
	  },
	  _on_success: function (text) {
	    this._is_online();

	    this.setCache(text);
	  },
	  _on_error: function (view) {
	    //assuming offline mode
	    this._is_offline();

	    var text = this.getCache() || this.data;
	    view.parse(text);
	  },
	  load: function (view) {
	    var _this = this;

	    //in cache mode - always load data from cache
	    if (this.cache && this.getCache()) {
	      this._on_error(view);
	    } //else try to load actual data first
	    else {
	        var result;
	        if (this.source.$proxy) result = this.source.load(view);else result = ajax().get(this.source);

	        if (result && result.then) {
	          result.then(function (data) {
	            _this._on_success(data.text());
	          }, function () {
	            _this._on_error(view);
	          });
	        }

	        return result;
	      }
	  },
	  getCache: function () {
	    return this.storage.get(this._data_name());
	  },
	  clearCache: function () {
	    this.storage.remove(this._data_name());
	  },
	  setCache: function (text) {
	    this.storage.put(this._data_name(), text);
	  },
	  _data_name: function () {
	    if (this.source.$proxy) return this.source.source + "_$proxy$_data";else return this.source + "_$proxy$_data";
	  },
	  save: function (master, data, view) {
	    if (!env.offline && !this.cache) {
	      if (this.source.$proxy) {
	        return this.source.save(master, data, view);
	      } else {
	        return ajax().post(this.source, data.data);
	      }
	    }
	  },
	  saveAll: function (view, update) {
	    this.setCache(view.serialize());
	    update = this.cache || env.offline ? update : [];

	    for (var i = 0; i < update.length; i++) {
	      update[i] = {
	        id: update[i].id,
	        status: update[i].operation
	      };
	    }

	    return Deferred.resolve(update);
	  }
	};

	var proxy$8 = {
	  init: function () {
	    exports.extend(this, proxy$7);
	  },
	  cache: true
	};

	var proxy$9 = {
	  init: function () {
	    exports.extend(this, proxy$7);
	  },
	  cache: true,
	  local: true,
	  data: []
	};

	function unbox(data) {
	  if (!data || !_typeof(data) === "object" || Array.isArray(data)) return data;
	  var lkey = "";
	  var count = 0;

	  for (var key in data) {
	    count++;
	    if (count == 2) return data;
	    lkey = key;
	  }

	  return data[lkey];
	}

	var GraphQL = {
	  $proxy: true,
	  save: function (data) {
	    return this.load(data);
	  },
	  load: function (view) {
	    var params = {
	      query: this.source
	    };

	    if (arguments.length === 1) {
	      params.variables = view;
	    }

	    return ajax().headers({
	      "Content-type": "application/json"
	    }).post(this.url, params).then(function (data) {
	      return unbox(data.json().data);
	    });
	  }
	};

	function proxy$a(name, source, extra) {
	  assert(proxy$a[name], "Invalid proxy name: " + name);
	  var copy$$1 = copy(proxy$a[name]);
	  copy$$1.source = source;
	  if (extra) exports.extend(copy$$1, extra, true);
	  if (copy$$1.init) copy$$1.init();
	  return copy$$1;
	}

	proxy$a.$parse = function (value) {
	  if (typeof value == "string" && value.indexOf("->") != -1) {
	    var parts = value.split("->");
	    return proxy$a(parts[0], parts[1]);
	  }

	  return value;
	};

	proxy$a.binary = proxy;
	proxy$a.connector = proxy$1;
	proxy$a.debug = proxy$2;
	proxy$a.faye = proxy$3;
	proxy$a.indexdb = indexdb;
	proxy$a.json = proxy$5;
	proxy$a.post = proxy$6;
	proxy$a.rest = proxy$4;
	proxy$a.offline = proxy$7;
	proxy$a.cache = proxy$8;
	proxy$a.local = proxy$9;
	proxy$a.local = proxy$9;
	proxy$a.GraphQL = GraphQL;
    proxy$a.socketio = proxy$10;

	var AtomDataLoader = {
	  $init: function (config) {
	    //prepare data store
	    this.data = {};
	    this.waitData = Deferred.defer();
	    if (config) this._settings.datatype = config.datatype || "json";
	    this.$ready.push(this._load_when_ready);
	  },
	  _load_when_ready: function () {
	    this._ready_for_data = true;
	    if (this._settings.url) this.url_setter(this._settings.url);
	    if (this._settings.data) this.data_setter(this._settings.data);
	  },
	  url_setter: function (value) {
	    value = proxy$a.$parse(value);
	    if (!this._ready_for_data) return value;
	    this.load(value, this._settings.datatype);
	    return value;
	  },
	  data_setter: function (value) {
	    if (!this._ready_for_data) return value;
	    this.parse(value, this._settings.datatype);
	    return true;
	  },
	  //loads data from external URL
	  load: function (url, call) {
	    var _this = this;

	    var details = arguments[2] || null;
	    if (!this.callEvent("onBeforeLoad", [])) return Deferred.reject();

	    if (typeof call == "string") {
	      //second parameter can be a loading type or callback
	      //we are not using setDriver as data may be a non-datastore here
	      this.data.driver = DataDriver[call];
	      call = arguments[2];
	    } else if (!this.data.driver) this.data.driver = DataDriver.json;

	    var result; //proxy	

	    url = proxy$a.$parse(url);

	    if (url.$proxy && url.load) {
	      result = url.load(this, details);
	    } //promize
	    else if (typeof url === "function") {
	        result = url.call(this, details);
	      } //normal url
	      else {
	          result = ajax().bind(this).get(url);
	        } //we wrap plain data in promise to keep the same processing for it


	    if (result && !result.then) {
	      result = Deferred.resolve(result);
	    }

	    var gen = this._data_generation;

	    if (result && result.then) {
	      result.then(function (data) {
	        // component destroyed, or clearAll was issued
	        if (_this.$destructed || gen && _this._data_generation !== gen) // by returning rejection we are preventing the further executing chain
	          // if user have used list.load(data).then(do_something)
	          // the do_something will not be executed
	          // the error handler may be triggered though
	          return Deferred.reject();

	        _this._onLoad(data);

	        if (call) ajax.$callback(_this, call, "", data, -1);
	      }, function (x) {
	        return _this._onLoadError(x, gen);
	      });
	    }

	    return result;
	  },
	  //loads data from object
	  parse: function (data, type) {
	    if (data && typeof data.then == "function") {
	      var generation = this._data_generation; // component destroyed, or clearAll was issued

	      return data.then(bind(function (data) {
	        if (this.$destructed || generation && this._data_generation !== generation) return Deferred.reject();
	        this.parse(data, type);
	      }, this));
	    } //loading data from other component


	    if (data && data.sync && this.sync) this._syncData(data);else if (!this.callEvent("onBeforeLoad", [])) return Deferred.reject();else {
	      this.data.driver = DataDriver[type || "json"];

	      this._onLoad(data);
	    }
	    return Deferred.resolve();
	  },
	  _syncData: function (data) {
	    if (this.data) this.data.attachEvent("onSyncApply", bind(function () {
	      if (this._call_onready) this._call_onready();
	    }, this));
	    this.sync(data);
	  },
	  _parse: function (data) {
	    var parsed,
	        record,
	        driver = this.data.driver;
	    record = driver.getRecords(data)[0];
	    parsed = record ? driver.getDetails(record) : {};
	    if (this.setValues) this.setValues(parsed);else this.data = parsed;
	  },
	  _onLoadContinue: function (data) {
	    if (data) {
	      if (!this.$onLoad || !this.$onLoad(data, this.data.driver)) {
	        if (this.data && this.data._parse) this.data._parse(data); //datastore
	        else this._parse(data);
	      }
	    } else this._onLoadError(data); //data loaded, view rendered, call onready handler


	    if (this._call_onready) this._call_onready();
	    this.callEvent("onAfterLoad", []);
	    this.waitData.resolve();
	  },
	  //default after loading callback
	  _onLoad: function (data) {
	    var _this2 = this;

	    if (data && typeof data.text === "function") {
	      data = data.text();
	    }

	    data = this.data.driver.toObject(data);
	    if (data && data.then) data.then(function (data) {
	      return _this2._onLoadContinue(data);
	    });else this._onLoadContinue(data);
	  },
	  _onLoadError: function (xhttp, generation) {
	    //ignore error for dead components
	    if (!this.$destructed && (!generation || this._data_generation === generation)) {
	      this.callEvent("onAfterLoad", []);
	      this.callEvent("onLoadError", arguments);
	    }

	    callEvent("onLoadError", [xhttp, this]);
	    return Deferred.reject(xhttp);
	  },
	  _check_data_feed: function (data) {
	    if (!this._settings.dataFeed || this._ignore_feed || !data) return true;
	    var url = this._settings.dataFeed;
	    if (typeof url == "function") return url.call(this, data.id || data, data);
	    url = url + (url.indexOf("?") == -1 ? "?" : "&") + "action=get&id=" + encodeURIComponent(data.id || data);
	    if (!this.callEvent("onBeforeLoad", [])) return false;
	    ajax(url, function (text, xml, loader) {
	      this._ignore_feed = true;
	      var driver = DataDriver.json;
	      var data = driver.toObject(text, xml);
	      if (data) this.setValues(driver.getDetails(driver.getRecords(data)[0]));else this._onLoadError(loader);
	      this._ignore_feed = false;
	      this.callEvent("onAfterLoad", []);
	    }, this);
	    return false;
	  }
	};

	var AtomRender = {
	  //convert item to the HTML text
	  _toHTML: function (obj) {
	    if (obj.$empty) return "";
	    return this._settings.template(obj, this);
	  },
	  //render self, by templating data object
	  render: function () {
	    var cfg = this._settings;

	    if (this.isVisible(cfg.id)) {
	      if (!this.callEvent || this.callEvent("onBeforeRender", [this.data])) {
	        if (this.data && !cfg.content) {
	          //it is critical to have this as two commands
	          //its prevent destruction race in Chrome
	          this._dataobj.innerHTML = "";
	          this._dataobj.innerHTML = this._toHTML(this.data);
	        }

	        if (this.callEvent) this.callEvent("onAfterRender", []);
	      }

	      return true;
	    }

	    return false;
	  },
	  sync: function (source) {
	    this._backbone_sync = false;

	    if (source.name != "DataStore") {
	      if (source.data && source.name == "DataStore") {
	        source = source.data;
	      } else {
	        this._backbone_sync = true;
	      }
	    }

	    if (this._backbone_sync) source.bind("change", bind(function (data) {
	      if (data.id == this.data.id) {
	        this.data = data.attributes;
	        this.refresh();
	      }
	    }, this));else source.attachEvent("onStoreUpdated", bind(function (id) {
	      if (!id || id == this.data.id) {
	        this.data = source.pull[id];
	        this.refresh();
	      }
	    }, this));
	  },
	  template_setter: template
	};

	var AutoScroll = {
	  _auto_scroll: function (pos$$1) {
	    var yscroll = 1;
	    var xscroll = 0;
	    var mode = this._settings.dragscroll;

	    if (typeof mode == "string") {
	      xscroll = mode.indexOf("x") != -1;
	      yscroll = mode.indexOf("y") != -1;
	    }

	    var data = this._body || this.$view;
	    var box = offset(data);
	    var top = box.y;
	    var bottom = top + data.offsetHeight;
	    var left = box.x;
	    var right = left + data.offsetWidth;
	    var scroll = this.getScrollState();
	    var reset = false;
	    var sense = Math.max(this.type && !isNaN(parseFloat(this.type.height)) ? this.type.height + 5 : 0, 40); //dnd auto-scroll sensivity

	    if (yscroll) {
	      var config = this._settings;

	      if (config.topSplit) {
	        var topSplitPos = this._cellPosition(this.getIdByIndex(config.topSplit - 1), this.columnId(0));

	        top += topSplitPos.top + topSplitPos.height;
	      }

	      if (pos$$1.y < top + sense) {
	        this._auto_scrollTo(scroll.x, scroll.y - sense * 2, pos$$1);

	        reset = true;
	      } else if (pos$$1.y > bottom - sense) {
	        this._auto_scrollTo(scroll.x, scroll.y + sense * 2, pos$$1);

	        reset = true;
	      }
	    }

	    if (xscroll) {
	      if (pos$$1.x < left + sense) {
	        this._auto_scrollTo(scroll.x - sense * 2, scroll.y, pos$$1);

	        reset = true;
	      } else if (pos$$1.x > right - sense) {
	        this._auto_scrollTo(scroll.x + sense * 2, scroll.y, pos$$1);

	        reset = true;
	      }
	    }

	    if (reset) this._auto_scroll_delay = delay(this._auto_scroll, this, [pos$$1], 100);
	  },
	  _auto_scrollTo: function (x, y, pos$$1) {
	    if (this.callEvent("onBeforeAutoScroll", [pos$$1])) this.scrollTo(x, y);
	  }
	};

	var TooltipControl = {
	  _tooltip_masters: toArray(["dummy"]),
	  _tooltip_exist: 0,
	  delay: 400,
	  addTooltip: function (target, config) {
	    var _this = this;

	    var node, ctrl;
	    target = toNode(target);
	    assert(target, "Target isn't defined");

	    if (target instanceof Element) {
	      node = target;
	      if (typeof config === "string") node.setAttribute("webix_tooltip", config);else ctrl = config;
	    } else {
	      node = target.$view;
	      ctrl = target;
	    }

	    ctrl = ctrl || this;

	    var index$$1 = this._tooltip_masters.find(ctrl);

	    if (index$$1 === -1) {
	      index$$1 = this._tooltip_masters.length;

	      this._tooltip_masters.push(ctrl);
	    }

	    node.webix_tooltip = index$$1;
	    this._tooltip_exist++;

	    if (!this._tooltip) {
	      this._tooltip = new ui.tooltip({});
	      this._webix_tooltip_mm = event$1(document, "mousemove", this._move_tooltip, {
	        bind: this
	      });
	      this._webix_tooltip_ml = event$1(document, "mouseleave", this._hide_tooltip, {
	        bind: this
	      });
	      this._drag_event = attachEvent("onDragMode", function () {
	        _this._hide_tooltip();
	      });
	      this._click_event = attachEvent("onClick", function () {
	        _this._hide_tooltip();
	      });
	    }
	  },
	  getTooltip: function () {
	    return this._tooltip;
	  },
	  _move_tooltip: function (e) {
	    var node = e.target || e.srcElement;
	    var text;

	    while (node instanceof Element && node.tagName != "HTML") {
	      if (this._tooltip_masters[node.webix_tooltip]) {
	        if (this._last && this._last != node) {
	          this.$tooltipOut(this._last, node, e);
	          this._last = null;
	          return;
	        }

	        if (!this._last) this._last = this.$tooltipIn(node, e);
	        this.$tooltipMove(node, e, text);
	        return;
	      }

	      text = text || node.getAttribute("webix_tooltip");
	      node = node.parentElement;
	    }

	    if (this._last) this._last = this.$tooltipOut(this._last, null, e);
	  },
	  _hide_tooltip: function () {
	    clearTimeout(this._before_show_delay);

	    this._tooltip.hide();
	  },
	  getMaster: function (t) {
	    return this._tooltip_masters[t.webix_tooltip];
	  },
	  removeTooltip: function (target) {
	    var node;
	    assert(target, "Target isn't defined");
	    target = toNode(target);
	    if (target instanceof Element) node = target;else node = target.$view;

	    if (node.webix_tooltip) {
	      if (this._last == node) {
	        this._hide_tooltip();

	        this._last = null;
	      }

	      delete node.webix_tooltip;
	      this._tooltip_exist--;
	    }

	    if (!this._tooltip_exist && this._tooltip) {
	      // detach events first
	      this._webix_tooltip_mm = eventRemove(this._webix_tooltip_mm);
	      this._webix_tooltip_ml = eventRemove(this._webix_tooltip_ml);
	      this._drag_event = detachEvent(this._drag_event);
	      this._click_event = detachEvent(this._click_event); // then destroy the tooltip

	      this._tooltip.destructor();

	      this._tooltip = this._last = null;
	      this._tooltip_masters = toArray(["dummy"]);
	    }
	  },
	  $tooltipIn: function (t, e) {
	    var m = this._tooltip_masters[t.webix_tooltip];
	    if (m.$tooltipIn && m != this) return m.$tooltipIn(t, e);

	    this._tooltip.define({
	      dx: 20,
	      dy: 0,
	      template: t.getAttribute("webix_tooltip") || "",
	      css: ""
	    });

	    return t;
	  },
	  $tooltipOut: function (t, n, e) {
	    var m = this._tooltip_masters[t.webix_tooltip];
	    if (m.$tooltipOut && m != this) return m.$tooltipOut(t, n, e);

	    this._hide_tooltip();

	    return null;
	  },
	  $tooltipMove: function (t, e, text) {
	    var m = this._tooltip_masters[t.webix_tooltip];
	    if (m.$tooltipMove && m != this) return m.$tooltipMove(t, e, text);

	    this._tooltip.hide();

	    clearTimeout(this._before_show_delay);
	    this._before_show_delay = delay(this._tooltip.show, this._tooltip, [text || {}, pos(e)], this.delay);
	  }
	};

	function _uid(name) {
	  return "$" + name + (_namecount[name] = (_namecount[name] || 0) + 1);
	}
	var _namecount = {};
	var _freeze_resize = false;
	function freeze(handler, trigger) {
	  _freeze_resize = true;
	  var res = handler();

	  if (res && res.then) {
	    res = res.then(function (any) {
	      _freeze_resize = false;
	      if (trigger !== false) resize();
	      return any;
	    });
	  } else {
	    _freeze_resize = false;
	    if (trigger !== false) resize();
	  }

	  return res;
	}
	function resize() {
	  use("UIManager").applyChanges();
	  callEvent("onClick", []);
	  state._force_resize = true;
	  if (!_freeze_resize) for (var i = state.top_views.length - 1; i >= 0; i--) {
	    if (state.top_views[i].obj) state.top_views[i].obj.resize();
	  }
	  state._force_resize = false;
	}
	function each(parent, logic, master, include) {
	  if (parent) {
	    var children = include ? [parent] : parent.getChildViews();

	    for (var i = 0; i < children.length; i++) {
	      if (logic.call(master, children[i]) !== false) each(children[i], logic, master);
	    }
	  }
	}
	function zIndex() {
	  return env.zIndexBase++;
	}
	event$1(window, "resize", function () {
	  // check for virtual keyboard
	  if (env.touch && (state._focus_time && new Date() - state._focus_time < 1000)) {
	    return;
	  } else {
	    resize();
	  }
	});

	function ready(code) {
	  if (_ready) code.call();else _ready_code.push(code);
	}

	var _ready = false;
	var _ready_code = []; //autodetect codebase folder

	var temp = document.getElementsByTagName("SCRIPT"); //current script, most probably

	assert(temp.length, "Can't locate codebase");

	if (temp.length) {
	  //full path to script
	  temp = (temp[temp.length - 1].getAttribute("src") || "").split("/"); //get folder name

	  temp.splice(temp.length - 1, 1);
	  env.codebase = temp.slice(0, temp.length).join("/") + "/";
	}

	var handler = function () {
	  if (env.isIE) document.body.className += " webix_ie";
	  callEvent("onReady", []);
	};

	var doit = function () {
	  _ready = true;
	  /* global webix_ready */

	  if (window.webix_ready && isArray(webix_ready)) _ready_code = webix_ready.concat(_ready_code);

	  for (var i = 0; i < _ready_code.length; i++) {
	    _ready_code[i].call();
	  }

	  _ready_code = [];
	};

	attachEvent("onReady", function (force) {
	  if (force) doit();else delay(doit);
	});
	if (document.readyState == "complete") handler();else event$1(window, "load", handler);
	ready(function () {
	  event$1(document.body, "click", function (e) {
	    callEvent("onClick", [e || event$1]);
	  });
	});

	var UIManager = {
	  _view: null,
	  _hotkeys: {},
	  _focus_time: 0,
	  _tab_time: 0,
	  _mouse_time: 0,
	  _controls: {
	    "enter": 13,
	    "tab": 9,
	    "esc": 27,
	    "escape": 27,
	    "up": 38,
	    "down": 40,
	    "left": 37,
	    "right": 39,
	    "pgdown": 34,
	    "pagedown": 34,
	    "pgup": 33,
	    "pageup": 33,
	    "end": 35,
	    "home": 36,
	    "insert": 45,
	    "delete": 46,
	    "backspace": 8,
	    "space": 32,
	    "meta": 91,
	    "win": 91,
	    "mac": 91,
	    "multiply": 106,
	    "add": 107,
	    "subtract": 109,
	    "decimal": 110,
	    "divide": 111,
	    "scrollock": 145,
	    "pausebreak": 19,
	    "numlock": 144,
	    "5numlocked": 12,
	    "shift": 16,
	    "capslock": 20
	  },
	  _inputs: {
	    "input": 1,
	    "button": 1,
	    "textarea": 1,
	    "select": 1
	  },
	  _enable: function () {
	    // attaching events here
	    event$1(document.body, "click", bind(this._focus_click, this));
	    event$1(document, "keydown", bind(this._keypress, this));

	    _event(document.body, "mousedown", bind(function () {
	      this._mouse_time = new Date();
	    }, this));

	    if (document.body.addEventListener) event$1(document.body, "focus", this._focus_tab, {
	      capture: true,
	      bind: this
	    });
	    state.destructors.push({
	      obj: this
	    });
	  },
	  destructor: function () {
	    UIManager._view = null;
	  },
	  getFocus: function () {
	    return this._view;
	  },
	  _focus_action: function (view) {
	    this._focus_was_there = this._focus_was_there || view._settings.id;
	  },
	  setFocus: function (view, only_api, tab) {
	    //view can be empty
	    view = $$(view); //unfocus if view is hidden

	    if (view && !view.$view) view = null; //store last click time, it is necessary to prevent refocusing
	    //for example when user moves focus from onclick handler somewher
	    //and we want to prevent autofocusing, when event will reach document.body

	    this._focus_time = state._focus_time = new Date();
	    if (this._view === view) return true;
	    if (this._view && this._view.callEvent) this._view.callEvent("onBlur", [this._view]);

	    if (view && view.callEvent) {
	      view.callEvent("onFocus", [view, this._view]);
	      if (tab) view.callEvent("onTabFocus", [view, this._view]);
	    }

	    callEvent("onFocusChange", [view, this._view]);
	    if (this._view && this._view.blur && !only_api) this._view.blur();
	    this._view = view;
	    if (view && view.focus && !only_api) view.focus();
	    return true;
	  },
	  applyChanges: function (element) {
	    var view = this.getFocus();
	    if (view && view != element && view._applyChanges) view._applyChanges(element);
	  },
	  hasFocus: function (view) {
	    return view === this._view ? true : false;
	  },
	  _focus: function (e, dont_clear) {
	    var view = locate(e,
	    /*@attr*/
	    "view_id") || this._focus_was_there; //if html was repainted we can miss the view, so checking last processed one


	    view = $$(view);
	    this._focus_was_there = null; //set timer, to fix issue with Android input focusin

	    state._focus_time = new Date();
	    if (view == this._view) return;
	    if (!dont_clear) this._focus_was_there = null;

	    if (view) {
	      view = $$(view);

	      if (this.canFocus(view)) {
	        //[ACTIVECONTENT] focus operations for active content
	        if (view.getNode) view.getNode(e);
	        this.setFocus(view);
	      }
	    } else if (!dont_clear) this.setFocus(null);

	    return true;
	  },
	  _focus_click: function (e) {
	    // if it was onfocus/onclick less then 100ms behore then we ignore it
	    if (new Date() - this._focus_time < 100) {
	      this._focus_was_there = null;
	      return false;
	    }

	    return this._focus(e);
	  },
	  _focus_tab: function (e) {
	    if (!this._inputs[e.target.nodeName.toLowerCase()]) return false;
	    return this._focus(e, true);
	  },
	  canFocus: function (view) {
	    return view.isVisible() && view.isEnabled() && !view.queryView({
	      disabled: true
	    }, "parent");
	  },
	  _moveChildFocus: function (check_view) {
	    var focus = this.getFocus(); //we have not focus inside of closing item

	    if (check_view && !this._is_child_of(check_view, focus)) return false;
	    if (!this._focus_logic("getPrev", check_view)) this._view = null;
	  },
	  _translation_table: {},
	  _is_child_of: function (parent, child) {
	    if (!parent) return false;
	    if (!child) return false;

	    while (child) {
	      if (child === parent) return true;
	      child = child.getParentView();
	    }

	    return false;
	  },
	  _keypress_timed: function () {
	    if (this && this.callEvent) this.callEvent("onTimedKeyPress", []);
	  },
	  _isNumPad: function (code) {
	    return code < 112 && code > 105;
	  },
	  _keypress: function (e) {
	    var code = e.which || e.keyCode;
	    if (code > 95 && code < 106) code -= 48; //numpad support (numbers)

	    code = this._translation_table[code] || code;
	    var ctrl = e.ctrlKey;
	    var shift = e.shiftKey;
	    var alt = e.altKey;
	    var meta = e.metaKey;

	    var codeid = this._keycode(code, ctrl, shift, alt, meta);

	    var view = this.getFocus();

	    if (view && view.callEvent) {
	      if (view.callEvent("onKeyPress", [code, e]) === false) preventEvent(e);

	      if (view.hasEvent("onTimedKeyPress")) {
	        clearTimeout(view._key_press_timeout);
	        view._key_press_timeout = delay(this._keypress_timed, view, [], view._settings.keyPressTimeout || 250);
	      }
	    }

	    if (!this._isNumPad(code)) codeid = this._keycode(String.fromCharCode(code), ctrl, shift, alt, meta); //flag, that some non-special key was pressed

	    var is_any = !ctrl && !alt && !meta && code != 9 && code != 27 && code != 13;

	    if (this._check_keycode(codeid, is_any, e) === false) {
	      preventEvent(e);
	      return false;
	    }
	  },
	  // dir - getNext or getPrev
	  _focus_logic: function (dir) {
	    if (!this.getFocus()) return null;
	    dir = dir || "getNext";
	    var next = this.getFocus();
	    var start = next;
	    var marker = uid();

	    while (true) {
	      // eslint-disable-line
	      next = this[dir](next); // view with focus ability

	      if (next && this.canFocus(next)) return this.setFocus(next); // elements with focus ability not found

	      if (next === start || next.$fmarker == marker) return null; //prevents infinity loop

	      next.$fmarker = marker;
	    }
	  },
	  _tab_logic: function (view, e) {
	    var mode = !e.shiftKey;
	    UIManager._tab_time = new Date();
	    if (view && view._custom_tab_handler && !view._custom_tab_handler(mode, e)) return false;

	    if (view && view._in_edit_mode) {
	      if (view.editNext) return view.editNext(mode);else if (view.editStop) {
	        view.editStop();
	        return true;
	      }
	    } else delay(function () {
	      UIManager.setFocus($$(document.activeElement), true, true);
	    }, 1);
	  },
	  getTop: function (id) {
	    var next,
	        view = $$(id);

	    while (view && (next = view.getParentView())) {
	      view = next;
	    }

	    return view;
	  },
	  getNext: function (view, _inner_call) {
	    var cells = view.getChildViews(); //tab to first children

	    if (cells.length && !_inner_call) return cells[0]; //unique case - single view without child and parent

	    var parent = view.getParentView();
	    if (!parent) return view;
	    var p_cells = parent.getChildViews();

	    if (p_cells.length) {
	      var index$$1 = PowerArray.find.call(p_cells, view) + 1;

	      while (index$$1 < p_cells.length) {
	        //next visible child
	        if (this.canFocus(p_cells[index$$1])) return p_cells[index$$1];
	        index$$1++;
	      }
	    } //sibling of parent


	    return this.getNext(parent, true);
	  },
	  getPrev: function (view, _inner_call) {
	    var cells = view.getChildViews(); //last child of last child

	    if (cells.length && _inner_call) return this.getPrev(cells[cells.length - 1], true);
	    if (_inner_call) return view; //fallback from top to bottom

	    var parent = view.getParentView();
	    if (!parent) return this.getPrev(view, true);
	    var p_cells = parent.getChildViews();

	    if (p_cells) {
	      var index$$1 = PowerArray.find.call(p_cells, view) - 1;

	      while (index$$1 >= 0) {
	        if (this.canFocus(p_cells[index$$1])) return this.getPrev(p_cells[index$$1], true);
	        index$$1--;
	      }
	    }

	    return parent;
	  },
	  addHotKey: function (keys, handler, view) {
	    assert(handler, "Hot key handler is not defined");

	    var pack = this._parse_keys(keys);

	    assert(pack.letter, "Unknown key code");
	    if (!view) view = null;
	    pack.handler = handler;
	    pack.view = view;

	    var code = this._keycode(pack.letter, pack.ctrl, pack.shift, pack.alt, pack.meta);

	    if (!this._hotkeys[code]) this._hotkeys[code] = [];

	    this._hotkeys[code].push(pack);

	    return keys;
	  },
	  removeHotKey: function (keys, func, view) {
	    var pack = this._parse_keys(keys);

	    var code = this._keycode(pack.letter, pack.ctrl, pack.shift, pack.alt, pack.meta);

	    if (!func && !view) delete this._hotkeys[code];else {
	      var t = this._hotkeys[code];

	      if (t) {
	        for (var i = t.length - 1; i >= 0; i--) {
	          if (view && t[i].view !== view) continue;
	          if (func && t[i].handler !== func) continue;
	          t.splice(i, 1);
	        }

	        if (!t.length) delete this._hotkeys[code];
	      }
	    }
	  },
	  _keycode: function (code, ctrl, shift, alt, meta) {
	    return code + "_" + ["", ctrl ? "1" : "0", shift ? "1" : "0", alt ? "1" : "0", meta ? "1" : "0"].join("");
	  },
	  _check_keycode: function (code, is_any, e) {
	    var focus = this.getFocus();
	    if (this._hotkeys[code]) return this._process_calls(this._hotkeys[code], focus, e);else if (is_any && this._hotkeys["ANY_0000"]) return this._process_calls(this._hotkeys["ANY_0000"], focus, e);
	    return true;
	  },
	  _process_calls: function (calls, focus, e) {
	    for (var i = 0; i < calls.length; i++) {
	      var key = calls[i];
	      if (key.view !== null && //common hot-key
	      focus !== key.view && ( //hot-key for current view
	      //hotkey for current type of view
	      typeof key.view !== "string" || !focus || focus.name !== key.view)) continue;
	      var temp_result = key.handler(focus, e);
	      if (!!temp_result === temp_result) return temp_result;
	    }

	    return true;
	  },
	  _parse_keys: function (keys) {
	    var controls = this._controls;
	    var parts = keys.toLowerCase().split(/[+\-_]/);
	    var ctrl, shift, alt, meta;
	    ctrl = shift = alt = meta = 0;
	    var letter = "";

	    for (var i = 0; i < parts.length; i++) {
	      if (parts[i] === "ctrl") ctrl = 1;else if (parts[i] === "shift") shift = 1;else if (parts[i] === "alt") alt = 1;else if (parts[i] === "command") meta = 1;else {
	        if (controls[parts[i]]) {
	          var code = controls[parts[i]];
	          if (this._isNumPad(code)) letter = code.toString();else letter = String.fromCharCode(code);
	        } else {
	          letter = parts[i];
	        }
	      }
	    }

	    return {
	      letter: letter.toUpperCase(),
	      ctrl: ctrl,
	      shift: shift,
	      alt: alt,
	      meta: meta,
	      debug: keys
	    };
	  },
	  getState: function (node, children) {
	    children = children || false;
	    node = $$(node);
	    var state$$1 = {
	      id: node.config.id,
	      width: node.config.width,
	      height: node.config.height,
	      gravity: node.config.gravity
	    };
	    if (!isUndefined(node.config.collapsed)) state$$1.collapsed = node.config.collapsed;
	    if (node.name === "tabs" || node.name === "tabbar") state$$1.activeCell = node.getValue();

	    if (children) {
	      state$$1 = [state$$1];

	      if (node._cells) {
	        for (var i = 0; i < node._cells.length; i++) {
	          state$$1 = state$$1.concat(this.getState(node._cells[i], children));
	        }
	      }
	    }

	    return state$$1;
	  },
	  setState: function (states) {
	    if (!isArray(states)) states = [states];

	    for (var i = 0; i < states.length; i++) {
	      var state$$1 = states[i];
	      var node = $$(state$$1.id);
	      if (!node) continue;
	      if (!isUndefined(state$$1.collapsed)) node.define("collapsed", state$$1.collapsed);
	      if (!isUndefined(state$$1.activeCell)) node.setValue(state$$1.activeCell);
	      node.define("width", state$$1.width);
	      node.define("height", state$$1.height);
	      node.define("gravity", state$$1.gravity);
	    }

	    var top = $$(states[0].id);
	    if (top) top.resize();
	  }
	};
	ready(function () {
	  UIManager._enable();

	  UIManager.addHotKey("enter", function (view, ev) {
	    if (view && view.callEvent) view.callEvent("onEnter", [ev]);

	    if (view && view.editStop && view._in_edit_mode) {
	      view.editStop();
	      return true;
	    } else if (view && view.touchable) {
	      var form = view.getFormView();
	      if (form && !view._skipSubmit) form.callEvent("onSubmit", [view, ev]);
	    }
	  });
	  UIManager.addHotKey("esc", function (view) {
	    if (view) {
	      if (view.editCancel && view._in_edit_mode) {
	        view.editCancel();
	        return true;
	      }

	      var top = view.getTopParentView();
	      if (top && top.setPosition) top._hide();
	    }
	  });
	  UIManager.addHotKey("shift+tab", UIManager._tab_logic);
	  UIManager.addHotKey("tab", UIManager._tab_logic);
	});
	define("UIManager", UIManager);

	var Settings = {
	  $init: function () {
	    /* 
	    	property can be accessed as this.config.some
	    	in same time for inner call it have sense to use _settings
	    	because it will be minified in final version
	    */
	    this._settings = this.config = {};
	  },
	  define: function (property, value) {
	    if (_typeof(property) == "object") return this._parseSeetingColl(property);
	    return this._define(property, value);
	  },
	  _define: function (property, value) {
	    //method with name {prop}_setter will be used as property setter
	    //setter is optional
	    var setter = this[property + "_setter"];
	    return this._settings[property] = setter ? setter.call(this, value, property) : value;
	  },
	  //process configuration object
	  _parseSeetingColl: function (coll) {
	    if (coll) {
	      for (var a in coll) {
	        //for each setting
	        this._define(a, coll[a]);
	      } //set value through config

	    }
	  },
	  //helper for object initialization
	  _parseSettings: function (obj, initial) {
	    //initial - set of default values
	    var settings = {};
	    if (initial) settings = exports.extend(settings, initial); //code below will copy all properties over default one

	    if (_typeof(obj) == "object" && !obj.tagName) exports.extend(settings, obj, true); //call config for each setting

	    this._parseSeetingColl(settings);
	  },
	  _mergeSettings: function (config, defaults) {
	    for (var key in defaults) {
	      switch (_typeof(config[key])) {
	        case "object":
	          config[key] = this._mergeSettings(config[key] || {}, defaults[key]);
	          break;

	        case "undefined":
	          config[key] = defaults[key];
	          break;

	        default:
	          //do nothing
	          break;
	      }
	    }

	    return config;
	  }
	};

	var Destruction = {
	  $init: function () {
	    //wrap in object to simplify removing self-reference
	    var t = this._destructor_handler = {
	      obj: this
	    }; //register self in global list of destructors

	    state.destructors.push(t);
	  },
	  //will be called automatically on unload, can be called manually
	  //simplifies job of GC
	  destructor: function () {
	    var config = this._settings;
	    if (this._last_editor) this.editCancel();
	    if (this.callEvent) this.callEvent("onDestruct", []); //destructor can be called only once

	    this.destructor = function () {}; //remove self reference from global destructions collection


	    this._destructor_handler.obj = null; //destroy child and related cells

	    if (this.getChildViews) {
	      var cells = this.getChildViews();
	      if (cells) for (var i = 0; i < cells.length; i++) {
	        cells[i].destructor();
	      }
	      if (this._destroy_with_me) for (var _i = 0; _i < this._destroy_with_me.length; _i++) {
	        this._destroy_with_me[_i].destructor();
	      }
	    }

	    delete ui.views[config.id];

	    if (config.$id) {
	      var top = this.getTopParentView();
	      if (top && top._destroy_child) top._destroy_child(config.$id);
	    } //html collection


	    this._htmlmap = null;
	    this._htmlrows = null;
	    this._html = null;

	    if (this._contentobj) {
	      this._contentobj.innerHTML = "";
	      this._contentobj._htmlmap = null;
	    } //removes view container


	    if (this._viewobj && this._viewobj.parentNode) {
	      this._viewobj.parentNode.removeChild(this._viewobj);
	    }

	    if (this.data && this.data.destructor) this.data.destructor();
	    if (this.unbind) this.unbind();
	    this.data = null;
	    this._viewobj = this.$view = this._contentobj = this._dataobj = null;
	    this._evs_events = this._evs_handlers = {}; //remove focus from destructed view

	    if (UIManager._view == this) UIManager._view = null;
	    var url = config.url;
	    if (url && url.$proxy && url.release) url.release();
	    this.$scope = null; // this flag is checked in delay method

	    this.$destructed = true;
	  }
	}; //global list of destructors

	event$1(window, "unload", function () {
	  callEvent("unload", []);
	  state._final_destruction = true; //call all registered destructors

	  for (var i = 0; i < state.destructors.length; i++) {
	    var obj = state.destructors[i].obj;
	    if (obj) obj.destructor();
	  }

	  state.destructors = [];
	  state._popups = toArray();

	  _events_final_destructor();
	});

	var CollectionBind = {
	  $init: function () {
	    this._cursor = null;
	    this.attachEvent("onSelectChange", function () {
	      var sel = this.getSelectedId();
	      this.setCursor(sel ? sel.id || sel : null);
	    });
	    this.attachEvent("onAfterCursorChange", this._update_binds);
	    this.attachEvent("onAfterDelete", function (id) {
	      if (id == this.getCursor()) this.setCursor(null);
	    });
	    this.data.attachEvent("onStoreUpdated", bind(function (id, data, mode) {
	      //paint - ignored
	      //delete - handled by onAfterDelete above
	      if (id && id == this.getCursor() && mode != "paint" && mode != "delete") this._update_binds();
	    }, this));
	    this.data.attachEvent("onClearAll", bind(function () {
	      this._cursor = null;
	    }, this));
	    this.data.attachEvent("onIdChange", bind(function (oldid, newid) {
	      if (this._cursor == oldid) {
	        this._cursor = newid;

	        this._update_binds();
	      }
	    }, this));
	  },
	  refreshCursor: function () {
	    if (this._cursor) this.callEvent("onAfterCursorChange", [this._cursor]);
	  },
	  setCursor: function (id) {
	    if (id == this._cursor || id !== null && !this.getItem(id)) return;
	    this.callEvent("onBeforeCursorChange", [this._cursor]);
	    this._cursor = id;
	    this.callEvent("onAfterCursorChange", [id]);
	  },
	  getCursor: function () {
	    return this._cursor;
	  },
	  _bind_update: function (target, rule, format) {
	    if (rule == "$level" && this.data.getBranch) return (target.data || target).importData(this.data.getBranch(this.getCursor()));
	    var data = this.getItem(this.getCursor()) || this._settings.defaultData || null;

	    if (rule == "$data") {
	      if (typeof format === "function") format.call(target, data, this);else target.data.importData(data ? data[format] : []);
	      target.callEvent("onBindApply", [data, rule, this]);
	    } else {
	      if (format) data = format(data);

	      this._bind_update_common(target, rule, data);
	    }
	  }
	};

	var ValueBind = {
	  $init: function () {
	    this.attachEvent("onChange", this._update_binds);
	  },
	  _bind_update: function (target, rule, format) {
	    rule = rule || "value";
	    var data = this.getValue() || "";
	    if (format) data = format(data);
	    if (target.setValue) target.setValue(data);else if (!target.filter) {
	      var pod = {};
	      pod[rule] = data;
	      if (target._check_data_feed(data)) target.setValues(pod);
	    } else {
	      target.data.silent(function () {
	        this.filter(rule, data);
	      });
	    }
	    target.callEvent("onBindApply", [data, rule, this]);
	  }
	};

	var RecordBind = {
	  $init: function () {
	    this.attachEvent("onChange", this._update_binds);
	  },
	  _bind_update: function (target, rule, format) {
	    var data = this.getValues() || null;
	    if (format) data = format(data);

	    this._bind_update_common(target, rule, data);
	  }
	};

	var BindSource = {
	  $init: function () {
	    this._bind_hash = {}; //rules per target

	    this._bind_updated = {}; //update flags

	    this._ignore_binds = {}; //apply specific bind extension

	    this._bind_specific_rules(this);
	  },
	  saveBatch: function (code) {
	    this._do_not_update_binds = true;
	    code.call(this);
	    this._do_not_update_binds = false;

	    this._update_binds();
	  },
	  setBindData: function (data, key) {
	    //save called, updating master data
	    if (key) this._ignore_binds[key] = true;
	    if (this.setValue) this.setValue(data);else if (this.setValues) this.setValues(data);else {
	      var id = this.getCursor();
	      if (id) this.updateItem(id, data);else this.add(data);
	    }
	    this.callEvent("onBindUpdate", [data, key]);
	    if (this.save) this.save();
	    if (key) this._ignore_binds[key] = false;
	  },
	  //fill target with data
	  getBindData: function (key, update) {
	    //fire only if we have data updates from the last time
	    if (this._bind_updated[key]) return false;
	    var target = $$(key); //fill target only when it visible

	    if (target.isVisible(target._settings.id)) {
	      this._bind_updated[key] = true;

	      this._bind_update(target, this._bind_hash[key][0], this._bind_hash[key][1]); //trigger component specific updating logic


	      if (update && target.filter) target.refresh();
	    }
	  },
	  //add one more bind target
	  addBind: function (source, rule, format) {
	    this._bind_hash[source] = [rule, format];
	  },
	  removeBind: function (source) {
	    delete this._bind_hash[source];
	    delete this._bind_updated[source];
	    delete this._ignore_binds[source];
	  },
	  //returns true if object belong to "collection" type
	  _bind_specific_rules: function (obj) {
	    if (obj.filter) exports.extend(this, CollectionBind);else if (obj.setValue) exports.extend(this, ValueBind);else exports.extend(this, RecordBind);
	  },
	  //inform all binded objects, that source data was updated
	  _update_binds: function () {
	    if (!this._do_not_update_binds) for (var key in this._bind_hash) {
	      if (this._ignore_binds[key]) continue;
	      this._bind_updated[key] = false;
	      this.getBindData(key, true);
	    }
	  },
	  //copy data from source to the target
	  _bind_update_common: function (target, rule, data) {
	    if (target.setValue) target.setValue(data && rule ? data[rule] : data);else if (!target.filter) {
	      if (!data && target.clear) target.clear();else {
	        if (target._check_data_feed(data)) target.setValues(clone(data));
	      }
	    } else {
	      target.data.silent(function () {
	        this.filter(rule, data);
	      });
	    }
	    target.callEvent("onBindApply", [data, rule, this]);
	  }
	};

	var BaseBind = {
	  bind: function (target, rule, format) {
	    if (!this.attachEvent) exports.extend(this, EventSystem);
	    if (typeof target == "string") target = $$(target);
	    if (target._initBindSource) target._initBindSource();
	    if (this._initBindSource) this._initBindSource();
	    if (!target.getBindData) exports.extend(target, BindSource);

	    this._bind_ready();

	    target.addBind(this._settings.id, rule, format);
	    this._bind_source = target._settings.id;
	    var target_id = this._settings.id; //FIXME - check for touchable is not the best solution, to detect necessary event

	    this._bind_refresh_handler = this.attachEvent(this.touchable ? "onAfterRender" : "onBindRequest", function () {
	      return target.getBindData(target_id);
	    });
	    if (this.refresh && this.isVisible(this._settings.id)) this.refresh();
	  },
	  unbind: function () {
	    if (this._bind_source) {
	      var target = $$(this._bind_source);
	      if (target) target.removeBind(this._settings.id);
	      this.detachEvent(this._bind_refresh_handler);
	      this._bind_source = null;
	    }
	  },
	  _bind_ready: function () {
	    var config = this._settings;

	    if (this.filter) {
	      var key = config.id;
	      this.data._on_sync = bind(function () {
	        $$(this._bind_source)._bind_updated[key] = false;
	      }, this);
	    }

	    var old_render = this.render;

	    this.render = function () {
	      if (this._in_bind_processing) return;
	      this._in_bind_processing = true;
	      var result = this.callEvent("onBindRequest");
	      this._in_bind_processing = false;
	      return old_render.apply(this, result === false ? arguments : []);
	    };

	    if (this.getValue || this.getValues) this.save = function (data) {
	      var source = $$(this._bind_source);
	      if (data) source.setBindData(data);else {
	        if (this.validate && !this.validate()) return false;
	        var values = this.getValue ? this.getValue : this.getValues();
	        source.setBindData(values, this._settings.id); //reset form, so it will be counted as saved

	        if (this.setDirty) this.setDirty(false);
	      }
	    };

	    this._bind_ready = function () {};
	  }
	};

	var UIExtension = window.webix_view || {};
	var api = {
	  name: "baseview",
	  //attribute , which will be used for ID storing
	  $init: function (config) {
	    if (!config.id) config.id = _uid(this.name);
	    this._parent_cell = state._parent_cell;
	    state._parent_cell = null; // if scope not provided directly, and there is no parent view
	    // check if we have a global scope

	    this.$scope = config.$scope || (this._parent_cell ? this._parent_cell.$scope : state._global_scope);

	    if (!this._viewobj) {
	      this._contentobj = this._viewobj = create("DIV", {
	        "class": "webix_view"
	      });
	      this.$view = this._viewobj;
	    }
	  },
	  $skin: false,
	  defaults: {
	    width: 0,
	    height: 0,
	    gravity: 1
	  },
	  getNode: function () {
	    return this._viewobj;
	  },
	  getParentView: function () {
	    return this._parent_cell || null;
	  },
	  getTopParentView: function () {
	    var parent = this.getParentView();
	    return parent ? parent.getTopParentView() : this;
	  },
	  getFormView: function () {
	    var parent = this.getParentView();
	    return !parent || parent._recollect_elements ? parent : parent.getFormView();
	  },
	  getChildViews: function () {
	    return [];
	  },
	  queryView: function (search, all) {
	    var confirm;
	    if (typeof search === "string") search = {
	      view: search
	    };

	    if (_typeof(search) === "object") {
	      //IE8 compatibility
	      confirm = function (test) {
	        var config = test.config;

	        for (var key in search) {
	          if (config[key] != search[key]) return false;
	        }

	        return true;
	      };
	    } else confirm = search;

	    if (all === "self" && confirm(this)) return this;
	    var results = all === "all" ? [] : false;
	    var direction = all === "parent" ? this._queryGoUp : this._queryGoDown;

	    var found = this._queryView(confirm, direction, results);

	    return all === "all" ? results : found;
	  },
	  _queryGoDown: function (node) {
	    return node.getChildViews();
	  },
	  _queryGoUp: function (node) {
	    var parent = node.getParentView();
	    return parent ? [parent] : [];
	  },
	  _queryView: function (confirm, next, all) {
	    var kids = next(this);

	    for (var i = 0; i < kids.length; i++) {
	      if (confirm(kids[i])) {
	        if (all) all.push(kids[i]);else return kids[i];
	      }

	      var sub = kids[i]._queryView(confirm, next, all);

	      if (sub && !all) {
	        return sub;
	      }
	    }

	    return null;
	  },
	  isVisible: function (base_id) {
	    if (this._settings.hidden) {
	      if (base_id) {
	        if (!this._hidden_render) {
	          this._hidden_render = [];
	          this._hidden_hash = {};
	        }

	        if (!this._hidden_hash[base_id]) {
	          this._hidden_hash[base_id] = true;

	          this._hidden_render.push(base_id);
	        }
	      }

	      return false;
	    }

	    var parent = this.getParentView();
	    if (parent) return parent.isVisible(base_id, this._settings.id);
	    return true;
	  },
	  isEnabled: function () {
	    if (this._disable_cover) return false;
	    var parent = this.getParentView();
	    if (parent) return parent.isEnabled();
	    return true;
	  },
	  disable: function () {
	    remove(this._disable_cover);
	    this._settings.disabled = true;
	    this._disable_cover = create("div", {
	      "class": "webix_disabled"
	    });

	    this._viewobj.appendChild(this._disable_cover);

	    this._viewobj.setAttribute("aria-disabled", "true");

	    addCss(this._viewobj, "webix_disabled_view", true);

	    UIManager._moveChildFocus(this);
	  },
	  enable: function () {
	    this._settings.disabled = false;

	    if (this._disable_cover) {
	      remove(this._disable_cover);
	      removeCss(this._viewobj, "webix_disabled_view");

	      this._viewobj.removeAttribute("aria-disabled");

	      this._disable_cover = null;
	    }
	  },
	  disabled_setter: function (value) {
	    if (value) this.disable();else this.enable();
	    return value;
	  },
	  container_setter: function (value) {
	    assert(toNode(value), "Invalid container");
	    return true;
	  },
	  css_setter: function (value) {
	    if (_typeof(value) == "object") value = createCss(value);
	    this._viewobj.className += " " + value;
	    return value;
	  },
	  id_setter: function (value) {
	    if (state._global_collection && (state._global_collection != this || this._prev_global_col)) {
	      var oldvalue = this.config.$id = value;
	      (this._prev_global_col || state._global_collection)._elements[value] = this;
	      value = _uid(this.name);
	      (this._prev_global_col || state._global_collection)._translate_ids[value] = oldvalue;
	    }

	    assert(!ui.views[value], "Non unique view id: " + value);
	    ui.views[value] = this;

	    this._viewobj.setAttribute(
	    /*@attr*/
	    "view_id", value);

	    return value;
	  },
	  $setSize: function (x, y) {
	    var last = this._last_size;

	    if (last && last[0] == x && last[1] == y) {
	      debug_size_box(this, [x, y, "not changed"]);
	      return false;
	    }

	    debug_size_box(this, [x, y]);
	    this._last_size = [x, y];
	    this.$width = this._content_width = x - (this._scroll_y ? env.scrollSize : 0);
	    this.$height = this._content_height = y - (this._scroll_x ? env.scrollSize : 0);
	    var config = this._settings;

	    if (!config.flex) {
	      this._viewobj.style.width = x + "px";
	      this._viewobj.style.height = y + "px";
	    }

	    return true;
	  },
	  $getSize: function (dx, dy) {
	    var s = this._settings;
	    var size = [(s.width || s.minWidth || 0) * 1, (s.width || s.maxWidth || 100000) * 1, (s.height || s.minHeight || 0) * 1, (s.height || s.maxHeight || 100000) * 1, s.gravity];

	    if (assert) {
	      var check = isNaN(size[0]) || isNaN(size[1]) || isNaN(size[2]) || isNaN(size[3]);

	      if (check) {
	        assert(false, "Size is not a number " + this._settings.id);
	        s.width = s.height = s.maxWidth = s.maxHeight = s.minWidth = s.minHeight = 0;
	        size = [0, 0, 100000, 100000, 1];
	      }
	    }

	    size[0] += dx;
	    size[1] += dx;
	    size[2] += dy;
	    size[3] += dy;
	    return size;
	  },
	  show: function (force, animate_settings) {
	    var parent = this.getParentView();
	    var show = !arguments[2];

	    if (parent) {
	      if (!animate_settings && animate_settings !== false && this._settings.animate) if (parent._settings.animate) animate_settings = exports.extend(parent._settings.animate ? exports.extend({}, parent._settings.animate) : {}, this._settings.animate, true);
	      if (show ? parent._show : parent._hide) (show ? parent._show : parent._hide).call(parent, this, animate_settings);
	      if (show) this._render_hidden_views(); //force show of parent view
	      //stop further processing is view is a part of isolated scope

	      if (force && show) parent.show(parent.$$ ? false : force);
	    } else {
	      if (this._settings.hidden) {
	        if (show) {
	          var node = toNode(this._settings._container || document.body);
	          node.appendChild(this._viewobj);
	          this._settings.hidden = false;
	          this.adjust();

	          this._render_hidden_views();
	        }
	      } else {
	        if (!show) {
	          this._settings.hidden = this._settings._hidden = true;

	          if (this._viewobj) {
	            this._settings._container = this._viewobj.parentNode;
	            remove(this._viewobj);
	          }
	        }
	      }
	    }
	  },
	  _render_hidden_views: function () {
	    if (this._hidden_render) {
	      for (var i = 0; i < this._hidden_render.length; i++) {
	        var ui_to_render = $$(this._hidden_render[i]);
	        if (ui_to_render) ui_to_render.render();
	      }

	      this._hidden_render = [];
	      this._hidden_hash = {};
	    }
	  },
	  _onKeyPress: function (code, e) {
	    var target = e.srcElement || e.target,
	        role = target.getAttribute("role");

	    if ((code === 13 || code === 32) && (role == "button" || role == "tab") && !this._settings.disabled) {
	      triggerEvent(target, "MouseEvents", "click");
	      preventEvent(e);
	    }
	  },
	  hidden_setter: function (value) {
	    if (value) this.hide();
	    return this._settings.hidden;
	  },
	  hide: function () {
	    this.show(null, null, true);

	    UIManager._moveChildFocus(this);
	  },
	  adjust: function () {
	    if (!this._viewobj.parentNode) return false;
	    var x = this._viewobj.parentNode.clientWidth || 0;
	    var y = this._viewobj.parentNode.clientHeight || 0;
	    var sizes = this.$getSize(0, 0);
	    var fullscreen = this._viewobj.parentNode == document.body && !this.setPosition; //minWidth

	    if (sizes[0] > x) x = sizes[0]; //minHeight

	    if (sizes[2] > y) y = sizes[2]; //maxWidth rule

	    if ((!fullscreen || this._settings.width) && x > sizes[1]) x = sizes[1]; //maxHeight rule

	    if ((!fullscreen || this._settings.height) && y > sizes[3]) y = sizes[3];
	    this.$setSize(x, y);

	    if (state._responsive_exception) {
	      state._responsive_exception = false;
	      this.adjust();
	    }
	  },
	  resize: function () {
	    if (state._child_sizing_active || state._freeze_resize || state._responsive_tinkery) return;
	    var parent = this.getParentView();

	    if (parent) {
	      if (parent.resizeChildren) parent.resizeChildren();else parent.resize();
	    } else {
	      this.adjust();
	      callEvent("onResize", []);
	    }
	  }
	};
	var view = exports.protoUI(api, Settings, Destruction, BaseBind, UIExtension);
	var baseview = {
	  api: api,
	  view: view
	};

	var api$1 = {
	  name: "view",
	  $init: function (config) {
	    this._set_inner(config);
	  },
	  //deside, will component use borders or not
	  _set_inner: function (config) {
	    var border_not_set = isUndefined(config.borderless);

	    if (border_not_set && !this.setPosition && config.$topView) {
	      config.borderless = true;
	      border_not_set = false;
	    }

	    if (border_not_set && this.defaults.borderless || config.borderless) {
	      //button and custom borderless
	      config._inner = {
	        top: true,
	        left: true,
	        bottom: true,
	        right: true
	      };
	    } else {
	      //default borders
	      if (!config._inner) config._inner = {};
	      this._contentobj.style.borderWidth = "1px";
	    }
	  },
	  $getSize: function (dx, dy) {
	    var _borders = this._settings._inner;

	    if (_borders) {
	      dx += (_borders.left ? 0 : 1) + (_borders.right ? 0 : 1);
	      dy += (_borders.top ? 0 : 1) + (_borders.bottom ? 0 : 1);
	    }

	    var size = baseview.api.$getSize.call(this, dx, dy);
	    debug_size_box(this, size, true);
	    return size;
	  },
	  $setSize: function (x, y) {
	    debug_size_box(this, [x, y]);
	    var _borders = this._settings._inner;

	    if (_borders) {
	      x -= (_borders.left ? 0 : 1) + (_borders.right ? 0 : 1);
	      y -= (_borders.top ? 0 : 1) + (_borders.bottom ? 0 : 1);
	    }

	    return baseview.api.$setSize.call(this, x, y);
	  }
	};
	var view$1 = exports.protoUI(api$1, baseview.view);
	var base = {
	  api: api$1,
	  view: view$1
	}; //not necessary anymore
	//preserving for backward compatibility

	view$1.call(-1);

	/*
		REnders single item. 
		Can be used for elements without datastore, or with complex custom rendering logic
		
		@export
			render
	*/

	var SingleRender = exports.proto({
	  template_setter: function (value) {
	    this.type.template = template(value);
	  },
	  //convert item to the HTML text
	  _toHTML: function (obj) {
	    var type$$1 = this.type;
	    return (type$$1.templateStart ? type$$1.templateStart(obj, type$$1) : "") + type$$1.template(obj, type$$1) + (type$$1.templateEnd ? type$$1.templateEnd(obj, type$$1) : "");
	  },
	  customize: function (obj) {
	    type(this, obj);
	  }
	}, AtomRender);

	/*
		UI: Tooltip
		
		@export
			show
			hide
	*/
	// #include core/template.js
	// #include core/single_render.js

	var api$2 = {
	  name: "tooltip",
	  defaults: {
	    dy: 0,
	    dx: 20
	  },
	  $init: function (config) {
	    if (typeof config == "string") {
	      config = {
	        template: config
	      };
	    } //create  container for future tooltip


	    this.$view = this._viewobj = this._contentobj = this._dataobj = create("DIV", {
	      role: "alert",
	      "aria-atomic": "true"
	    });
	    this._contentobj.className = "webix_tooltip";
	    insertBefore(this._contentobj, document.body.firstChild, document.body);
	  },
	  adjust: function () {},
	  isVisible: function () {
	    return this._visible;
	  },
	  _alt_render: function (text) {
	    if (this.callEvent("onBeforeRender", [text])) {
	      //it is critical to have this as two commands
	      //its prevent destruction race in Chrome
	      this._dataobj.innerHTML = "";
	      this._dataobj.innerHTML = text;
	      this.callEvent("onAfterRender", []);
	    }
	  },
	  css_setter: function (value) {
	    if (_typeof(value) === "object") value = createCss(value);
	    this._viewobj.className = "webix_tooltip " + value;
	    return value;
	  },
	  //show tooltip
	  //pos - object, pos.x - left, pox.y - top
	  show: function (data, pos$$1) {
	    if (this._disabled) return;
	    this._visible = true;
	    if (typeof data === "string") this._alt_render(data);else {
	      this.data = exports.extend({}, data);
	      this.render();
	    }

	    if (this._dataobj.firstChild) {
	      //show at specified position
	      var w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
	      var h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
	      var positionX = w - pos$$1.x;
	      var positionY = h - pos$$1.y;
	      this._contentobj.style.display = "block";
	      if (positionX - this._settings.dx > this._contentobj.offsetWidth) positionX = pos$$1.x;else {
	        positionX = pos$$1.x - this._settings.dx * 2 - this._contentobj.offsetWidth;
	        if (positionX <= 0) positionX = 0;
	      }
	      if (positionY - this._settings.dy > this._contentobj.offsetHeight) positionY = pos$$1.y;else positionY = pos$$1.y - this._settings.dy - this._contentobj.offsetHeight;
	      this._contentobj.style.left = positionX + this._settings.dx + "px";
	      this._contentobj.style.top = positionY + this._settings.dy + "px";
	    } else this.hide();
	  },
	  //hide tooltip
	  hide: function () {
	    if (this._visible) {
	      this.data = null; //nulify, to be sure that on next show it will be fresh-rendered

	      this._contentobj.style.display = "none";
	      this._visible = false;
	    }
	  },
	  disable: function () {
	    this._disabled = true;
	  },
	  enable: function () {
	    this._disabled = false;
	  },
	  type: {
	    template: template("{obj.value}"),
	    templateStart: template.empty,
	    templateEnd: template.empty
	  }
	};
	var view$2 = exports.protoUI(api$2, SingleRender, Settings, EventSystem, base.view);

	/*
		Behavior: AutoTooltip - links tooltip to data driven item
	*/

	var AutoTooltip = {
	  tooltip_setter: function (value) {
	    if (value) {
	      if (typeof value === "function" || typeof value === "string") value = {
	        template: value
	      };
	      if (_typeof(value) !== "object") value = {};

	      this._init_tooltip_once();

	      return value;
	    }
	  },
	  _init_tooltip_once: function () {
	    TooltipControl.addTooltip(this);
	    this.attachEvent("onDestruct", function () {
	      TooltipControl.removeTooltip(this);
	    });
	    this.attachEvent("onAfterScroll", function () {
	      if (TooltipControl._tooltip_exist) TooltipControl._hide_tooltip();
	    });

	    this._init_tooltip_once = function () {};
	  },
	  $tooltipIn: function (t) {
	    var tooltip = TooltipControl._tooltip;
	    var def = exports.extend({
	      dx: 20,
	      dy: 0,
	      template: "{obj.value}",
	      css: ""
	    }, this._settings.tooltip, true);
	    tooltip.define(def);
	    return t;
	  },
	  $tooltipOut: function () {
	    TooltipControl._hide_tooltip();

	    return null;
	  },
	  $tooltipMove: function (t, e, text) {
	    TooltipControl._tooltip.hide();

	    clearTimeout(TooltipControl._before_show_delay);
	    TooltipControl._before_show_delay = delay(this._show_tooltip, this, [t, e, text], TooltipControl.delay);
	  },
	  _show_tooltip: function (t, e, text) {
	    var data = text || this._get_tooltip_data(t, e);

	    if (!data || !this.isVisible()) return;

	    TooltipControl._tooltip.show(data, pos(e));
	  },
	  _get_tooltip_data: function (t, e) {
	    if (this.locate && this.getItem) {
	      var id = this.locate(e);
	      if (!id) return null;
	      return this.getItem(id);
	    }

	    return this._settings;
	  }
	};

	var Canvas = exports.proto({
	  $init: function (container) {
	    this._canvas_labels = [];
	    this._canvas_series = !isUndefined(container.series) ? container.series : container.name;
	    this._obj = toNode(container.container || container);
	    var width = container.width * (window.devicePixelRatio || 1);
	    var height = container.height * (window.devicePixelRatio || 1);
	    var style = container.style || "";
	    style += ";width:" + container.width + "px;height:" + container.height + "px;";

	    this._prepareCanvas(container.name, style, width, height, container.title);
	  },
	  _prepareCanvas: function (name, style, x, y, title) {
	    //canvas has the same size as master object
	    this._canvas = create("canvas", {
	      title: title,
	      width: x,
	      height: y,
	      canvas_id: name,
	      style: style || ""
	    });

	    this._obj.appendChild(this._canvas); //use excanvas in IE


	    if (!this._canvas.getContext) {
	      if (env.isIE) {
	        require("legacy/excanvas/excanvas.js", true); //sync loading

	        /* global G_vmlCanvasManager */


	        G_vmlCanvasManager.init_(document);
	        G_vmlCanvasManager.initElement(this._canvas);
	      } else //some other not supported browser
	        assert(this._canvas.getContext, "Canvas is not supported in the browser");
	    }

	    return this._canvas;
	  },
	  getCanvas: function (context) {
	    var ctx = (this._canvas || this._prepareCanvas(this._contentobj)).getContext(context || "2d");

	    if (!this._webixDevicePixelRatio) {
	      this._webixDevicePixelRatio = true;
	      ctx.scale(window.devicePixelRatio || 1, window.devicePixelRatio || 1);
	    }

	    return ctx;
	  },
	  _resizeCanvas: function (x, y) {
	    if (this._canvas) {
	      this._canvas.setAttribute("width", x * (window.devicePixelRatio || 1));

	      this._canvas.setAttribute("height", y * (window.devicePixelRatio || 1));

	      this._canvas.style.width = x + "px";
	      this._canvas.style.height = y + "px";
	      this._webixDevicePixelRatio = false;
	    }
	  },
	  renderText: function (x, y, text, css, w) {
	    if (!text) return; //ignore empty text

	    if (w) w = Math.max(w, 0);
	    if (y) y = Math.max(y, 0);
	    var t = create("DIV", {
	      "class": "webix_canvas_text" + (css ? " " + css : ""),
	      "style": "left:" + x + "px; top:" + y + "px;",
	      "aria-hidden": "true"
	    }, text);

	    this._obj.appendChild(t);

	    this._canvas_labels.push(t); //destructor?


	    if (w) t.style.width = w + "px";
	    return t;
	  },
	  renderTextAt: function (valign, align, x, y, t, c, w) {
	    var text = this.renderText.call(this, x, y, t, c, w);

	    if (text) {
	      if (valign) {
	        if (valign == "middle") text.style.top = parseInt(y - text.offsetHeight / 2, 10) + "px";else text.style.top = y - text.offsetHeight + "px";
	      }

	      if (align) {
	        if (align == "left") text.style.left = x - text.offsetWidth + "px";else text.style.left = parseInt(x - text.offsetWidth / 2, 10) + "px";
	      }
	    }

	    return text;
	  },
	  clearCanvas: function (skipMap) {
	    var areas = [];
	    remove(this._canvas_labels);
	    this._canvas_labels = [];

	    if (!skipMap && this._obj._htmlmap) {
	      //areas that correspond this canvas layer
	      areas = this._getMapAreas(); //removes areas of this canvas

	      while (areas.length) {
	        areas[0].parentNode.removeChild(areas[0]);
	        areas.splice(0, 1);
	      }

	      areas = null; //removes _htmlmap object if all its child nodes are removed

	      if (!this._obj._htmlmap.getElementsByTagName("AREA").length) {
	        this._obj._htmlmap.parentNode.removeChild(this._obj._htmlmap);

	        this._obj._htmlmap = null;
	      }
	    } //FF breaks, when we are using clear canvas and call clearRect without parameters
	    //width|height are used insead of offsetWidth|offsetHeight for hidden canvas (series)


	    this.getCanvas().clearRect(0, 0, this._canvas.offsetWidth || Math.floor(this._canvas.width / (window.devicePixelRatio || 1)), this._canvas.offsetHeight || Math.floor(this._canvas.height / (window.devicePixelRatio || 1)));
	  },
	  toggleCanvas: function () {
	    this._toggleCanvas(this._canvas.style.display == "none");
	  },
	  showCanvas: function () {
	    this._toggleCanvas(true);
	  },
	  hideCanvas: function () {
	    this._toggleCanvas(false);
	  },
	  _toggleCanvas: function (show) {
	    var areas, i;

	    for (i = 0; i < this._canvas_labels.length; i++) {
	      this._canvas_labels[i].style.display = show ? "" : "none";
	    }

	    if (this._obj._htmlmap) {
	      areas = this._getMapAreas();

	      for (i = 0; i < areas.length; i++) {
	        if (show) areas[i].removeAttribute("disabled");else areas[i].setAttribute("disabled", "true");
	      }
	    } //FF breaks, when we are using clear canvas and call clearRect without parameters


	    this._canvas.style.display = show ? "" : "none";
	  },
	  _getMapAreas: function () {
	    var res = [],
	        areas,
	        i;
	    areas = this._obj._htmlmap.getElementsByTagName("AREA");

	    for (i = 0; i < areas.length; i++) {
	      if (areas[i].getAttribute("userdata") == this._canvas_series) {
	        res.push(areas[i]);
	      }
	    }

	    return res;
	  }
	});

	var animate = function (html_element, config) {
	  var animation = config;

	  if (isArray(html_element)) {
	    for (var i = 0; i < html_element.length; i++) {
	      if (isArray(config)) animation = config[i];

	      if (animation.type == "slide") {
	        if (animation.subtype == "out" && i === 0) {
	          // next
	          continue;
	        }

	        if (animation.subtype == "in" && i == 1) {
	          // current
	          continue;
	        }
	      }

	      if (animation.type == "flip") {
	        var animation_copy = clone(animation);

	        if (i === 0) {
	          // next
	          animation_copy.type = "flipback";
	        }

	        if (i == 1) {
	          // current
	          animation_copy.callback = null;
	        }

	        animate(html_element[i], animation_copy);
	        continue;
	      }

	      animate(html_element[i], animation);
	    }

	    return;
	  }

	  var node = toNode(html_element);
	  if (node._has_animation) animate.end(node, animation);else animate.start(node, animation);
	};

	animate.end = function (node, animation) {
	  //stop animation
	  node.style[env.transitionDuration] = "1ms";
	  node._has_animation = null; //clear animation wait order, if any

	  if (state._wait_animate) window.clearTimeout(state._wait_animate); //plan next animation, if any

	  state._wait_animate = delay(animate, this, [node, animation], 10);
	};

	animate.isSupported = function () {
	  return !state.noanimate && env.transform && env.transition && !env.isOpera;
	};

	animate.formLine = function (next, current, animation) {
	  var direction = animation.direction; //sometimes user can initiate animation multiple times ( fast clicking )
	  //as result animation may be called against already removed from the dom node

	  if (current.parentNode) current.parentNode.style.position = "relative";
	  current.style.position = "absolute";
	  next.style.position = "absolute"; //this is initial shift of second view in animation
	  //normally we need to have this value as 0
	  //but FF has bug with animation initially invisible elements
	  //so we are adjusting this value, to make 1px of second view visible

	  var defAniPos = env.isFF ? direction == "top" || direction == "left" ? -1 : 1 : 0;

	  if (direction == "top" || direction == "bottom") {
	    next.style.left = "0px";
	    next.style.top = (animation.top || defAniPos) + (direction == "top" ? 1 : -1) * current.offsetHeight + "px";
	  } else {
	    next.style.top = (animation.top || 0) + "px";
	    next.style.left = defAniPos + (direction == "left" ? 1 : -1) * current.offsetWidth + "px";
	  } // apply 'keepViews' mode, iframe, datatable with x scroll solution
	  //( keepViews won't work in case of "in" and "out" subtypes )


	  if (current.parentNode == next.parentNode && animation.keepViews) next.style.display = "";else insertBefore(next, current.nextSibling, current.parentNode);

	  if (animation.type == "slide" && animation.subtype == "out") {
	    next.style.left = "0px";
	    next.style.top = (animation.top || 0) + "px";
	    current.parentNode.removeChild(current);
	    insertBefore(current, next.nextSibling, next.parentNode);
	  }

	  return [next, current];
	};

	animate.breakLine = function (line) {
	  if (arguments[1]) line[1].style.display = "none"; // 'keepViews' multiview mode
	  else remove(line[1]); // 1 = current

	  animate.clear(line[0]);
	  animate.clear(line[1]);
	  line[0].style.position = "";
	};

	animate.clear = function (node) {
	  node.style[env.transform] = "none";
	  node.style[env.transition] = "none";
	  node.style.top = node.style.left = "";
	};

	animate.defaults = {
	  type: "slide",
	  delay: "0",
	  duration: "500",
	  timing: "ease-in-out",
	  x: 0,
	  y: 0
	};

	animate.start = function (node, animation) {
	  //getting config object by merging specified and default options
	  if (typeof animation == "string") animation = {
	    type: animation
	  };
	  animation = Settings._mergeSettings(animation, animate.defaults);
	  var prefix = env.cssPrefix;
	  var settings = node._has_animation = animation;
	  var skew_options, scale_type; //jshint -W086:true

	  switch (settings.type == "slide" && settings.direction) {
	    // getting new x, y in case it is slide with direction
	    case "right":
	      settings.x = node.offsetWidth;
	      break;

	    case "left":
	      settings.x = -node.offsetWidth;
	      break;

	    case "top":
	      settings.y = -node.offsetHeight;
	      break;

	    case "bottom":
	    default:
	      settings.y = settings.y || node.offsetHeight;
	      break;
	  }

	  if (settings.type == "flip" || settings.type == "flipback") {
	    skew_options = [0, 0];
	    scale_type = "scaleX";

	    if (settings.subtype == "vertical") {
	      skew_options[0] = 20;
	      scale_type = "scaleY";
	    } else skew_options[1] = 20;

	    if (settings.direction == "right" || settings.direction == "bottom") {
	      skew_options[0] *= -1;
	      skew_options[1] *= -1;
	    }
	  }

	  var duration = settings.duration + "ms " + settings.timing + " " + settings.delay + "ms";
	  var css_general = prefix + "TransformStyle: preserve-3d;"; // general css rules

	  var css_transition = "";
	  var css_transform = "";

	  switch (settings.type) {
	    case "fade":
	      // changes opacity to 0
	      css_transition = "opacity " + duration;
	      css_general = "opacity: 0;";
	      break;

	    case "show":
	      // changes opacity to 1
	      css_transition = "opacity " + duration;
	      css_general = "opacity: 1;";
	      break;

	    case "flip":
	      duration = settings.duration / 2 + "ms " + settings.timing + " " + settings.delay + "ms";
	      css_transform = "skew(" + skew_options[0] + "deg, " + skew_options[1] + "deg) " + scale_type + "(0.00001)";
	      css_transition = "all " + duration;
	      break;

	    case "flipback":
	      settings.delay += settings.duration / 2;
	      duration = settings.duration / 2 + "ms " + settings.timing + " " + settings.delay + "ms";
	      node.style[env.transform] = "skew(" + -1 * skew_options[0] + "deg, " + -1 * skew_options[1] + "deg) " + scale_type + "(0.00001)";
	      node.style.left = "0";
	      css_transform = "skew(0deg, 0deg) " + scale_type + "(1)";
	      css_transition = "all " + duration;
	      break;

	    case "slide":
	      // moves object to specified location
	      var x = settings.x + "px";
	      var y = settings.y + "px"; // translate(x, y) OR translate3d(x, y, 0)

	      css_transform = env.translate + "(" + x + ", " + y + (env.translate == "translate3d" ? ", 0" : "") + ")";
	      css_transition = prefix + "transform " + duration;
	      break;

	    default:
	      break;
	  } //set styles only after applying transition settings


	  delay(function () {
	    node.style[env.transition] = css_transition;
	    delay(function () {
	      if (css_general) node.style.cssText += css_general;
	      if (css_transform) node.style[env.transform] = css_transform;
	      var transitionEnded = false;
	      var tid = event$1(node, env.transitionEnd, function (ev) {
	        node._has_animation = null;
	        if (settings.callback) settings.callback.call(settings.master || window, node, settings, ev);
	        transitionEnded = true;
	        eventRemove(tid);
	      });
	      window.setTimeout(function () {
	        if (!transitionEnded) {
	          node._has_animation = null;
	          if (settings.callback) settings.callback.call(settings.master || window, node, settings);
	          transitionEnded = true;
	          eventRemove(tid);
	        }
	      }, (settings.duration * 1 + settings.delay * 1) * 1.3);
	    });
	  });
	};

	var skin = {
	  topLayout: "space",
	  //bar in accordion
	  barHeight: 44,
	  //!!!Set the same in skin.less!!!
	  tabbarHeight: 42,
	  sidebarTitleHeight: 44,
	  rowHeight: 36,
	  toolbarHeight: 44,
	  listItemHeight: 36,
	  //list, grouplist, dataview, etc.
	  inputHeight: 38,
	  buttonHeight: 38,
	  inputPadding: 3,
	  menuHeight: 36,
	  labelTopHeight: 22,
	  propertyItemHeight: 28,
	  unitHeaderHeight: 36,
	  inputSpacing: 4,
	  borderWidth: 1,
	  sliderHandleWidth: 14,
	  sliderPadding: 10,
	  sliderBorder: 1,
	  vSliderPadding: 15,
	  vSliderHeight: 100,
	  switchHeight: 22,
	  switchWidth: 50,
	  //margin - distance between cells
	  layoutMargin: {
	    space: 10,
	    wide: 10,
	    clean: 0,
	    head: 4,
	    line: -1,
	    toolbar: 4,
	    form: 8,
	    accordion: 2
	  },
	  //padding - distance inside cell between cell border and cell content
	  layoutPadding: {
	    space: 10,
	    wide: 0,
	    clean: 0,
	    head: 0,
	    line: 0,
	    toolbar: 2,
	    form: 17,
	    accordion: 0
	  },
	  //space between tabs in tabbar
	  tabMargin: 0,
	  tabOffset: 0,
	  tabBottomOffset: 0,
	  tabTopOffset: 0,
	  tabBorder: true,
	  customCheckbox: true,
	  customRadio: true,
	  sidebarMarkAll: true,
	  popupNoPoint: true,
	  borderlessPopup: true,
	  popupPadding: 0,
	  calendar: {
	    timepickerHeight: 24
	  },
	  calendarHeight: 70,
	  padding: 0,
	  accordionType: "accordion",
	  optionHeight: 32,
	  organogramLineColor: "#CCD7E6"
	};

	var skin$1 = {
	  topLayout: "space",
	  //bar in accordion
	  barHeight: 36,
	  //!!!Set the same in skin.less!!!
	  tabbarHeight: 34,
	  sidebarTitleHeight: 36,
	  rowHeight: 28,
	  toolbarHeight: 36,
	  listItemHeight: 28,
	  //list, grouplist, dataview, etc.
	  inputHeight: 30,
	  buttonHeight: 30,
	  inputPadding: 3,
	  menuHeight: 28,
	  labelTopHeight: 16,
	  propertyItemHeight: 24,
	  unitHeaderHeight: 28,
	  inputSpacing: 4,
	  borderWidth: 1,
	  sliderHandleWidth: 12,
	  sliderPadding: 10,
	  sliderBorder: 1,
	  vSliderPadding: 13,
	  vSliderHeight: 100,
	  switchHeight: 20,
	  switchWidth: 40,
	  //margin - distance between cells
	  layoutMargin: {
	    space: 5,
	    wide: 5,
	    clean: 0,
	    head: 4,
	    line: -1,
	    toolbar: 4,
	    form: 8,
	    accordion: 2
	  },
	  //padding - distance inside cell between cell border and cell content
	  layoutPadding: {
	    space: 5,
	    wide: 0,
	    clean: 0,
	    head: 0,
	    line: 0,
	    toolbar: 2,
	    form: 12,
	    accordion: 0
	  },
	  //space between tabs in tabbar
	  tabMargin: 0,
	  tabOffset: 0,
	  tabBottomOffset: 0,
	  tabTopOffset: 0,
	  tabBorder: true,
	  customCheckbox: true,
	  customRadio: true,
	  sidebarMarkAll: true,
	  popupNoPoint: true,
	  borderlessPopup: true,
	  popupPadding: 0,
	  calendar: {
	    timepickerHeight: 24
	  },
	  calendarHeight: 70,
	  padding: 0,
	  accordionType: "accordion",
	  optionHeight: 24,
	  organogramLineColor: "#CCD7E6"
	};

	var skin$2 = {
	  topLayout: "space",
	  //bar in accordion
	  barHeight: 46,
	  //!!!Set the same in skin.less!!!
	  tabbarHeight: 46,
	  rowHeight: 34,
	  toolbarHeight: 46,
	  sidebarTitleHeight: 45,
	  listItemHeight: 34,
	  //list, grouplist, dataview, etc.
	  inputHeight: 38,
	  buttonHeight: 38,
	  inputPadding: 3,
	  menuHeight: 34,
	  labelTopHeight: 22,
	  unitHeaderHeight: 20,
	  propertyItemHeight: 28,
	  inputSpacing: 4,
	  borderWidth: 1,
	  sliderHandleWidth: 14,
	  sliderPadding: 10,
	  sliderBorder: 1,
	  vSliderPadding: 15,
	  vSliderHeight: 100,
	  switchHeight: 22,
	  switchWidth: 50,
	  //margin - distance between cells
	  layoutMargin: {
	    space: 10,
	    wide: 10,
	    clean: 0,
	    head: 4,
	    line: -1,
	    toolbar: 4,
	    form: 8,
	    accordion: 10
	  },
	  //padding - distance insede cell between cell border and cell content
	  layoutPadding: {
	    space: 10,
	    wide: 0,
	    clean: 0,
	    head: 0,
	    line: 0,
	    toolbar: 3,
	    form: 17,
	    accordion: 0
	  },
	  //space between tabs in tabbar
	  tabMargin: 4,
	  tabOffset: 0,
	  tabBottomOffset: 6,
	  tabTopOffset: 1,
	  customCheckbox: true,
	  customRadio: true,
	  popupPadding: 8,
	  calendarHeight: 70,
	  padding: 0,
	  accordionType: "accordion",
	  optionHeight: 32
	};

	var skin$3 = {
	  topLayout: "space",
	  //bar in accordion
	  barHeight: 34,
	  //!!!Set the same in skin.less!!!
	  tabbarHeight: 34,
	  sidebarTitleHeight: 34,
	  rowHeight: 24,
	  toolbarHeight: 34,
	  listItemHeight: 28,
	  //list, grouplist, dataview, etc.
	  unitHeaderHeight: 20,
	  propertyItemHeight: 24,
	  inputHeight: 30,
	  buttonHeight: 30,
	  inputPadding: 3,
	  menuHeight: 28,
	  labelTopHeight: 16,
	  inputSpacing: 4,
	  borderWidth: 1,
	  sliderHandleWidth: 12,
	  sliderPadding: 10,
	  sliderBorder: 1,
	  vSliderPadding: 13,
	  vSliderHeight: 100,
	  switchHeight: 20,
	  switchWidth: 40,
	  //margin - distance between cells
	  layoutMargin: {
	    space: 5,
	    wide: 5,
	    clean: 0,
	    head: 4,
	    line: -1,
	    toolbar: 4,
	    form: 4,
	    accordion: 5
	  },
	  //padding - distance inside cell between cell border and cell content
	  layoutPadding: {
	    space: 5,
	    wide: 0,
	    clean: 0,
	    head: 0,
	    line: 0,
	    toolbar: 1,
	    form: 12,
	    accordion: 0
	  },
	  //space between tabs in tabbar
	  tabMargin: 3,
	  tabOffset: 0,
	  tabBottomOffset: 3,
	  tabTopOffset: 1,
	  customCheckbox: true,
	  customRadio: true,
	  popupPadding: 8,
	  calendarHeight: 70,
	  padding: 0,
	  accordionType: "accordion",
	  optionHeight: 23
	};

	var skin$4 = {
	  topLayout: "space",
	  //bar in accordion
	  barHeight: 46,
	  // !!!Set the same in skin.less!!!
	  tabbarHeight: 46,
	  rowHeight: 34,
	  toolbarHeight: 46,
	  sidebarTitleHeight: 45,
	  listItemHeight: 34,
	  // list, grouplist, dataview, etc.
	  unitHeaderHeight: 20,
	  inputHeight: 38,
	  buttonHeight: 38,
	  inputPadding: 3,
	  menuHeight: 34,
	  labelTopHeight: 22,
	  propertyItemHeight: 28,
	  inputSpacing: 4,
	  borderWidth: 1,
	  sliderHandleWidth: 14,
	  sliderPadding: 10,
	  sliderBorder: 1,
	  vSliderPadding: 15,
	  vSliderHeight: 100,
	  switchHeight: 22,
	  switchWidth: 50,
	  //margin - distance between cells
	  layoutMargin: {
	    space: 10,
	    wide: 10,
	    clean: 0,
	    head: 4,
	    line: -1,
	    toolbar: 8,
	    form: 8,
	    accordion: 10
	  },
	  //padding - distance inside cell between cell border and cell content
	  layoutPadding: {
	    space: 10,
	    wide: 0,
	    clean: 0,
	    head: 0,
	    line: 0,
	    toolbar: 3,
	    form: 17,
	    accordion: 0
	  },
	  //space between tabs in tabbar
	  tabMargin: 4,
	  tabOffset: 0,
	  tabBottomOffset: 6,
	  tabTopOffset: 1,
	  customCheckbox: true,
	  customRadio: true,
	  popupPadding: 8,
	  calendarHeight: 70,
	  padding: 0,
	  accordionType: "accordion",
	  optionHeight: 32
	};

	var $active, $name;
	function set$1(name) {
	  assert(skin$5[name], "Incorrect skin name: " + name);
	  if ($name === name) return;
	  skin$5.$active = $active = skin$5[name];
	  skin$5.$name = $name = name;

	  if (ui) {
	    for (var key in ui) {
	      var view = ui[key];
	      if (view && view.prototype && view.prototype.$skin) view.prototype.$skin(view.prototype);
	    }
	  }
	}
	var skin$5 = {
	  set: set$1,
	  material: skin,
	  mini: skin$1,
	  flat: skin$2,
	  compact: skin$3,
	  contrast: skin$4
	};
	set$1(window.webix_skin || "material"); //necessary for skin builder

	var Touch = {
	  config: {
	    longTouchDelay: 1000,
	    scrollDelay: 150,
	    gravity: 500,
	    deltaStep: 30,
	    speed: "0ms",
	    finish: 1500,
	    ellastic: true,
	    fastClick: true
	  },
	  limit: function (value) {
	    Touch._limited = value !== false;
	  },
	  disable: function () {
	    Touch._disabled = true;
	  },
	  enable: function () {
	    Touch._disabled = false;
	  },
	  $init: function () {
	    Touch.$init = function () {};

	    event$1(document.body, mouse.down, Touch._touchstart, {
	      passive: false
	    });
	    event$1(document.body, mouse.move, Touch._touchmove, {
	      passive: false
	    });
	    event$1(document.body, mouse.up, Touch._touchend);
	    event$1(document.body, "dragstart", function (e) {
	      if (Touch._disabled || Touch._limited) return;
	      return preventEvent(e);
	    });
	    event$1(document.body, "touchstart", function (e) {
	      if (Touch._disabled || Touch._limited || !Touch.config.fastClick) return; //fast click mode for iOS
	      //To have working form elements Android must not block event - so there are no fast clicks for Android
	      //Selects still don't work with fast clicks

	      if (env.isSafari) {
	        var tag = e.srcElement.tagName.toLowerCase();
	        if (tag == "input" || tag == "textarea" || tag == "select" || tag == "label") return true;
	        Touch._fire_fast_event = true;
	        return preventEvent(e);
	      }
	    }, {
	      passive: false
	    });

	    Touch._clear_artefacts();

	    Touch._scroll = [null, null];
	    Touch.$active = true;
	  },
	  _clear_artefacts: function () {
	    Touch._start_context = Touch._current_context = Touch._prev_context = Touch._scroll_context = null;
	    Touch._scroll_mode = Touch._scroll_node = Touch._scroll_stat = this._long_touched = null; //remove(Touch._scroll);
	    //Touch._scroll = [null, null];

	    Touch._delta = {
	      _x_moment: 0,
	      _y_moment: 0,
	      _time: 0
	    };

	    if (Touch._css_button_remove) {
	      removeCss(Touch._css_button_remove, "webix_touch");
	      Touch._css_button_remove = null;
	    }

	    window.clearTimeout(Touch._long_touch_timer);
	    Touch._was_not_moved = true;
	    Touch._axis_x = true;
	    Touch._axis_y = true;
	    if (!Touch._active_transion) Touch._scroll_end();
	  },
	  _touchend: function (e) {
	    if (Touch._start_context) {
	      if (!Touch._scroll_mode) {
	        if (!this._long_touched) {
	          if (Touch._axis_y && !Touch._axis_x) {
	            Touch._translate_event("onSwipeX");
	          } else if (Touch._axis_x && !Touch._axis_y) {
	            Touch._translate_event("onSwipeY");
	          } else {
	            if (env.isSafari && Touch._fire_fast_event) {
	              //need to test for mobile ff and blackbery
	              Touch._fire_fast_event = false;
	              var target = Touch._start_context.target; //dark iOS magic, without delay it can skip repainting

	              delay(function () {
	                var click_event = document.createEvent("MouseEvents");
	                click_event.initEvent("click", true, true);
	                target.dispatchEvent(click_event);
	              });
	            }
	          }
	        }
	      } else {
	        var temp = Touch._get_matrix(Touch._scroll_node);

	        var x = temp.e;
	        var y = temp.f;
	        var finish = Touch.config.finish;

	        var delta = Touch._get_delta(e, true);

	        var view = $$(Touch._scroll_node);
	        var gravity = view && view.$scroll ? view.$scroll.gravity : Touch.config.gravity;

	        if (delta._time) {
	          var nx = x + gravity * delta._x_moment / delta._time;
	          var ny = y + gravity * delta._y_moment / delta._time;
	          var cnx = Touch._scroll[0] ? Touch._correct_minmax(nx, false, false, Touch._scroll_stat.dx, Touch._scroll_stat.px) : x;
	          var cny = Touch._scroll[1] ? Touch._correct_minmax(ny, false, false, Touch._scroll_stat.dy, Touch._scroll_stat.py) : y;
	          var size = Math.max(Math.abs(cnx - x), Math.abs(cny - y));
	          if (size < 150) finish = finish * size / 150;
	          if (cnx != x || cny != y) finish = Math.round(finish * Math.max((cnx - x) / (nx - x), (cny - y) / (ny - y)));
	          var result = {
	            e: cnx,
	            f: cny
	          };
	          view = $$(Touch._scroll_node);
	          if (view && view.adjustScroll) view.adjustScroll(result); //finish = Math.max(100,(Touch._fast_correction?100:finish));

	          finish = Math.max(100, finish);

	          if (x != result.e || y != result.f) {
	            Touch._set_matrix(Touch._scroll_node, result.e, result.f, finish + "ms");

	            if (Touch._scroll_master) Touch._scroll_master._sync_scroll(result.e, result.f, finish + "ms");

	            Touch._set_scroll(result.e, result.f, finish + "ms");
	          } else {
	            Touch._scroll_end();
	          }
	        } else Touch._scroll_end();
	      }

	      Touch._translate_event("onTouchEnd");

	      Touch._clear_artefacts();
	    }
	  },
	  _touchmove: function (e) {
	    if (!Touch._scroll_context || !Touch._start_context) return;

	    var delta = Touch._get_delta(e);

	    Touch._translate_event("onTouchMove");

	    if (Touch._scroll_mode) {
	      Touch._set_scroll_pos(delta);
	    } else {
	      Touch._axis_x = Touch._axis_check(delta._x, "x", Touch._axis_x);
	      Touch._axis_y = Touch._axis_check(delta._y, "y", Touch._axis_y);

	      if (Touch._scroll_mode) {
	        var view = Touch._get_event_view("onBeforeScroll", true);

	        if (view) {
	          var data = {};
	          view.callEvent("onBeforeScroll", [data]);

	          if (data.update) {
	            Touch.config.speed = data.speed;
	            Touch.config.scale = data.scale;
	          }
	        }

	        Touch._init_scroller(delta); //apply scrolling

	      }
	    }

	    return preventEvent(e);
	  },
	  _set_scroll_pos: function () {
	    if (!Touch._scroll_node) return;

	    var temp = Touch._get_matrix(Touch._scroll_node);

	    var prev = Touch._prev_context || Touch._start_context;
	    var view = $$(Touch._scroll_node);
	    var ellastic = view && view.$scroll ? view.$scroll.ellastic : Touch.config.ellastic;
	    if (Touch._scroll[0]) temp.e = Touch._correct_minmax(temp.e - prev.x + Touch._current_context.x, ellastic, temp.e, Touch._scroll_stat.dx, Touch._scroll_stat.px);
	    if (Touch._scroll[1]) temp.f = Touch._correct_minmax(temp.f - prev.y + Touch._current_context.y, ellastic, temp.f, Touch._scroll_stat.dy, Touch._scroll_stat.py);

	    Touch._set_matrix(Touch._scroll_node, temp.e, temp.f, "0ms");

	    if (Touch._scroll_master) Touch._scroll_master._sync_scroll(temp.e, temp.f, "0ms");

	    Touch._set_scroll(temp.e, temp.f, "0ms");
	  },
	  _set_scroll: function (dx, dy, speed) {
	    var edx = Touch._scroll_stat.px / Touch._scroll_stat.dx * -dx;
	    var edy = Touch._scroll_stat.py / Touch._scroll_stat.dy * -dy;
	    if (Touch._scroll[0]) Touch._set_matrix(Touch._scroll[0], edx, 0, speed);
	    if (Touch._scroll[1]) Touch._set_matrix(Touch._scroll[1], 0, edy, speed);
	  },
	  scrollTo: function (node, x, y, speed) {
	    Touch._set_matrix(node, x, y, speed);
	  },
	  _set_matrix: function (node, xv, yv, speed) {
	    if (!Touch._in_anim_frame && window.setAnimationFrame) {
	      window.setAnimationFrame(function () {
	        Touch._in_anim_frame = true;
	        return Touch._set_matrix(node, xv, yv, speed);
	      });
	    }

	    Touch._in_anim_frame = null;
	    Touch._active_transion = true;

	    if (node) {
	      var trans = Touch.config.translate || env.translate;
	      node.style[env.transform] = trans + "(" + Math.round(xv) + "px, " + Math.round(yv) + "px" + (trans == "translate3d" ? ", 0" : "") + ")";
	      node.style[env.transitionDuration] = speed;
	    }
	  },
	  _get_matrix: function (node) {
	    var matrix = window.getComputedStyle(node)[env.transform];
	    var tmatrix;
	    if (matrix == "none") tmatrix = {
	      e: 0,
	      f: 0
	    };else {
	      if (window.WebKitCSSMatrix)
	        /* global WebKitCSSMatrix */
	        tmatrix = new WebKitCSSMatrix(matrix);else if (window.MSCSSMatrix)
	        /* global MSCSSMatrix */
	        tmatrix = new MSCSSMatrix(matrix);else {
	        // matrix(1, 0, 0, 1, 0, 0) --> 1, 0, 0, 1, 0, 0
	        var _tmatrix = matrix.replace(/(matrix\()(.*)(\))/gi, "$2"); // 1, 0, 0, 1, 0, 0 --> 1,0,0,1,0,0


	        _tmatrix = _tmatrix.replace(/\s/gi, "");
	        _tmatrix = _tmatrix.split(",");
	        tmatrix = {};
	        var tkey = ["a", "b", "c", "d", "e", "f"];

	        for (var i = 0; i < tkey.length; i++) {
	          tmatrix[tkey[i]] = parseInt(_tmatrix[i], 10);
	        }
	      }
	    }
	    if (Touch._scroll_master) Touch._scroll_master._sync_pos(tmatrix);
	    return tmatrix;
	  },
	  _correct_minmax: function (value, allow, current, dx, px) {
	    if (value === current) return value;
	    var delta = Math.abs(value - current);
	    var sign = delta / (value - current); //	Touch._fast_correction = true;

	    if (value > 0) return allow ? current + sign * Math.sqrt(delta) : 0;
	    var max = dx - px;
	    if (max + value < 0) return allow ? current - Math.sqrt(-(value - current)) : -max; //	Touch._fast_correction = false;

	    return value;
	  },
	  _init_scroll_node: function (node) {
	    if (!node.scroll_enabled) {
	      node.scroll_enabled = true;
	      node.parentNode.style.position = "relative";
	      var prefix = env.cssPrefix;
	      node.style.cssText += prefix + "transition: " + prefix + "transform; " + prefix + "user-select:none; " + prefix + "transform-style:flat;";
	      node.addEventListener(env.transitionEnd, Touch._scroll_end, false);
	    }
	  },
	  _init_scroller: function () {
	    if (Touch._scroll_mode.indexOf("x") != -1) Touch._scroll[0] = Touch._create_scroll("x", Touch._scroll_stat.dx, Touch._scroll_stat.px, "width");
	    if (Touch._scroll_mode.indexOf("y") != -1) Touch._scroll[1] = Touch._create_scroll("y", Touch._scroll_stat.dy, Touch._scroll_stat.py, "height");

	    Touch._init_scroll_node(Touch._scroll_node);

	    window.setTimeout(Touch._set_scroll_pos, 1);
	  },
	  _create_scroll: function (mode, dy, py, dim) {
	    if (dy - py < 2) {
	      var matrix = Touch._get_matrix(Touch._scroll_node);

	      var e = mode == "y" ? matrix.e : 0;
	      var f = mode == "y" ? 0 : matrix.f;
	      if (!Touch._scroll_master) Touch._set_matrix(Touch._scroll_node, e, f, "0ms");
	      Touch._scroll_mode = Touch._scroll_mode.replace(mode, "");
	      return "";
	    }

	    var scroll = create("DIV", {
	      "class": "webix_scroll_" + mode
	    }, "");
	    scroll.style[dim] = Math.max(py * py / dy - 7, 10) + "px";
	    if (Touch._scroll_stat.left) if (mode === "x") scroll.style.left = Touch._scroll_stat.left + "px";else scroll.style.right = -Touch._scroll_stat.left + "px";
	    if (Touch._scroll_stat.hidden) scroll.style.visibility = "hidden";

	    Touch._scroll_node.parentNode.appendChild(scroll);

	    return scroll;
	  },
	  _axis_check: function (value, mode, old) {
	    if (value > Touch.config.deltaStep) {
	      if (Touch._was_not_moved) {
	        Touch._long_move(mode);

	        Touch._locate(mode);

	        if ((Touch._scroll_mode || "").indexOf(mode) == -1) Touch._scroll_mode = "";
	      }

	      return false;
	    }

	    return old;
	  },
	  _scroll_end: function () {
	    //sending event to the owner of the scroll only
	    var result, state, view;
	    view = $$(Touch._scroll_node || this);

	    if (view) {
	      if (Touch._scroll_node) result = Touch._get_matrix(Touch._scroll_node);else if (view.getScrollState) {
	        state = view.getScrollState();
	        result = {
	          e: state.x,
	          f: state.y
	        };
	      }
	      callEvent("onAfterScroll", [result]);
	      if (view.callEvent) view.callEvent("onAfterScroll", [result]);
	    }

	    if (!Touch._scroll_mode) {
	      remove(Touch._scroll);
	      Touch._scroll = [null, null];
	    }

	    Touch._active_transion = false;
	  },
	  _long_move: function () {
	    window.clearTimeout(Touch._long_touch_timer);
	    Touch._was_not_moved = false;
	  },
	  _stop_old_scroll: function (e) {
	    if (Touch._scroll[0] || Touch._scroll[1]) {
	      Touch._stop_scroll(e, Touch._scroll[0] ? "x" : "y");
	    } else return true;
	  },
	  _touchstart: function (e) {
	    var target = e.target || event$1.srcElement;
	    if (Touch._disabled || target.tagName && target.tagName.toLowerCase() == "textarea" && target.offsetHeight < target.scrollHeight) return;
	    Touch._long_touched = null;
	    Touch._scroll_context = Touch._start_context = mouse.context(e); // in "limited" mode we should have possibility to use slider

	    var element = $$(e);

	    if (Touch._limited && !Touch._is_scroll() && !(element && element.$touchCapture)) {
	      Touch._scroll_context = null;
	    }

	    Touch._translate_event("onTouchStart");

	    if (Touch._stop_old_scroll(e)) Touch._long_touch_timer = window.setTimeout(Touch._long_touch, Touch.config.longTouchDelay);

	    if (element && element.touchable && (!target.className || target.className.indexOf("webix_view") !== 0)) {
	      Touch._css_button_remove = element.getNode(e);
	      addCss(Touch._css_button_remove, "webix_touch");
	    }
	  },
	  _long_touch: function () {
	    if (Touch._start_context) {
	      Touch._translate_event("onLongTouch");

	      callEvent("onClick", [Touch._start_context]);
	      Touch._long_touched = true; //Touch._clear_artefacts();
	    }
	  },
	  _stop_scroll: function (e, stop_mode) {
	    Touch._locate(stop_mode);

	    var scroll = Touch._scroll[0] || Touch._scroll[1];

	    if (scroll) {
	      var view = Touch._get_event_view("onBeforeScroll", true);

	      if (view) view.callEvent("onBeforeScroll", [Touch._start_context, Touch._current_context]);
	    }

	    if (scroll && (!Touch._scroll_node || scroll.parentNode != Touch._scroll_node.parentNode)) {
	      Touch._clear_artefacts();

	      Touch._scroll_end();

	      Touch._start_context = mouse.context(e);
	    }

	    Touch._touchmove(e);
	  },
	  _get_delta: function (e) {
	    Touch._prev_context = Touch._current_context;
	    Touch._current_context = mouse.context(e);
	    Touch._delta._x = Math.abs(Touch._start_context.x - Touch._current_context.x);
	    Touch._delta._y = Math.abs(Touch._start_context.y - Touch._current_context.y);

	    if (Touch._prev_context) {
	      if (Touch._current_context.time - Touch._prev_context.time < Touch.config.scrollDelay) {
	        Touch._delta._x_moment = Touch._delta._x_moment / 1.3 + Touch._current_context.x - Touch._prev_context.x;
	        Touch._delta._y_moment = Touch._delta._y_moment / 1.3 + Touch._current_context.y - Touch._prev_context.y;
	      } else {
	        Touch._delta._y_moment = Touch._delta._x_moment = 0;
	      }

	      Touch._delta._time = Touch._delta._time / 1.3 + (Touch._current_context.time - Touch._prev_context.time);
	    }

	    return Touch._delta;
	  },
	  _get_sizes: function (node) {
	    Touch._scroll_stat = {
	      dx: node.offsetWidth,
	      dy: node.offsetHeight,
	      px: node.parentNode.offsetWidth,
	      py: node.parentNode.offsetHeight
	    };
	  },
	  _is_scroll: function (locate_mode) {
	    var node = Touch._start_context.target;
	    if (!env.touch && !env.transition && !env.transform) return null;

	    while (node && node.tagName != "BODY") {
	      if (node.getAttribute) {
	        var mode = node.getAttribute("touch_scroll");
	        if (mode && (!locate_mode || mode.indexOf(locate_mode) != -1)) return [node, mode];
	      }

	      node = node.parentNode;
	    }

	    return null;
	  },
	  _locate: function (locate_mode) {
	    var state = this._is_scroll(locate_mode);

	    if (state) {
	      Touch._scroll_mode = state[1];
	      Touch._scroll_node = state[0];

	      Touch._get_sizes(state[0]);
	    }

	    return state;
	  },
	  _translate_event: function (name) {
	    callEvent(name, [Touch._start_context, Touch._current_context]);

	    var view = Touch._get_event_view(name);

	    if (view) view.callEvent(name, [Touch._start_context, Touch._current_context]);
	  },
	  _get_event_view: function (name, active) {
	    var view = $$(active ? Touch._scroll_node : Touch._start_context);
	    if (!view) return null;

	    while (view) {
	      if (view.hasEvent && view.hasEvent(name)) return view;
	      view = view.getParentView();
	    }

	    return null;
	  },
	  _get_context: function (e) {
	    if (!e.touches[0]) {
	      var temp = Touch._current_context;
	      temp.time = new Date();
	      return temp;
	    }

	    return {
	      target: e.target,
	      x: e.touches[0].pageX,
	      y: e.touches[0].pageY,
	      time: new Date()
	    };
	  },
	  _get_context_m: function (e) {
	    return {
	      target: e.target || e.srcElement,
	      x: e.pageX,
	      y: e.pageY,
	      time: new Date()
	    };
	  }
	};

	function touchInit() {
	  if (env.touch) {
	    Touch.$init(); //not full screen mode

	    if (document.body.className.indexOf("webix_full_screen") == -1) Touch.limit(true);
	    if (window.MSCSSMatrix) addStyle(".webix_view{ -ms-touch-action: none; }");
	  } else {
	    var id = event$1(document.body, "touchstart", function (ev) {
	      if (ev.touches.length && ev.touches[0].radiusX > 4) {
	        env.touch = true;
	        setMouse(mouse);
	        touchInit();

	        for (var key in ui.views) {
	          var view = ui.views[key];
	          if (view && view.$touch) view.$touch();
	        }
	      }

	      eventRemove(id);
	    }, {
	      capture: true
	    });
	  }
	}

	function setMouse(mouse) {
	  mouse.down = "touchstart";
	  mouse.move = "touchmove";
	  mouse.up = "touchend";
	  mouse.context = Touch._get_context;
	}

	ready(touchInit);
	var mouse = env.mouse = {
	  down: "mousedown",
	  up: "mouseup",
	  move: "mousemove",
	  context: Touch._get_context_m
	};

	if (window.navigator.pointerEnabled) {
	  mouse.down = "pointerdown";
	  mouse.move = "pointermove";
	  mouse.up = "pointerup";
	} else if (window.navigator.msPointerEnabled) {
	  mouse.down = "MSPointerDown";
	  mouse.move = "MSPointerMove";
	  mouse.up = "MSPointerUp";
	} else if (env.touch) setMouse(mouse);

	/*
		Behavior:DND - low-level dnd handling
		@export
			getContext
			addDrop
			addDrag
			
		DND master can define next handlers
			onCreateDrag
			onDragIng
			onDragOut
			onDrag
			onDrop
		all are optional
	*/

	var DragControl = {
	  //has of known dnd masters
	  _drag_masters: toArray(["dummy"]),

	  /*
	  	register drop area
	  	@param node 			html node or ID
	  	@param ctrl 			options dnd master
	  	@param master_mode 		true if you have complex drag-area rules
	  */
	  addDrop: function (node, ctrl, master_mode) {
	    node = toNode(node);
	    node.webix_drop = this._getCtrl(ctrl);
	    if (master_mode) node.webix_master = true;
	  },
	  //return index of master in collection
	  //it done in such way to prevent dnd master duplication
	  //probably useless, used only by addDrop and addDrag methods
	  _getCtrl: function (ctrl) {
	    ctrl = ctrl || DragControl;

	    var index$$1 = this._drag_masters.find(ctrl);

	    if (index$$1 < 0) {
	      index$$1 = this._drag_masters.length;

	      this._drag_masters.push(ctrl);
	    }

	    return index$$1;
	  },
	  _createTouchDrag: function (e) {
	    var dragCtrl = DragControl;

	    var master = this._getActiveDragMaster(); // for data items only


	    if (master && master._getDragItemPos) {
	      if (!dragCtrl._html) dragCtrl.createDrag(e);
	      var ctx = dragCtrl._drag_context;
	      dragCtrl._html.style.left = e.x + dragCtrl.left + (ctx.x_offset || 0) + "px";
	      dragCtrl._html.style.top = e.y + dragCtrl.top + (ctx.y_offset || 0) + "px";
	    }
	  },

	  /*
	  	register drag area
	  	@param node 	html node or ID
	  	@param ctrl 	options dnd master
	  */
	  addDrag: function (node, ctrl) {
	    node = toNode(node);
	    node.webix_drag = this._getCtrl(ctrl);

	    _event(node, env.mouse.down, this._preStart, {
	      bind: node
	    });

	    _event(node, "dragstart", preventEvent);
	  },
	  //logic of drag - start, we are not creating drag immediately, instead of that we hears mouse moving
	  _preStart: function (e) {
	    if (DragControl._active) {
	      //if we have nested drag areas, use the top one and ignore the inner one
	      if (DragControl._saved_event == e) return;

	      DragControl._preStartFalse();

	      DragControl.destroyDrag(e);
	    }

	    DragControl._active = this;
	    var evobj = env.mouse.context(e);
	    DragControl._start_pos = evobj;
	    DragControl._saved_event = e;
	    DragControl._webix_drag_mm = event$1(document.body, env.mouse.move, DragControl._startDrag);
	    DragControl._webix_drag_mu = event$1(document, env.mouse.up, DragControl._preStartFalse); //need to run here, or will not work in IE

	    addCss(document.body, "webix_noselect", 1);
	  },
	  //if mouse was released before moving - this is not a dnd, remove event handlers
	  _preStartFalse: function () {
	    DragControl._clean_dom_after_drag();
	  },
	  //mouse was moved without button released - dnd started, update event handlers
	  _startDrag: function (e) {
	    //prevent unwanted dnd
	    var pos$$1 = env.mouse.context(e);

	    var master = DragControl._getActiveDragMaster(); // only long-touched elements can be dragged


	    var longTouchLimit = master && env.touch && master._getDragItemPos && !Touch._long_touched;
	    if (longTouchLimit || Math.abs(pos$$1.x - DragControl._start_pos.x) < 5 && Math.abs(pos$$1.y - DragControl._start_pos.y) < 5) return;

	    DragControl._clean_dom_after_drag(true);

	    if (!DragControl._html) if (!DragControl.createDrag(DragControl._saved_event)) return;
	    DragControl.sendSignal("start"); //useless for now

	    DragControl._webix_drag_mm = event$1(document.body, env.mouse.move, DragControl._moveDrag);
	    DragControl._webix_drag_mu = event$1(document, env.mouse.up, DragControl._stopDrag);

	    DragControl._moveDrag(e);

	    if (env.touch) return preventEvent(e);
	  },
	  //mouse was released while dnd is active - process target
	  _stopDrag: function (e) {
	    DragControl._clean_dom_after_drag();

	    DragControl._saved_event = null;

	    if (DragControl._last) {
	      //if some drop target was confirmed
	      DragControl.$drop(DragControl._active, DragControl._last, e);
	      DragControl.$dragOut(DragControl._active, DragControl._last, null, e);
	    }

	    DragControl.destroyDrag(e);
	    DragControl.sendSignal("stop"); //useless for now
	  },
	  _clean_dom_after_drag: function (still_drag) {
	    this._webix_drag_mm = eventRemove(this._webix_drag_mm);
	    this._webix_drag_mu = eventRemove(this._webix_drag_mu);
	    if (!still_drag) removeCss(document.body, "webix_noselect");
	  },
	  //dnd is active and mouse position was changed
	  _moveDrag: function (e) {
	    var dragCtrl = DragControl;
	    var pos$$1 = pos(e); //give possibility to customize drag position

	    var customPos = dragCtrl.$dragPos(pos$$1, e); //adjust drag marker position

	    var ctx = dragCtrl._drag_context;
	    dragCtrl._html.style.top = pos$$1.y + dragCtrl.top + (customPos || !ctx.y_offset ? 0 : ctx.y_offset) + "px";
	    dragCtrl._html.style.left = pos$$1.x + dragCtrl.left + (customPos || !ctx.x_offset ? 0 : ctx.x_offset) + "px";
	    var evobj = e;
	    if (dragCtrl._skip) dragCtrl._skip = false;else {
	      if (env.touch) {
	        var context = env.mouse.context(e);
	        var target = document.elementFromPoint(context.x, context.y);
	        evobj = new Proxy(e, {
	          get: function (obj, prop) {
	            if (prop === "target") {
	              return target;
	            }

	            var res = obj[prop];

	            if (typeof res === "function") {
	              return res.bind(e);
	            }

	            return res;
	          }
	        });
	      }

	      dragCtrl._checkLand(evobj.target || evobj.srcElement, evobj);
	    }
	    return preventEvent(e);
	  },
	  //check if item under mouse can be used as drop landing
	  _checkLand: function (node, e) {
	    while (node && node.tagName != "BODY") {
	      if (node.webix_drop) {
	        //if drop area registered
	        if (this._last && (this._last != node || node.webix_master)) //if this area with complex dnd master
	          this.$dragOut(this._active, this._last, node, e); //inform master about possible mouse-out

	        if (!this._last || this._last != node || node.webix_master) {
	          //if this is new are or area with complex dnd master
	          this._last = null; //inform master about possible mouse-in

	          this._landing = this.$dragIn(DragControl._active, node, e);
	          if (this._landing) //landing was rejected
	            this._last = node;
	          return;
	        }

	        return;
	      }

	      node = node.parentNode;
	    }

	    if (this._last) //mouse was moved out of previous landing, and without finding new one 
	      this._last = this._landing = this.$dragOut(this._active, this._last, null, e);
	  },
	  //mostly useless for now, can be used to add cross-frame dnd
	  sendSignal: function (signal) {
	    DragControl.active = signal == "start";
	    callEvent("onDragMode", [signal]);
	  },
	  //return master for html area
	  getMaster: function (t) {
	    return this._drag_masters[t.webix_drag || t.webix_drop];
	  },
	  //return dhd-context object
	  getContext: function () {
	    return this._drag_context;
	  },
	  getNode: function () {
	    return this._html;
	  },
	  //called when dnd is initiated, must create drag representation
	  createDrag: function (e) {
	    var dragCtl = DragControl;
	    var a = dragCtl._active;
	    dragCtl._drag_context = {};
	    var master = this._drag_masters[a.webix_drag];
	    var drag_container; //if custom method is defined - use it

	    if (master.$dragCreate) {
	      drag_container = master.$dragCreate(a, e);
	      if (!drag_container) return false;

	      this._setDragOffset(e);

	      drag_container.style.position = "absolute";
	    } else {
	      //overvise use default one
	      var text = dragCtl.$drag(a, e);

	      dragCtl._setDragOffset(e);

	      if (!text) return false;
	      drag_container = document.createElement("DIV");
	      drag_container.innerHTML = text;
	      drag_container.className = "webix_drag_zone";
	      document.body.appendChild(drag_container);
	      var context = dragCtl._drag_context;

	      if (context.html && env.pointerevents) {
	        context.x_offset = -Math.round(drag_container.offsetWidth * 0.5);
	        context.y_offset = -Math.round(drag_container.offsetHeight * 0.75);
	      }
	    }
	    /*
	    	dragged item must have topmost z-index
	    	in some cases item already have z-index
	    	so we will preserve it if possible
	    */


	    drag_container.style.zIndex = Math.max(drag_container.style.zIndex, zIndex());
	    DragControl._skipDropH = event$1(drag_container, env.mouse.move, DragControl._skip_mark);
	    if (!DragControl._drag_context.from) DragControl._drag_context = {
	      source: a,
	      from: a
	    };
	    DragControl._html = drag_container;
	    return true;
	  },
	  //helper, prevents unwanted mouse-out events
	  _skip_mark: function () {
	    DragControl._skip = true;
	  },
	  //after dnd end, remove all traces and used html elements
	  destroyDrag: function (e) {
	    var a = DragControl._active;
	    var master = this._drag_masters[a.webix_drag];

	    if (master && master.$dragDestroy) {
	      DragControl._skipDropH = eventRemove(DragControl._skipDropH);
	      if (DragControl._html) master.$dragDestroy(a, DragControl._html, e);
	    } else {
	      remove(DragControl._html);
	    }

	    if (DragControl._dropHTML) remove(DragControl._dropHTML);
	    DragControl._landing = DragControl._active = DragControl._last = DragControl._html = DragControl._dropHTML = null; //DragControl._x_offset = DragControl._y_offset = null;
	  },
	  _getActiveDragMaster: function () {
	    return DragControl._drag_masters[DragControl._active.webix_drag];
	  },
	  top: 0,
	  //relative position of drag marker to mouse cursor
	  left: 0,
	  _setDragOffset: function (e) {
	    var dragCtl = DragControl;
	    var pos$$1 = dragCtl._start_pos;
	    var ctx = dragCtl._drag_context;
	    if (typeof ctx.x_offset != "undefined" && typeof ctx.y_offset != "undefined") return null;
	    ctx.x_offset = ctx.y_offset = 0;

	    if (env.pointerevents) {
	      var m = DragControl._getActiveDragMaster();

	      if (m._getDragItemPos && m !== this) {
	        var itemPos = m._getDragItemPos(pos$$1, e);

	        if (itemPos) {
	          ctx.x_offset = itemPos.x - pos$$1.x;
	          ctx.y_offset = itemPos.y - pos$$1.y;
	        }
	      }
	    }
	  },
	  $dragPos: function (pos$$1, e) {
	    var m = this._drag_masters[DragControl._active.webix_drag];

	    if (m.$dragPos && m != this) {
	      m.$dragPos(pos$$1, e, DragControl._html);
	      return true;
	    }
	  },
	  //called when mouse was moved in drop area
	  $dragIn: function (s, t, e) {
	    var m = this._drag_masters[t.webix_drop];
	    if (m.$dragIn && m != this) return m.$dragIn(s, t, e);
	    t.className = t.className + " webix_drop_zone";
	    return t;
	  },
	  //called when mouse was moved out drop area
	  $dragOut: function (s, t, n, e) {
	    var m = this._drag_masters[t.webix_drop];
	    if (m.$dragOut && m != this) return m.$dragOut(s, t, n, e);
	    t.className = t.className.replace("webix_drop_zone", "");
	    return null;
	  },
	  //called when mouse was released over drop area
	  $drop: function (s, t, e) {
	    var m = this._drag_masters[t.webix_drop];
	    DragControl._drag_context.from = DragControl.getMaster(s);
	    if (m.$drop && m != this) return m.$drop(s, t, e);
	    t.appendChild(s);
	  },
	  //called when dnd just started
	  $drag: function (s, e) {
	    var m = this._drag_masters[s.webix_drag];
	    if (m.$drag && m != this) return m.$drag(s, e);
	    return "<div style='" + s.style.cssText + "'>" + s.innerHTML + "</div>";
	  }
	}; //global touch-drag handler

	attachEvent("onLongTouch", function (ev) {
	  if (DragControl._active) DragControl._createTouchDrag(ev);
	});

	var Movable = {
	  move_setter: function (value) {
	    if (value) {
	      this._move_admin = clone(this._move_admin);
	      this._move_admin.master = this;
	      DragControl.addDrag(this._headobj ? this._headobj : this.$view, this._move_admin);
	    }

	    return value;
	  },
	  _move_admin: {
	    $dragCreate: function (object, e) {
	      if (this.master.config.move) {
	        var elOffset = offset(object);
	        var elPos = pos(e);
	        DragControl.top = elOffset.y - elPos.y;
	        DragControl.left = elOffset.x - elPos.x;
	        return toNode(this.master._viewobj);
	      }
	    },
	    $dragDestroy: function (node, drag) {
	      var view = this.master;

	      if (view._settings) {
	        view._settings.top = parseInt(drag.style.top, 10);
	        view._settings.left = parseInt(drag.style.left, 10);
	      }

	      DragControl.top = DragControl.left = 0;
	      this.master.callEvent("onViewMoveEnd", []);
	      return;
	    },
	    $dragPos: function (pos$$1, e) {
	      this.master.callEvent("onViewMove", [pos$$1, e]);
	    }
	  }
	};

	var Modality = {
	  _modal_set: function (value) {
	    if (value) {
	      if (!this._modal_cover) {
	        this._modal_cover = create("div", {
	          "class": "webix_modal"
	        });
	        /*	with below code we will have the same zIndex for modal layer as for the previous 
	        	abs positioned element, but because of attaching order modal layer will be on top anyway
	        */

	        var index$$1 = this._settings.zIndex || zIndex(); //set topmost modal layer

	        this._previous_modality = state._modality;
	        state._modality = index$$1;
	        this._modal_cover.style.zIndex = index$$1 - 1;
	        this._viewobj.style.zIndex = index$$1;
	        document.body.appendChild(this._modal_cover);
	        document.body.style.overflow = "hidden";

	        _event(this._modal_cover, "click", bind(this._ignore_clicks, this));
	      }
	    } else {
	      if (this._modal_cover) {
	        remove(this._modal_cover);
	        document.body.style.overflow = ""; //restore topmost modal layer
	        //set delay, as current window closing may have not finished click event
	        //need to wait while it is not fully processed

	        var topmost = this._previous_modality;
	        setTimeout(function () {
	          state._modality = topmost;
	        }, 1);
	        this._modal_cover = null;
	      }
	    }

	    return value;
	  }
	};

	var ResizeArea = {
	  resize_setter: function (value) {
	    if (value && !this._resizeHandlers) this._renderResizeHandler();
	    return value;
	  },
	  _renderResizeHandler: function () {
	    if (!this._rwHandle) {
	      var rp = this._viewobj;

	      if (rp.firstChild) {
	        rp = rp.firstChild;
	        rp.style.position = "relative";
	      }

	      this._rwHandle = create("DIV", {
	        "class": "webix_resize_handle",

	        /*@attr*/
	        "webix_disable_drag": "true"
	      });
	      rp.appendChild(this._rwHandle);

	      _event(this._rwHandle, env.mouse.down, this._wrDown, {
	        bind: this
	      });
	    }
	  },
	  _showResizeFrame: function (width, height) {
	    if (!this._resizeFrame) {
	      this._resizeFrame = create("div", {
	        "class": "webix_resize_frame"
	      }, "");
	      document.body.appendChild(this._resizeFrame);
	      var elPos = offset(this._viewobj);
	      this._resizeFrame.style.left = elPos.x + "px";
	      this._resizeFrame.style.top = elPos.y + "px";
	      this._resizeFrame.style.zIndex = zIndex();
	    }

	    this._resizeFrame.style.width = width + "px";
	    this._resizeFrame.style.height = height + "px";
	  },
	  _wrDown: function () {
	    if (this.config.resize) {
	      addCss(document.body, "webix_noselect webix_resize_cursor");
	      this._wsReady = offset(this._viewobj);
	      this._resizeHandlersMove = event$1(document.body, env.mouse.move, this._wrMove, {
	        bind: this
	      });
	      this._resizeHandlersUp = event$1(document.body, env.mouse.up, this._wrUp, {
	        bind: this
	      });
	    }
	  },
	  _wrMove: function (e) {
	    if (this._wsReady !== false) {
	      var elPos = pos(e);
	      var progress = {
	        x: elPos.x - this._wsReady.x + 10,
	        y: elPos.y - this._wsReady.y + 10
	      };
	      if (this.$resizeMove) this.$resizeMove(progress);else {
	        var width = Math.abs(this._wsReady.x - elPos.x),
	            height = Math.abs(this._wsReady.y - elPos.y);
	        if (width < (this.config.minWidth || 100) || height < (this.config.minHeight || 100) || width > this._settings.maxWidth || height > this._settings.maxHeight) return;
	      }
	      this._wsProgress = progress;

	      this._showResizeFrame(progress.x, progress.y);
	    }
	  },
	  _wrUp: function () {
	    // remove resize frame and css styles
	    if (this._resizeFrame) this._resizeFrame = remove(this._resizeFrame);
	    removeCss(document.body, "webix_resize_cursor");
	    removeCss(document.body, "webix_noselect");
	    eventRemove(this._resizeHandlersMove);
	    eventRemove(this._resizeHandlersUp); // set Window sizes

	    if (this._wsProgress) {
	      if (this.$resizeEnd) this.$resizeEnd(this._wsProgress);else {
	        this.config.width = this._wsProgress.x;
	        this.config.height = this._wsProgress.y;
	        this.resize();
	      }
	    }

	    this._wsReady = this._wsProgress = false;
	    this.callEvent("onViewResize", []);
	  }
	};

	state._popups = toArray();
	var api$3 = {
	  name: "window",
	  $init: function (config) {
	    this._viewobj.innerHTML = "<div class='webix_win_content'><div class='webix_win_head'></div><div class='webix_win_body'></div></div>";
	    this._contentobj = this._viewobj.firstChild;
	    this._headobj = this._contentobj.childNodes[0];
	    this._dataobj = this._bodyobj = this._contentobj.childNodes[1];
	    this._viewobj.className += " webix_window";

	    this._viewobj.setAttribute("role", "dialog");

	    this._viewobj.setAttribute("tabindex", "0");

	    this._head_cell = this._body_cell = null;
	    config._inner = {
	      top: false,
	      left: false,
	      right: false,
	      bottom: false
	    }; //set border flags

	    if (!config.id) config.id = uid();

	    _event(this._contentobj, "click", bind(this._ignore_clicks, this)); // IE8 does not allow to define event capturing


	    if (this._contentobj.addEventListener) _event(this._contentobj, "click", function () {
	      // brings a window to the front of other windows
	      if (!this._settings.zIndex && this._settings.toFront) {
	        this._viewobj.style.zIndex = zIndex();
	      }
	    }, {
	      bind: this,
	      capture: true
	    }); // hidden_setter handling

	    if (config.modal) this._modal = true; // head_setter handling

	    if (config.close) this._settings.close = config.close;
	    this.attachEvent("onViewMoveEnd", function () {
	      if (this._settings.position) delete this._settings.position;
	    });
	  },
	  _ignore_clicks: function (e) {
	    var popups = state._popups;
	    var index$$1 = popups.find(this);
	    if (index$$1 == -1) index$$1 = popups.length - 1;
	    e.click_view = index$$1;
	    if (env.isIE8) e.srcElement.click_view = index$$1;
	  },
	  getChildViews: function () {
	    if (this._head_cell) return [this._head_cell, this._body_cell];else return [this._body_cell];
	  },
	  zIndex_setter: function (value) {
	    this._viewobj.style.zIndex = value;
	    return value;
	  },
	  _remove: function () {
	    this.body_setter();
	  },
	  _replace: function (new_view, old_view) {
	    old_view = old_view || this._body_cell;
	    var isBody = old_view == this._body_cell;
	    old_view.destructor();
	    if (isBody) this._body_cell = new_view;else this._head_cell = new_view;
	    (isBody ? this._bodyobj : this._headobj).appendChild(new_view._viewobj);
	    var cell = new_view._viewobj.style;
	    var settings = {
	      top: true,
	      left: true,
	      right: true,
	      bottom: true
	    };
	    var size = "0px";

	    if (new_view.config.borderless === false) {
	      settings = clone(this._settings._inner);
	      size = "1px";
	    }

	    new_view._settings._inner = settings;
	    cell.borderTopWidth = cell.borderBottomWidth = cell.borderLeftWidth = cell.borderRightWidth = size;
	    this.resize(true);
	  },
	  show: function (node, mode, point) {
	    if (node === true) {
	      //recursive call from some child item
	      if (!this._settings.hidden) return;
	      node = null;
	    }

	    if (!this.callEvent("onBeforeShow", arguments)) return false;
	    this._settings.hidden = false;
	    this._viewobj.style.zIndex = this._settings.zIndex || zIndex();

	    if (this._settings.modal || this._modal) {
	      this._modal_set(true);

	      this._modal = null; // hidden_setter handling
	    }

	    var elPos, dx, dy;
	    mode = mode || {};
	    if (!mode.pos) mode.pos = this._settings.relative; //get position of source html node
	    //we need to show popup which pointing to that node

	    if (node) {
	      //if event was provided - get node info from it
	      if (_typeof(node) == "object" && !node.tagName) {
	        /*below logic is far from ideal*/
	        if (node.target || node.srcElement) {
	          elPos = pos(node);
	          dx = 20;
	          dy = 5;
	        } else elPos = node;
	      } else {
	        node = toNode(node);
	        assert(node, "Not existing target for window:show");
	        elPos = offset(node);
	      } //size of body, we need to fit popup inside


	      var x = Math.max(window.innerWidth || 0, document.body.offsetWidth);
	      var y = Math.max(window.innerHeight || 0, document.body.offsetHeight); //size of node, near which popup will be rendered

	      dx = dx || node.offsetWidth || 0;
	      dy = dy || node.offsetHeight || 0; //size of popup element

	      var size = this._last_size;
	      var fin_x = elPos.x;
	      var fin_y = elPos.y;
	      var point_y = 0;
	      var point_x = 0;
	      var scrollLeft = 0,
	          scrollTop = 0;
	      var fit = this._settings.autofit;

	      if (fit) {
	        var nochange = fit === "node";
	        var delta_x = 6;
	        var delta_y = 6;
	        var delta_point = 6;
	        if (!this._settings.point) delta_x = delta_y = delta_point = 0; //default pointer position - top 

	        point = "top";
	        fin_y = 0;
	        fin_x = 0;
	        scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft; //if we want to place menu at righ, but there is no place move it to left instead

	        if (x - elPos.x - dx < size[0] && mode.pos == "right" && !nochange) mode.pos = "left";

	        if (mode.pos == "right") {
	          fin_x = elPos.x + delta_x + dx;
	          delta_y = -dy;
	          point = "left";
	          point_y = Math.round(elPos.y + dy / 2);
	          point_x = fin_x - delta_point;
	        } else if (mode.pos == "left") {
	          fin_x = elPos.x - delta_x - size[0] - 1;
	          delta_y = -dy;
	          point = "right";
	          point_y = Math.round(elPos.y + dy / 2);
	          point_x = fin_x + size[0] + 1;
	        } else {
	          //left border of screen
	          if (elPos.x < scrollLeft) {
	            fin_x = scrollLeft; //popup exceed the right border of screen
	          } else if (x + scrollLeft - elPos.x > size[0]) {
	            fin_x = elPos.x; //aligned
	          } else {
	            fin_x = x + scrollLeft - delta_x - size[0]; //not aligned
	          }

	          point_x = Math.round(elPos.x + dx / 2); //when we have a small popup, point need to be rendered at center of popup

	          point_x = Math.min(point_x, fin_x + size[0] - delta_point * 3);
	        } //if height is not fixed - use default position


	        scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;

	        if ((!size[1] || y + scrollTop - dy - elPos.y - delta_y > size[1] || nochange) && mode.pos != "top") {
	          //bottom	
	          fin_y = dy + elPos.y + delta_y - (!this._settings.point ? 0 : 4);

	          if (!point_y) {
	            point = "top";
	            point_y = fin_y - delta_point;
	          }
	        } else {
	          //top
	          fin_y = elPos.y - delta_y - size[1];

	          if (fin_y < 0) {
	            fin_y = 0; //left|right point can be used, but there is no place for top point

	            if (point == "top") point = false;
	          } else if (!point_y) {
	            point = "bottom";
	            fin_y--;
	            point_y = fin_y + size[1] + 1;
	          }
	        }
	      }

	      var deltax = mode.x || 0;
	      var deltay = mode.y || 0;

	      var fixed = this._checkFixedPosition();

	      if (fixed) {
	        fin_y = fin_y - scrollTop;
	        point_y = point_y - scrollTop;
	      }

	      this.setPosition(fin_x + deltax, fin_y + deltay);

	      if (this._set_point) {
	        if (point && this._settings.point) this._set_point(point, point_x + deltax, point_y + deltay, fixed);else this._hide_point();
	      }
	    } else this._setPosition(this._settings.left, this._settings.top);

	    this._viewobj.style.display = "block";
	    this._hide_timer = 1;
	    delay(function () {
	      this._hide_timer = 0;
	    }, this, [], env.touch ? 400 : 100);

	    this._render_hidden_views();

	    if (this.config.autofocus) {
	      this._prev_focus = UIManager.getFocus();
	      UIManager.setFocus(this);
	    }

	    if (-1 == state._popups.find(this)) state._popups.push(this);
	    this.callEvent("onShow", []);
	  },
	  _hide: function (e) {
	    //do not hide modal windows
	    if (this._settings.hidden || this._settings.modal || this._hide_timer || e && e.showpopup) return; //do not hide popup, when we have modal layer above the popup

	    if (state._modality && this._settings.zIndex <= state._modality) return; //ignore inside clicks and clicks in child-popups

	    if (e) {
	      var index$$1 = env.isIE8 ? e.srcElement.click_view : e.click_view;
	      if (!index$$1 && index$$1 !== 0) index$$1 = -1;

	      var myindex = state._popups.find(this);

	      if (myindex <= index$$1) return;
	    }

	    this.hide();
	  },
	  hidden_setter: function (value) {
	    if (value) this.hide();else this.show();
	    return !!value;
	  },
	  hide: function (force) {
	    if (this.$destructed) return;
	    if (!force) if (this._settings.hidden) return;
	    if (this._settings.modal) this._modal_set(false);

	    if (this._settings.position == "top") {
	      animate(this._viewobj, {
	        type: "slide",
	        x: 0,
	        y: -(this._content_height + 20),
	        duration: 300,
	        callback: this._hide_callback,
	        master: this
	      });
	    } else this._hide_callback();

	    if (this._settings.autofocus) {
	      var el = document.activeElement; //as result of hotkey, we can have a activeElement set to document.body

	      if (el && this._viewobj && (this._viewobj.contains(el) || el === document.body)) {
	        UIManager.setFocus(this._prev_focus);
	        this._prev_focus = null;
	      }
	    }

	    this._hide_sub_popups();
	  },
	  //hide all child-popups
	  _hide_sub_popups: function () {
	    var order = state._popups;
	    var index$$1 = order.find(this);
	    var size = order.length - 1;
	    if (index$$1 > -1) for (var i = size; i > index$$1; i--) {
	      if (order[i]._hide_point) //hide only popups, skip windows
	        order[i].hide();
	    }
	    order.removeAt(index$$1);
	  },
	  destructor: function () {
	    this._modal_set(false);

	    remove(this._viewobj);

	    if (this._settings.autofocus) {
	      if (!state._final_destruction) UIManager.setFocus(this._prev_focus);
	      this._prev_focus = null;
	    }

	    this._hide_sub_popups();

	    if (this._hide_point) this._hide_point();
	    Destruction.destructor.apply(this, []);
	  },
	  _hide_callback: function () {
	    if (!this.$destructed) {
	      this._viewobj.style.display = "none";
	      this._settings.hidden = true;
	      this.callEvent("onHide", []);
	    }
	  },
	  close: function () {
	    this.destructor();
	  },
	  _inner_body_set: function (value) {
	    if (typeof value.borderless == "undefined") value.borderless = true;
	  },
	  body_setter: function (value) {
	    if (_typeof(value) != "object") value = {
	      template: value
	    };

	    this._inner_body_set(value);

	    state._parent_cell = this;
	    this._body_cell = ui._view(value);

	    this._bodyobj.appendChild(this._body_cell._viewobj);

	    return value;
	  },
	  head_setter: function (value) {
	    var _this = this;

	    if (value === false) return value;
	    var text = typeof value == "string";
	    var config = {
	      padding: 0,
	      css: "webix_win_title",
	      type: "header",
	      borderless: true
	    };

	    if (text) {
	      this._viewobj.setAttribute("aria-label", value);

	      value = {
	        template: value
	      };
	    }

	    if (value.view == "template" || !value.view && value.template) {
	      exports.extend(value, config);
	    }

	    if (text && this.config.close) {
	      value = {
	        padding: {
	          left: $active.inputHeight + 2,
	          right: 2
	        },
	        cols: [value, {
	          view: "icon",
	          icon: "wxi-close",
	          click: function () {
	            _this.hide();
	          }
	        }]
	      };
	    }

	    state._parent_cell = this;
	    this._head_cell = ui._view(value);

	    this._headobj.appendChild(this._head_cell._viewobj);

	    return value;
	  },
	  getBody: function () {
	    return this._body_cell;
	  },
	  getHead: function () {
	    return this._head_cell;
	  },
	  adjust: function () {
	    return this.resize();
	  },
	  resizeChildren: function () {
	    if (this._body_cell) this.resize();
	  },
	  resize: function () {
	    baseview.api.adjust.call(this);

	    if (this.isVisible()) {
	      this._setPosition(this._settings.left, this._settings.top);
	    }
	  },
	  _checkFixedPosition: function () {
	    if (this._settings.master) {
	      var top = $$(this._settings.master).getTopParentView().$view;
	      return top && top.style.position === "fixed";
	    }

	    return false;
	  },
	  _setPosition: function (x, y) {
	    if (this._settings.position || this._checkFixedPosition()) {
	      this.$view.style.position = "fixed";
	      var width = this._content_width;
	      var height = this._content_height;
	      if (width <= 0 || height <= 0) return;
	      var maxWidth = window.innerWidth || document.documentElement.offsetWidth;
	      var maxHeight = window.innerHeight || document.documentElement.offsetHeight;
	      var left = Math.round((maxWidth - width) / 2);
	      var top = Math.round((maxHeight - height) / 2);

	      if (typeof this._settings.position == "function") {
	        var state$$1 = {
	          left: left,
	          top: top,
	          width: width,
	          height: height,
	          maxWidth: maxWidth,
	          maxHeight: maxHeight
	        };

	        this._settings.position.call(this, state$$1);

	        if (state$$1.width != width || state$$1.height != height) this.$setSize(state$$1.width, state$$1.height);
	        this.setPosition(state$$1.left, state$$1.top);
	      } else {
	        if (this._settings.position == "top") {
	          if (animate.isSupported()) top = -1 * height;else top = 10;
	        } //popup inside a fixed win


	        if (!this._settings.position) {
	          left = this._settings.left || left;
	          top = this._settings.top || top;
	        }

	        this.setPosition(left, top);
	      }

	      if (this._settings.position == "top") animate(this._viewobj, {
	        type: "slide",
	        x: 0,
	        y: height - (this._settings.padding || 0) * 2,
	        duration: 300,
	        callback: this._topPositionCallback,
	        master: this
	      });
	    } else this.setPosition(x, y);
	  },
	  _topPositionCallback: function (node) {
	    animate.clear(node);
	    this._settings.top = -((this._settings.padding || 0) * 2);
	    this.setPosition(this._settings.left, this._settings.top);
	  },
	  setPosition: function (x, y) {
	    this._viewobj.style.top = y + "px";
	    this._viewobj.style.left = x + "px";
	    this._settings.left = x;
	    this._settings.top = y;
	  },
	  $getSize: function (dx, dy) {
	    var _borders = this._settings._inner;

	    if (_borders) {
	      dx += (_borders.left ? 0 : 1) + (_borders.right ? 0 : 1);
	      dy += (_borders.top ? 0 : 1) + (_borders.bottom ? 0 : 1);
	    } //line between head and body


	    if (this._settings.head) dy += 1;

	    var size = this._body_cell.$getSize(0, 0);

	    var headMinWidth = 0;

	    if (this._head_cell) {
	      var head_size = this._head_cell.$getSize(0, 0);

	      if (head_size[3] == head_size[2]) this._settings.headHeight = head_size[3];
	      dy += this._settings.headHeight;
	      headMinWidth = head_size[0];
	    }

	    if (this._settings.fullscreen) {
	      var width = window.innerWidth || document.body.clientWidth;
	      var height = window.innerHeight || document.body.clientHeight;
	      return [width, width, height, height];
	    } //get layout sizes


	    var self_size = base.api.$getSize.call(this, 0, 0); //use child settings if layout's one was not defined

	    if (headMinWidth && size[1] > 100000) size[0] = Math.max(headMinWidth, size[0]);
	    self_size[1] = Math.min(self_size[1], (size[1] >= 100000 && self_size[1] >= 100000 ? Math.max(size[0], self_size[0]) : size[1]) + dx);
	    self_size[3] = Math.min(self_size[3], (size[3] >= 100000 && self_size[3] >= 100000 ? Math.max(size[2], self_size[2]) : size[3]) + dy);
	    self_size[0] = Math.min(Math.max(self_size[0], size[0] + dx), self_size[1]);
	    self_size[2] = Math.min(Math.max(self_size[2], size[2] + dy), self_size[3]);
	    return self_size;
	  },
	  $setSize: function (x, y) {
	    base.api.$setSize.call(this, x, y);
	    x = this._content_width;
	    y = this._content_height;

	    if (this._settings.head === false) {
	      this._headobj.style.display = "none";

	      this._body_cell.$setSize(x, y);
	    } else {
	      this._head_cell.$setSize(x, this._settings.headHeight);

	      this._body_cell.$setSize(x, y - this._settings.headHeight);
	    }
	  },
	  $skin: function () {
	    this.defaults.headHeight = $active.barHeight;
	  },
	  defaults: {
	    top: 0,
	    left: 0,
	    autofit: true,
	    relative: "bottom",
	    body: "",
	    head: "",
	    hidden: true,
	    autofocus: true,
	    minWidth: 300,
	    minHeight: 200
	  }
	};
	var view$3 = exports.protoUI(api$3, base.view, Movable, Modality, EventSystem, ResizeArea);
	var window$1 = {
	  api: api$3,
	  view: view$3
	};

	var ContextHelper = {
	  defaults: {
	    padding: "4",
	    hidden: true
	  },
	  body_setter: function (value) {
	    value = window$1.api.body_setter.call(this, value);
	    this._body_cell._viewobj.style.borderWidth = "0px";
	    return value;
	  },
	  attachTo: function (obj) {
	    assert(obj, "Invalid target for Context::attach");
	    var id;
	    if (obj.on_context) id = obj.attachEvent("onAfterContextMenu", bind(this._show_at_ui, this));else id = event$1(obj, "contextmenu", this._show_at_node, {
	      bind: this
	    });
	    this.attachEvent("onDestruct", function () {
	      if (obj.callEvent) obj.detachEvent(id);else eventRemove(id);
	      obj = null;
	    });
	  },
	  getContext: function () {
	    return this._area;
	  },
	  setContext: function (area) {
	    this._area = area;
	  },
	  _show_at_node: function (e) {
	    this._area = toNode(e || event$1);
	    return this._show_at(e);
	  },
	  _show_at_ui: function (id, e) {
	    this._area = {
	      obj: $$(e),
	      id: id
	    };
	    return this._show_at(e);
	  },
	  _show_at: function (e) {
	    var result = this.show(e, null, true);
	    if (result === false) return result; //event forced to close other popups|context menus

	    callEvent("onClick", []);
	    return preventEvent(e);
	  },
	  _show_on_mouse_out: true,
	  master_setter: function (value) {
	    this.attachTo(value);
	    return null;
	  }
	};

	var clipbuffer = {
	  _area: null,
	  _blur_id: null,
	  _ctrl: 0,

	  /*! create textarea or returns existing
	   **/
	  init: function () {
	    // returns existing textarea
	    if (this._area !== null) return this._area;
	    state.destructors.push({
	      obj: this
	    }); // creates new textarea

	    this._area = document.createElement("textarea");
	    this._area.className = "webix_clipbuffer";

	    this._area.setAttribute(
	    /*@attr*/
	    "webixignore", 1);

	    this._area.setAttribute("spellcheck", "false");

	    this._area.setAttribute("autocapitalize", "off");

	    this._area.setAttribute("autocorrect", "off");

	    this._area.setAttribute("autocomplete", "off");

	    document.body.appendChild(this._area);
	    event$1(document.body, "keydown", bind(function (e) {
	      var key = e.keyCode;
	      var ctrl = !!(e.ctrlKey || e.metaKey);

	      if (key === 86 && ctrl) {
	        this._area.value = "";
	        delay(this._paste, this, [e], 100);
	      }
	    }, this));
	    return this._area;
	  },
	  destructor: function () {
	    this._area = null;
	  },

	  /*! set text into buffer
	   **/
	  set: function (text) {
	    this.init();
	    text = text === "" ? "\n" : text;
	    this._area.value = text;
	    this.focus();
	  },

	  /*! select text in textarea
	   **/
	  focus: function () {
	    // if there is native browser selection, skip focus
	    if (!this._isSelectRange()) {
	      this.init();

	      this._area.focus();

	      this._area.select();
	    }
	  },

	  /*! checks document selection
	   **/
	  _isSelectRange: function () {
	    var text = "";

	    if (typeof window.getSelection != "undefined") {
	      text = window.getSelection().toString();
	    } else if (typeof document.selection != "undefined" && document.selection.type == "Text") {
	      text = document.selection.createRange().text;
	    }

	    return !!text;
	  },

	  /*! process ctrl+V pressing
	   **/
	  _paste: function (e) {
	    var trg = e.target || e.srcElement;

	    if (trg === this._area) {
	      var text = this._area.value;
	      var last_active = UIManager.getFocus();

	      if (last_active && (!last_active.getEditor || !last_active.getEditor())) {
	        last_active.callEvent("onPaste", [text]);

	        this._area.select();
	      }
	    }
	  }
	};

	var CopyPaste = {
	  clipboard_setter: function (value) {
	    if (value === true || value === 1) value = "modify";
	    this.attachEvent("onAfterSelect", function (id) {
	      var item = this.getItem(id);
	      var text = this.type.templateCopy(item);
	      clipbuffer.set(text, this);
	      clipbuffer.focus();
	      UIManager.setFocus(this);
	    });
	    this.attachEvent("onPaste", function (text) {
	      if (!isUndefined(this._paste[this._settings.clipboard])) this._paste[this._settings.clipboard].call(this, text);
	    });
	    this.attachEvent("onFocus", function () {
	      clipbuffer.focus();
	    }); // solution for clicks on selected items

	    this.attachEvent("onItemClick", function (id) {
	      if (!this._selected || this._selected.find(id) !== -1) {
	        clipbuffer.focus();
	        UIManager.setFocus(this);
	      }
	    });
	    return value;
	  },
	  _paste: {
	    // insert new item with pasted value
	    insert: function (text) {
	      this.add({
	        value: text
	      });
	    },
	    // change value of each selected item
	    modify: function (text) {
	      var sel = this.getSelectedId(true);

	      for (var i = 0; i < sel.length; i++) {
	        this.getItem(sel[i]).value = text;
	        this.refresh(sel[i]);
	      }
	    },
	    // do nothing
	    custom: function () {}
	  },
	  templateCopy_setter: function (value) {
	    this.type.templateCopy = template(value);
	  },
	  type: {
	    templateCopy: function (item) {
	      return this.template(item);
	    }
	  }
	};

	var CustomPrint = {
	  $customPrint: function (options, htmlOnly) {
	    if (this._prePrint(options, htmlOnly)) return true;

	    var tableData = this._getTableArray(options);

	    var table = this._getTableHTML(tableData, options);

	    if (htmlOnly) return table;
	    var doc = create("div", {
	      "class": "webix_ui_print"
	    });
	    doc.appendChild(table);
	    insertBefore(doc, options.docFooter, document.body);
	    window.print();
	    remove(doc);
	  },
	  _prePrint: function (options, htmlOnly) {
	    if (!htmlOnly && (this.config.layout == "y" || options.scroll || this.config.prerender || this.config.autoheight)) return true;
	    if (this.config.layout == "x") exports.extend(options || {}, {
	      xCount: this.count(),
	      nobreaks: true
	    }, true);
	  },
	  _getPageWidth: function (options) {
	    if (options.fit == "page") return Infinity;
	    var size = options.size;
	    var width = size[options.mode == "portrait" ? "width" : "height"];
	    return Math.min(width * env.printPPI - 2 * env.printMargin);
	  },
	  _getTableArray: function (options, base, start) {
	    var maxWidth = this._getPageWidth(options);

	    var xCount = options.xCount || this._getVisibleRange()._dx;

	    var tableArray = [];
	    var colrow = [];
	    var width = 0;
	    var newTableStart, rownum, colnum;
	    start = start || 0;
	    base = base || [];

	    for (var i = 0; i < this.data.order.length;) {
	      var obj = this.data.pull[this.data.order[i]];
	      rownum = parseInt(i / xCount);
	      colnum = i - rownum * xCount;

	      if (obj && colnum >= start) {
	        width += this.type.width; //start a new table, if cells do not fit page width

	        if (width > maxWidth && colnum > start) {
	          // 'colnum>start' ensures that a single long cell will have to fit the page
	          newTableStart = colrow.length + start;
	          tableArray.push(colrow);
	          i = i + (xCount - colrow.length);
	          colrow = [];
	          width = 0;
	          continue;
	        }

	        var cellValue = this.type.template(obj, this.type);
	        var className = this._itemClassName;
	        var style = {
	          display: "table-cell",
	          height: this.type.height + "px",
	          width: this.type.width + "px"
	        }; //push a cell to a row

	        colrow.push({
	          txt: cellValue,
	          className: className + " " + (obj.$css || ""),
	          style: style
	        }); //push a row to a table and start a new row

	        if ((i + 1) % xCount === 0) {
	          tableArray.push(colrow);
	          colrow = [];
	          width = 0;
	        }
	      }

	      i++;
	    }

	    base.push(tableArray);
	    if (newTableStart) this._getTableArray(options, base, newTableStart);
	    return base;
	  },
	  _getTableHTML: function (tableData, options) {
	    var container = create("div");
	    tableData.forEach(bind(function (table, i) {
	      var tableHTML = create("table", {
	        "class": "webix_table_print " + this.$view.className,
	        "style": "border-collapse:collapse"
	      });
	      table.forEach(function (row) {
	        var tr = create("tr");
	        row.forEach(function (column) {
	          var td = create("td");
	          if (column.txt) td.innerHTML = column.txt;
	          if (column.className) td.className = column.className;

	          if (column.style) {
	            var keys = Object.keys(column.style);
	            keys.forEach(function (key) {
	              if (column.style[key]) td.style[key] = column.style[key];
	            });
	          }

	          if (column.span) {
	            if (column.span.colspan > 1) td.colSpan = column.span.colspan;
	            if (column.span.rowspan > 1) td.rowSpan = column.span.rowspan;
	          }

	          tr.appendChild(td);
	        });
	        tableHTML.appendChild(tr);
	      });
	      container.appendChild(tableHTML);

	      if (!options.nobreaks && i + 1 < tableData.length) {
	        var br = create("DIV", {
	          "class": "webix_print_pagebreak"
	        });
	        container.appendChild(br);
	      }
	    }, this));
	    return container;
	  }
	};

	var temp$1 = null;

	var DataMarks = {
	  addCss: function (id, css, silent) {
	    if (!this.addRowCss && !silent) {
	      if (!this.hasCss(id, css)) {
	        var node = this.getItemNode(id);

	        if (node) {
	          node.className += " " + css;
	          silent = true;
	        }
	      }
	    }

	    return this.data.addMark(id, css, 1, 1, silent);
	  },
	  removeCss: function (id, css, silent) {
	    if (!this.addRowCss && !silent) {
	      if (this.hasCss(id, css)) {
	        var node = this.getItemNode(id);

	        if (node) {
	          node.className = node.className.replace(css, "").replace("  ", " ");
	          silent = true;
	        }
	      }
	    }

	    return this.data.removeMark(id, css, 1, silent);
	  },
	  hasCss: function (id, mark) {
	    return this.data.getMark(id, mark);
	  },
	  clearCss: function (css, silent) {
	    return this.data.clearMark(css, 1, silent);
	  }
	};

	/*
		Behavior:DataMove - allows to move and copy elements, heavily relays on DataStore.move
		@export
			copy
			move
	*/

	var DataMove = {
	  //creates a copy of the item
	  copy: function (sid, tindex, tobj, details) {
	    details = details || {};
	    var new_id = details.newId || sid;
	    tobj = tobj || this;
	    var data = this.getItem(sid);
	    assert(data, "Incorrect ID in DataMove::copy"); //make data conversion between objects

	    if (tobj) data = tobj._externalData(data); //adds new element same as original

	    return tobj.data.add(tobj._externalData(data, new_id), tindex, details.parent || 0);
	  },
	  _next_move_index: function (nid, next, source) {
	    if (next && nid) {
	      var new_index = this.getIndexById(nid);
	      return new_index + (source == this && source.getIndexById(next) < new_index ? 0 : 1);
	    }
	  },
	  //move item to the new position
	  move: function (sid, tindex, tobj, details) {
	    details = details || {};
	    var new_id = details.newId || sid;
	    tobj = tobj || this;
	    assert(tobj.data, "moving attempt to component without datastore");
	    if (!tobj.data) return; //can process an arrya - it allows to use it from onDrag 

	    if (isArray(sid)) {
	      //block separate repaint operations
	      if (sid.length > 3) //heuristic value, duplicated below
	        this.$blockRender = tobj.$blockRender = true;

	      for (var i = 0; i < sid.length; i++) {
	        //increase index for each next item in the set, so order of insertion will be equal to order in the array
	        var _nid = this.move(sid[i], tindex, tobj, details);

	        tindex = tobj._next_move_index(_nid, sid[i + 1], this);
	      }

	      this.$blockRender = tobj.$blockRender = false;

	      if (sid.length > 3) {
	        //repaint whole component
	        this.refresh();
	        if (tobj != this) tobj.refresh();
	      }

	      return;
	    }

	    var nid = sid; //id after moving

	    var data = this.getItem(sid);
	    assert(data, "Incorrect ID in DataMove::move");

	    if (!tobj || tobj == this) {
	      if (tindex < 0) tindex = this.data.order.length - 1;
	      this.data.move(this.getIndexById(sid), tindex); //move inside the same object

	      this.data.callEvent("onDataMove", [sid, tindex, null, this.data.order[tindex + 1]]);
	    } else {
	      //copy to the new object
	      nid = tobj.data.add(tobj._externalData(data, new_id), tindex, details.parent || 0);
	      this.data.remove(sid); //delete in old object
	    }

	    return nid; //return ID of item after moving
	  },
	  //move item on one position up
	  moveUp: function (id, step) {
	    return this.move(id, this.getIndexById(id) - (step || 1));
	  },
	  //move item on one position down
	  moveDown: function (id, step) {
	    return this.moveUp(id, (step || 1) * -1);
	  },
	  //move item to the first position
	  moveTop: function (id) {
	    return this.move(id, 0);
	  },
	  //move item to the last position
	  moveBottom: function (id) {
	    return this.move(id, this.data.count() - 1);
	  },

	  /*
	  	this is a stub for future functionality
	  	currently it just makes a copy of data object, which is enough for current situation
	  */
	  _externalData: function (data, id) {
	    var newdata = exports.extend({}, data);
	    newdata.id = !id || this.data.pull[id] ? uid() : id;
	    newdata.$template = null;
	    if (this._settings.externalData) newdata = this._settings.externalData.call(this, newdata, id, data);
	    return newdata;
	  }
	};

	var rules = {
	  isEmail: function (value) {
	    return /\S+@[^@\s]+\.[^@\s]+$/.test((value || "").toString());
	  },
	  isNumber: function (value) {
	    return parseFloat(value) == value;
	  },
	  isChecked: function (value) {
	    return !!value || value === "0";
	  },
	  isNotEmpty: function (value) {
	    return value === 0 || value;
	  }
	};

	var ValidateData = {
	  $init: function () {
	    if (this._events) this.attachEvent("onChange", this.clearValidation);
	  },
	  clearValidation: function () {
	    if (this.elements) {
	      for (var id in this.elements) {
	        this._clear_invalid(id);
	      }
	    }
	  },
	  validate: function (mode, obj) {
	    assert(this.callEvent, "using validate for eventless object");
	    this.callEvent("onBeforeValidate", []);
	    var failed = this._validate_details = {}; //optimistic by default :) 

	    var result = true;
	    var rules$$1 = this._settings.rules;
	    var isHidden = this.isVisible && !this.isVisible();
	    var validateHidden = mode && mode.hidden;
	    var validateDisabled = mode && mode.disabled; //prevent validation of hidden elements

	    var elements = {},
	        hidden = {};

	    for (var i in this.elements) {
	      var name = this.elements[i].config.name; //we are ignoring hidden and disabled fields during validation
	      //if mode doesn not instruct us otherwise
	      //if form itself is hidden, we can't separate hidden fiels,
	      //so we will vaidate all fields

	      if ((isHidden || this.elements[i].isVisible() || validateHidden) && (this.elements[i].isEnabled() || validateDisabled)) elements[name] = this.elements[i];else {
	        hidden[name] = true;
	      }
	    }

	    if (rules$$1 || elements) if (!obj && this.getValues) obj = this.getValues();

	    if (rules$$1) {
	      //complex rule, which may chcek all properties of object
	      if (rules$$1.$obj) result = this._validate(rules$$1.$obj, obj, obj, "") && result; //all - applied to all fields

	      var all = rules$$1.$all;
	      var data = obj;
	      if (this._settings.complexData) data = CodeParser.collapseNames(obj, "", {}, function (v) {
	        return !rules$$1[v];
	      });
	      if (all) for (var _key in obj) {
	        if (hidden[_key]) continue;

	        var subresult = this._validate(all, data[_key], obj, _key);

	        if (!subresult) failed[_key] = true;
	        result = subresult && result;
	      } //per-field rules

	      for (var _key2 in rules$$1) {
	        if (hidden[_key2]) continue;

	        if (_key2.indexOf("$") !== 0 && !failed[_key2]) {
	          assert(rules$$1[_key2], "Invalid rule for:" + _key2);

	          var _subresult = this._validate(rules$$1[_key2], data[_key2], obj, _key2);

	          if (!_subresult) failed[_key2] = true;
	          result = _subresult && result;
	        }
	      }
	    } //check personal validation rules


	    if (elements) {
	      for (var key in elements) {
	        if (failed[key]) continue;
	        var subview = elements[key];

	        if (subview.validate) {
	          var _subresult2 = subview.validate();

	          result = _subresult2 && result;
	          if (!_subresult2) failed[key] = true;
	        } else {
	          var input = subview._settings;

	          if (input) {
	            //ignore non webix inputs
	            var validator = input.validate;
	            if (!validator && input.required) validator = rules$$1.isNotEmpty;

	            if (validator) {
	              var _subresult3 = this._validate(validator, obj[key], obj, key);

	              if (!_subresult3) failed[key] = true;
	              result = _subresult3 && result;
	            }
	          }
	        }
	      }
	    }

	    this.callEvent("onAfterValidation", [result, this._validate_details]);
	    return result;
	  },
	  _validate: function (rule, data, obj, key) {
	    if (typeof rule == "string") rule = rules[rule];

	    if (rule.call(this, data, obj, key)) {
	      if (this.callEvent("onValidationSuccess", [key, obj]) && this._clear_invalid) this._clear_invalid(key);
	      return true;
	    } else {
	      if (this.callEvent("onValidationError", [key, obj]) && this._mark_invalid) this._mark_invalid(key);
	    }

	    return false;
	  }
	};

	var _pull = {};
	function dp(name, getOnly) {
	  if (_typeof(name) == "object" && name._settings) name = name._settings.id;
	  if (_pull[name] || getOnly) return _pull[name];
	  if (typeof name == "string" || typeof name == "number") name = {
	    master: $$(name)
	  };
	  var dp = new DataProcessor(name);
	  var masterId = dp._settings.master._settings.id;
	  _pull[masterId] = dp;
	  $$(masterId).attachEvent("onDestruct", function () {
	    _pull[this._settings.id] = null;
	    delete _pull[this._settings.id];
	  });
	  return dp;
	}
	define("dp", dp);

	dp.$$ = function (id) {
	  return _pull[id];
	};

	var DataProcessor = exports.proto({
	  defaults: {
	    autoupdate: true,
	    updateFromResponse: false,
	    mode: "post",
	    operationName: "webix_operation",
	    trackMove: false
	  },

	  /*! constructor
	   **/
	  $init: function () {
	    this.reset();
	    this._ignore = false;
	    this.name = "DataProcessor";
	    this.$ready.push(this._after_init_call);
	  },
	  reset: function () {
	    this._updates = [];
	  },
	  url_setter: function (value) {
	    /*
	    	we can use simple url or mode->url
	    */
	    var mode = "";

	    if (typeof value == "string") {
	      var parts = value.split("->");

	      if (parts.length > 1) {
	        value = parts[1];
	        mode = parts[0];
	      }
	    } else if (value && value.mode) {
	      mode = value.mode;
	      value = value.url;
	    }

	    if (mode) return proxy$a(mode, value);
	    return value;
	  },
	  master_setter: function (value) {
	    var store = value;
	    if (value.name != "DataStore") store = value.data;
	    this._settings.store = store;
	    return value;
	  },
	  _promise: function (handler) {
	    var prev = this._waitSave;
	    this._waitSave = [];
	    handler();
	    var result = Promise.all(this._waitSave);
	    this._waitSave = prev;
	    if (prev) prev.push(result);
	    return result;
	  },

	  /*! attaching onStoreUpdated event
	   **/
	  _after_init_call: function () {
	    var store = this._settings.store;

	    if (store) {
	      store.attachEvent("onStoreUpdated", bind(this._onStoreUpdated, this));
	      store.attachEvent("onDataMove", bind(this._onDataMove, this));
	    }
	  },
	  ignore: function (code, master) {
	    var temp = this._ignore;
	    this._ignore = true;
	    code.call(master || this);
	    this._ignore = temp;
	  },
	  off: function () {
	    this._ignore = true;
	  },
	  on: function () {
	    this._ignore = false;
	  },
	  _copy_data: function (source) {
	    var obj = {};

	    for (var key in source) {
	      if (key.indexOf("$") !== 0) obj[key] = source[key];
	    }

	    return obj;
	  },
	  save: function (id, operation, obj) {
	    operation = operation || "update";
	    return this._save_inner(id, obj, operation, true);
	  },
	  _save_inner: function (id, obj, operation, now) {
	    if (_typeof(id) == "object") id = id.toString();
	    if (!id || this._ignore === true || !operation || operation == "paint") return;
	    var store = this._settings.store;

	    if (store) {
	      obj = obj || this._settings.store.getItem(id);
	      if (store._scheme_serialize) obj = store._scheme_serialize(obj);
	    }

	    var update = {
	      id: id,
	      data: this._copy_data(obj),
	      operation: operation
	    }; //save parent id

	    if (!isUndefined(obj.$parent)) update.data.parent = obj.$parent;

	    if (update.operation != "delete") {
	      //prevent saving of not-validated records
	      var master = this._settings.master;
	      if (master && master.data && master.data.getMark && master.data.getMark(id, "webix_invalid")) update._invalid = true;
	      if (!this.validate(null, update.data)) update._invalid = true;
	    }

	    if (this._check_unique(update)) this._updates.push(update);
	    if (this._settings.autoupdate || now) return this._sendData(id);
	    return;
	  },
	  _onDataMove: function (sid, tindex, parent, targetid) {
	    if (this._settings.trackMove) {
	      var obj = copy(this._settings.store.getItem(sid));
	      obj.webix_move_index = tindex;
	      obj.webix_move_id = targetid;
	      obj.webix_move_parent = parent;

	      this._save_inner(sid, obj, "order");
	    }
	  },
	  _onStoreUpdated: function (id, obj, operation) {
	    switch (operation) {
	      case "save":
	        operation = "update";
	        break;

	      case "update":
	        operation = "update";
	        break;

	      case "add":
	        operation = "insert";
	        break;

	      case "delete":
	        operation = "delete";
	        break;

	      default:
	        return true;
	    }

	    return this._save_inner(id, obj, operation);
	  },
	  _check_unique: function (check) {
	    for (var i = 0; i < this._updates.length; i++) {
	      var one = this._updates[i];

	      if (one.id == check.id && !one._in_progress) {
	        if (check.operation == "delete") {
	          if (one.operation == "insert") this._updates.splice(i, 1);else one.operation = "delete";
	        }

	        one.data = check.data;
	        one._invalid = check._invalid;
	        return false;
	      }
	    }

	    return true;
	  },
	  send: function () {
	    return this._sendData();
	  },
	  _sendData: function (triggerId) {
	    if (!this._settings.url) return;
	    var wait;
	    var marked = this._updates;
	    var to_send = [];
	    var url = this._settings.url;

	    for (var i = 0; i < marked.length; i++) {
	      var tosave = marked[i];
	      if (tosave._in_progress) continue;
	      if (tosave._invalid) continue;
	      var id = tosave.id; // call to .save(id) without autoupdate mode will send the specific object only

	      if (!this._settings.autoupdate && triggerId && triggerId != id) continue;
	      var operation = tosave.operation;
	      var precise_url = proxy$a.$parse(_typeof(url) == "object" && !url.$proxy ? url[operation] : url);
	      var custom = precise_url && (precise_url.$proxy || typeof precise_url === "function");
	      if (!precise_url) continue;
	      var store = this._settings.store;
	      if (store && store._scheme_save) store._scheme_save(tosave.data);
	      if (!this.callEvent("onBefore" + operation, [id, tosave])) continue;
	      tosave._in_progress = true;
	      if (!this.callEvent("onBeforeDataSend", [tosave])) return;
	      tosave.data = this._updatesData(tosave.data);
	      var result = void 0;

	      if (precise_url.$proxy) {
	        if (precise_url.save) {
	          //proxy
	          result = precise_url.save(this.config.master, tosave, this);
	        }

	        to_send.push(tosave);
	      } else {
	        if (operation == "insert") delete tosave.data.id;

	        if (custom) {
	          //save function
	          result = precise_url.call(this.config.master, tosave.id, tosave.operation, tosave.data);
	        } else {
	          //normal url
	          tosave.data[this._settings.operationName] = operation;
	          result = this._send(precise_url, tosave.data, this._settings.mode);
	        }
	      }

	      if (result) {
	        result = this._proxy_on_save(result, {
	          id: tosave.id,
	          status: tosave.operation
	        });

	        if (triggerId && id === triggerId) {
	          wait = result;
	        }
	      }

	      this.callEvent("onAfterDataSend", [tosave]);
	    }

	    if (url.$proxy && url.saveAll && to_send.length) {
	      var _result = url.saveAll(this.config.master, to_send, this);

	      if (_result) {
	        _result = this._proxy_on_save(_result, null);
	        if (!wait) wait = _result;
	      }
	    }

	    return wait;
	  },
	  _proxy_on_save: function (result, state) {
	    var _this = this;

	    if (result) {
	      if (!result.then) result = Deferred.resolve(result);
	      result = result.then(function (data) {
	        if (data && typeof data.json == "function") data = data.json();
	        var processed;

	        if (state === null) {
	          processed = _this._processResult(data); //array of responses
	        } else {
	          processed = _this._processResult(state, "", data, -1); //text, data, loader
	        }

	        if (!processed) throw processed; // trigger rejection

	        return processed;
	      }, function (x) {
	        _this._processError(state, "", null, x);

	        throw x;
	      });
	      if (this._waitSave) this._waitSave.push(result);
	      return result;
	    }
	  },

	  /*! process updates list to POST and GET params according dataprocessor protocol
	   *	@param updates
	   *		list of objects { id: "item id", data: "data hash", operation: "type of operation"}
	   *	@return
	   *		object { post: { hash of post params as name: value }, get: { hash of get params as name: value } }
	   **/
	  _updatesData: function (source) {
	    var target = {};

	    for (var j in source) {
	      if (j.indexOf("$") !== 0) target[j] = source[j];
	    }

	    return target;
	  },

	  /*! send dataprocessor query to server
	   *	and attach event to process result
	   *	@param url
	   *		server url
	   *	@param get
	   *		hash of get params
	   *	@param post
	   *		hash of post params
	   *	@mode
	   *		'post' or 'get'
	   **/
	  _send: function (url, post, mode) {
	    assert(url, "url was not set for DataProcessor");
	    return ajax()[mode](url, post);
	  },
	  attachProgress: function (start, end, error) {
	    this.attachEvent("onBeforeDataSend", start);
	    this.attachEvent("onAfterSync", end);
	    this.attachEvent("onAfterSaveError", error);
	    this.attachEvent("onLoadError", error);
	  },
	  _processError: function (id, text, data, loader) {
	    if (id) this._innerProcessResult(true, id.id, false, id.status, false, {
	      text: text,
	      data: data,
	      loader: loader
	    });else {
	      this.callEvent("onLoadError", arguments);
	      callEvent("onLoadError", [text, data, loader, this]);
	    }
	  },
	  _innerProcessResult: function (error, id, newid, status, obj, details) {
	    var master = this._settings.master;
	    var update = this.getItemState(id);
	    update._in_progress = false;

	    if (error) {
	      if (this.callEvent("onBeforeSaveError", [id, status, obj, details])) {
	        update._invalid = true;

	        if (this._settings.undoOnError && master._settings.undo) {
	          this.ignore(function () {
	            master.undo(id);
	          });
	          this.setItemState(id, false);
	        }

	        this.callEvent("onAfterSaveError", [id, status, obj, details]);
	      }

	      return;
	    } else this.setItemState(id, false);

	    var store = this._settings.store;

	    if (store && store.exists(id)) {
	      //update from response
	      if (newid && id != newid) store.changeId(id, newid);
	      if (obj && status != "delete" && this._settings.updateFromResponse) this.ignore(function () {
	        store.updateItem(newid || id, obj);
	      });
	    } //clean undo history, for the saved record


	    if (this._settings.undoOnError && master._settings.undo) master.removeUndo(newid || id);
	    this.callEvent("onAfterSave", [obj, id, details]);
	    this.callEvent("onAfter" + status, [obj, id, details]);
	    return obj || {};
	  },
	  processResult: function (state, hash, details) {
	    //compatibility with custom json response
	    var error = hash && (hash.status == "error" || hash.status == "invalid");
	    var newid = hash ? hash.newid || hash.id : false;
	    return this._innerProcessResult(error, state.id, newid, state.status, hash, details);
	  },
	  // process saving from result
	  _processResult: function (state, text, data, loader) {
	    var _this2 = this;

	    var finalResult;
	    this.callEvent("onBeforeSync", [state, text, data, loader]);

	    if (isArray(state)) {
	      //saveAll results
	      finalResult = [];
	      state.forEach(function (one) {
	        finalResult.push(_this2.processResult(one, one, {}));
	      });
	    } else {
	      if (loader === -1) {
	        //callback from promise
	        finalResult = this.processResult(state, data, {});
	      } else {
	        var proxy = this._settings.url;

	        if (proxy.$proxy && proxy.result) {
	          finalResult = proxy.result(state, this._settings.master, this, text, data, loader) || {};
	        } else {
	          var hash;

	          if (text) {
	            hash = data.json(); //invalid response

	            if (text && (hash === null || typeof hash == "undefined")) hash = {
	              status: "error"
	            };
	          }

	          finalResult = this.processResult(state, hash, {
	            text: text,
	            data: data,
	            loader: loader
	          });
	        }
	      }
	    }

	    this.callEvent("onAfterSync", [state, text, data, loader]);
	    return finalResult;
	  },

	  /*! if it's defined escape function - call it
	   *	@param value
	   *		value to escape
	   *	@return
	   *		escaped value
	   **/
	  escape: function (value) {
	    if (this._settings.escape) return this._settings.escape(value);else return encodeURIComponent(value);
	  },
	  getState: function () {
	    if (!this._updates.length) return false;

	    for (var i = this._updates.length - 1; i >= 0; i--) {
	      if (this._updates[i]._in_progress) return "saving";
	    }

	    return true;
	  },
	  getItemState: function (id) {
	    var index = this._get_stack_index(id);

	    return this._updates[index] || null;
	  },
	  setItemState: function (id, state) {
	    if (state) {
	      this._save_inner(id, null, "update");
	    } else {
	      var index = this._get_stack_index(id);

	      if (index > -1) this._updates.splice(index, 1);
	    }
	  },
	  _get_stack_index: function (id) {
	    var index = -1;

	    for (var i = 0; i < this._updates.length; i++) {
	      if (this._updates[i].id == id) {
	        index = i;
	        break;
	      }
	    }

	    return index;
	  }
	}, Settings, EventSystem, ValidateData);

	/*
		DataStore is not a behavior, it standalone object, which represents collection of data.
		Call provideAPI to map data API

		@export
			exists
			getIdByIndex
			getIndexById
			get
			set
			refresh
			count
			sort
			filter
			next
			previous
			clearAll
			first
			last
	*/

	function DataStore() {
	  this.name = "DataStore";
	  exports.extend(this, EventSystem);
	  this.setDriver("json"); //default data source is an

	  this.pull = {}; //hash of IDs

	  this.order = toArray(); //order of IDs

	  this._marks = {};
	}

	DataStore.prototype = {
	  //defines type of used data driver
	  //data driver is an abstraction other different data formats - xml, json, csv, etc.
	  setDriver: function (type) {
	    assert(DataDriver[type], "incorrect DataDriver");
	    this.driver = DataDriver[type];
	  },
	  //process incoming raw data
	  _parse: function (data) {
	    this.callEvent("onParse", [this.driver, data]);
	    if (this._filter_order) this.filter(); //get size and position of data

	    var info = this.driver.getInfo(data); //generated by connectors only

	    if (info.key) state.securityKey = info.key;
	    if (info.config) this.callEvent("onServerConfig", [info.config]);
	    var options = this.driver.getOptions(data);
	    if (options) this.callEvent("onServerOptions", [options]); //get array of records

	    var recs = this.driver.getRecords(data);

	    this._inner_parse(info, recs); //in case of tree store we may want to group data


	    if (this._scheme_group && this._group_processing && !this._not_grouped_order) this._group_processing(this._scheme_group); //optional data sorting

	    if (this._scheme_sort) {
	      this.blockEvent();
	      this.sort(this._scheme_sort);
	      this.unblockEvent();
	    }

	    this.callEvent("onStoreLoad", [this.driver, data]); //repaint self after data loading

	    this.refresh();
	  },
	  _inner_parse: function (info, recs) {
	    var from = info.from;
	    var subload = true;
	    var marks = false; //some data is loaded and new data doesn't have "pos" - assuming update

	    if (!from && from !== 0 && this.order[0]) {
	      if (this._removeMissed) {
	        //update mode, create kill list
	        marks = {};

	        for (var i = 0; i < this.order.length; i++) {
	          marks[this.order[i]] = true;
	        }
	      }

	      subload = false;
	      from = this.order.length;
	    } else from = (from || 0) * 1;

	    var j = 0;

	    for (var _i = 0; _i < recs.length; _i++) {
	      //get hash of details for each record
	      var temp = this.driver.getDetails(recs[_i]);
	      var id = this.id(temp); //generate ID for the record

	      if (!this.pull[id]) {
	        //if such ID already exists - update instead of insert
	        this.order[j + from] = id;
	        j++;
	      } else if (subload && this.order[j + from]) j++;

	      if (this.pull[id]) {
	        exports.extend(this.pull[id], temp, true); //add only new properties

	        if (this._scheme_update) this._scheme_update(this.pull[id]); //update mode, remove item from kill list

	        if (marks) delete marks[id];
	      } else {
	        this.pull[id] = temp;
	        if (this._scheme_init) this._scheme_init(temp);
	      }
	    } //update mode, delete items which are not existing in the new xml


	    if (marks) {
	      this.blockEvent();

	      for (var delid in marks) {
	        this.remove(delid);
	      }

	      this.unblockEvent();
	    }

	    var endpos = info.size * 1;

	    if (endpos) {
	      if (!this.order[endpos - 1]) this.order[endpos - 1] = undefined;
	      if (endpos < this.order.length) this.order = this.order.slice(0, endpos);
	    }
	  },
	  //generate id for data object
	  id: function (data) {
	    return data.id || (data.id = uid());
	  },
	  changeId: function (old, newid) {
	    //assert(this.pull[old],"Can't change id, for non existing item: "+old);
	    if (old == newid) return;
	    if (this.pull[old]) this.pull[newid] = this.pull[old];
	    this.pull[newid].id = newid;
	    this.order[this.order.find(old)] = newid;
	    if (this._filter_order) this._filter_order[this._filter_order.find(old)] = newid;

	    if (this._marks[old]) {
	      this._marks[newid] = this._marks[old];
	      delete this._marks[old];
	    }

	    this.callEvent("onIdChange", [old, newid]);
	    if (this._render_change_id) this._render_change_id(old, newid);
	    delete this.pull[old];
	  },
	  //get data from hash by id
	  getItem: function (id) {
	    return this.pull[id];
	  },
	  //assigns data by id
	  updateItem: function (id, update, mode) {
	    if (_typeof(id) === "object") id = id.toString();
	    var data = this.getItem(id);
	    var old = null; //check is change tracking active

	    var changeTrack = this.hasEvent("onDataUpdate");
	    assert(data, "Invalid ID for updateItem");
	    assert(!update || !update.id || update.id == id, "Attempt to change ID in updateItem");

	    if (!isUndefined(update) && data !== update) {
	      //preserve original object
	      if (changeTrack) old = copy(data);
	      id = data.id; //preserve id

	      exports.extend(data, update, true);
	      data.id = id;
	    }

	    if (this._scheme_update) this._scheme_update(data);
	    this.callEvent("onStoreUpdated", [id, data, mode || "update"]);
	    if (changeTrack) this.callEvent("onDataUpdate", [id, data, old]);
	  },
	  //sends repainting signal
	  refresh: function (id) {
	    if (this._skip_refresh) return;

	    if (id) {
	      if (this.exists(id)) this.callEvent("onStoreUpdated", [id, this.pull[id], "paint"]);
	    } else this.callEvent("onStoreUpdated", [null, null, null]);
	  },
	  silent: function (code, master) {
	    this._skip_refresh = true;
	    code.call(master || this);
	    this._skip_refresh = false;
	  },
	  //converts range IDs to array of all IDs between them
	  getRange: function (from, to) {
	    //if some point is not defined - use first or last id
	    //BEWARE - do not use empty or null ID
	    if (from) from = this.getIndexById(from);else from = this.$min || this.startOffset || 0;
	    if (to) to = this.getIndexById(to);else {
	      to = this.$max === 0 ? 0 : Math.min(this.$max ? this.$max - 1 : this.endOffset || Infinity, this.count() - 1);
	      if (to < 0) to = 0; //we have not data in the store
	    }

	    if (from > to) {
	      //can be in case of backward shift-selection
	      var a = to;
	      to = from;
	      from = a;
	    }

	    return this.getIndexRange(from, to);
	  },
	  //converts range of indexes to array of all IDs between them
	  getIndexRange: function (from, to) {
	    to = Math.min(to === 0 ? 0 : to || Infinity, this.count() - 1);
	    var ret = toArray(); //result of method is rich-array

	    for (var i = from || 0; i <= to; i++) {
	      ret.push(this.getItem(this.order[i]));
	    }

	    return ret;
	  },
	  //returns total count of elements
	  count: function () {
	    return this.order.length;
	  },
	  //returns truy if item with such ID exists
	  exists: function (id) {
	    return !!this.pull[id];
	  },
	  //nextmethod is not visible on component level, check DataMove.move
	  //moves item from source index to the target index
	  move: function (sindex, tindex) {
	    assert(sindex >= 0 && tindex >= 0, "DataStore::move", "Incorrect indexes");
	    if (sindex == tindex) return;
	    var id = this.getIdByIndex(sindex);
	    var obj = this.getItem(id);
	    if (this._filter_order) this._move_inner(this._filter_order, 0, 0, this.getIdByIndex(sindex), this.getIdByIndex(tindex));

	    this._move_inner(this.order, sindex, tindex); //repaint signal


	    this.callEvent("onStoreUpdated", [id, obj, "move"]);
	  },
	  _move_inner: function (col, sindex, tindex, sid, tid) {
	    if (sid || tid) {
	      sindex = tindex = -1;

	      for (var i = 0; i < col.length; i++) {
	        if (col[i] == sid && sindex < 0) sindex = i;
	        if (col[i] == tid && tindex < 0) tindex = i;
	      }
	    }

	    var id = col[sindex];
	    col.removeAt(sindex); //remove at old position

	    col.insertAt(id, Math.min(col.length, tindex)); //insert at new position
	  },
	  scheme: function (config) {
	    this._scheme = {};
	    this._scheme_save = config.$save;
	    this._scheme_init = config.$init || config.$change;
	    this._scheme_update = config.$update || config.$change;
	    this._scheme_serialize = config.$serialize;
	    this._scheme_group = config.$group;
	    this._scheme_sort = config.$sort;
	    this._scheme_export = config.$export; //ignore $-starting properties, as they have special meaning

	    for (var key in config) {
	      if (key.substr(0, 1) != "$") this._scheme[key] = config[key];
	    }
	  },
	  importData: function (target, silent) {
	    var data = target ? target.data || target : [];
	    this._filter_order = null;

	    if (typeof data.serialize == "function") {
	      this.order = toArray([].concat(data.order)); //make full copy, to preserve object properties
	      //[WE-CAN-DO-BETTER]

	      if (this._make_full_copy) {
	        this._make_full_copy = false;
	        var oldpull = this.pull;
	        this.pull = {};

	        for (var _key in data.pull) {
	          var old = oldpull[_key];
	          this.pull[_key] = copy(data.pull[_key]);
	          if (old && old.open) this.pull[_key].open = true;
	        }
	      } else this.pull = data.pull;

	      if (data.branch && this.branch) {
	        this.branch = copy(data.branch);
	        this._filter_branch = null;
	      }
	    } else {
	      this.order = toArray();
	      this.pull = {};
	      var id, obj;
	      if (isArray(target)) for (var _key2 = 0; _key2 < target.length; _key2++) {
	        obj = id = target[_key2];
	        if (_typeof(obj) == "object") obj.id = obj.id || uid();else obj = {
	          id: id,
	          value: id
	        };
	        this.order.push(obj.id);
	        if (this._scheme_init) this._scheme_init(obj);
	        this.pull[obj.id] = obj;
	      } else for (var _key3 in data) {
	        this.order.push(_key3);
	        this.pull[_key3] = {
	          id: _key3,
	          value: data[_key3]
	        };
	      }
	    }

	    if (this._extraParser && !data.branch) {
	      this.branch = {
	        0: []
	      };
	      if (!this._datadriver_child) this._set_child_scheme("data");

	      for (var i = 0; i < this.order.length; i++) {
	        var key = this.order[i];

	        this._extraParser(this.pull[key], 0, 0, false);
	      }
	    }

	    this.callEvent("onStoreLoad", []);
	    if (!silent) this.callEvent("onStoreUpdated", []);
	  },
	  sync: function (source, filter, silent) {
	    this.unsync();

	    var type = _typeof(source);

	    if (type == "string") source = $$(source);

	    if (type != "function" && type != "object") {
	      silent = filter;
	      filter = null;
	    }

	    if (source.name != "DataStore") {
	      if (source.data && (source.data.name === "DataStore" || source.data.name === "TreeStore")) source = source.data;else {
	        this._sync_source = source;
	        return callEvent("onSyncUnknown", [this, source, filter]);
	      }
	    }

	    var sync_logic = bind(function (id, data, mode) {
	      if (this._skip_next_sync) return; //sync of tree-structure with after-filtering
	      //we need to make a full copy, to preserve $count
	      //[WE-CAN-DO-BETTER]

	      if (filter && this.branch) this._make_full_copy = true;
	      this.importData(source, true);
	      if (filter) this.silent(filter);
	      if (this._on_sync) this._on_sync();
	      if (!(id && data && mode)) //clearall
	        this._marks = {};
	      if (mode == "delete" && this._marks[id]) delete this._marks[id];
	      this.callEvent("onSyncApply", []);
	      if (!silent) this.refresh();else silent = false;
	    }, this);
	    this._sync_events = [source.attachEvent("onStoreUpdated", sync_logic), source.attachEvent("onIdChange", bind(function (old, nid) {
	      this.changeId(old, nid);
	      this.refresh(nid);
	    }, this))];
	    this._sync_source = source; //backward data saving

	    this._back_sync_handler = this.attachEvent("onStoreUpdated", function (id, data, mode) {
	      if (mode == "update" || mode == "save") {
	        this._skip_next_sync = 1;
	        source.updateItem(id, data);
	        this._skip_next_sync = 0;
	      }
	    });
	    sync_logic();
	  },
	  unsync: function () {
	    if (this._sync_source) {
	      var source = this._sync_source;

	      if (source.name != "DataStore" && (!source.data || source.data.name != "DataStore")) {
	        //data sync with external component
	        callEvent("onUnSyncUnknown", [this, source]);
	      } else {
	        //data sync with webix component
	        for (var i = 0; i < this._sync_events.length; i++) {
	          source.detachEvent(this._sync_events[i]);
	        }

	        this.detachEvent(this._back_sync_handler);
	      }

	      this._sync_source = null;
	    }
	  },
	  destructor: function () {
	    this.unsync();
	    this.pull = this.order = this._marks = null;
	    this._evs_events = this._evs_handlers = {};
	  },
	  //adds item to the store
	  add: function (obj, index) {
	    //default values		
	    if (this._scheme) for (var key in this._scheme) {
	      if (isUndefined(obj[key])) obj[key] = this._scheme[key];
	    }
	    if (this._scheme_init) this._scheme_init(obj); //generate id for the item

	    var id = this.id(obj); //in case of treetable order is sent as 3rd parameter

	    var order = arguments[2] || this.order; //by default item is added to the end of the list

	    var data_size = order.length;
	    if (isUndefined(index) || index < 0) index = data_size; //check to prevent too big indexes			

	    if (index > data_size) {
	      assert(0, "Warning", "DataStore:add", "Index of out of bounds");
	      index = Math.min(order.length, index);
	    }

	    if (this.callEvent("onBeforeAdd", [id, obj, index]) === false) return false;
	    assert(!this.exists(id), "Not unique ID");
	    this.pull[id] = obj;
	    order.insertAt(id, index);

	    if (this._filter_order) {
	      //adding during filtering
	      //we can't know the location of new item in full dataset, making suggestion
	      //put at end of original dataset by default
	      var original_index = this._filter_order.length; //if some data exists, put at the same position in original and filtered lists

	      if (this.order.length) original_index = Math.min(index || 0, original_index);

	      this._filter_order.insertAt(id, original_index);
	    } //repaint signal


	    this.callEvent("onStoreUpdated", [id, obj, "add"]);
	    this.callEvent("onAfterAdd", [id, index]);
	    return obj.id;
	  },
	  //removes element from datastore
	  remove: function (id) {
	    //id can be an array of IDs - result of getSelect, for example
	    if (isArray(id)) {
	      for (var i = 0; i < id.length; i++) {
	        this.remove(id[i]);
	      }

	      return;
	    }

	    if (this.callEvent("onBeforeDelete", [id]) === false) return false;
	    assert(this.exists(id), "Not existing ID in remove command" + id);
	    var obj = this.getItem(id); //save for later event
	    //clear from collections

	    this.order.remove(id);
	    if (this._filter_order) this._filter_order.remove(id);
	    delete this.pull[id];
	    if (this._marks[id]) delete this._marks[id]; //repaint signal

	    this.callEvent("onStoreUpdated", [id, obj, "delete"]);
	    this.callEvent("onAfterDelete", [id]);
	  },
	  //deletes all records in datastore
	  clearAll: function (soft) {
	    //instead of deleting one by one - just reset inner collections
	    this.pull = {};
	    this._marks = {};
	    this.order = toArray(); //this.feed = null;

	    this._filter_order = null;
	    if (!soft) this.url = null;
	    this.callEvent("onClearAll", [soft]);
	    this.refresh();
	  },
	  //converts id to index
	  getIdByIndex: function (index) {
	    assert(index >= 0, "DataStore::getIdByIndex Incorrect index");
	    return this.order[index];
	  },
	  //converts index to id
	  getIndexById: function (id) {
	    if (!this.pull[id]) return -1;else return this.order.find(id); //slower than getIdByIndex
	  },
	  //returns ID of next element
	  getNextId: function (id, step) {
	    return this.order[this.getIndexById(id) + (step || 1)];
	  },
	  //returns ID of first element
	  getFirstId: function () {
	    return this.order[0];
	  },
	  //returns ID of last element
	  getLastId: function () {
	    return this.order[this.order.length - 1];
	  },
	  //returns ID of previous element
	  getPrevId: function (id, step) {
	    return this.order[this.getIndexById(id) - (step || 1)];
	  },

	  /*
	  	sort data in collection
	  		by - settings of sorting
	  	
	  	or
	  	
	  		by - sorting function
	  		dir - "asc" or "desc"
	  		
	  	or
	  	
	  		by - property
	  		dir - "asc" or "desc"
	  		as - type of sortings
	  	
	  	Sorting function will accept 2 parameters and must return 1,0,-1, based on desired order
	  */
	  sort: function (by, dir, as) {
	    var sort = by;
	    if (typeof by == "function") sort = {
	      as: by,
	      dir: dir
	    };else if (typeof by == "string") sort = {
	      by: by.replace(/#/g, ""),
	      dir: dir,
	      as: as
	    };
	    var parameters = [sort.by, sort.dir, sort.as, sort];
	    if (!this.callEvent("onBeforeSort", parameters)) return;
	    this.order = this._sort_core(sort, this.order);
	    if (this._filter_order && this._filter_order.length != this.order.length) this._filter_order = this._sort_core(sort, this._filter_order); //repaint self

	    this.refresh();
	    this.callEvent("onAfterSort", parameters);
	  },
	  _sort_core: function (sort, order) {
	    var sorter = this.sorting.create(sort);

	    if (this.order.length) {
	      var pre = order.splice(0, this.$freeze); //get array of IDs

	      var neworder = toArray();

	      for (var i = order.length - 1; i >= 0; i--) {
	        neworder[i] = this.pull[order[i]];
	      }

	      neworder.sort(sorter);
	      return toArray(pre.concat(neworder.map(function (obj) {
	        assert(obj, "Client sorting can't be used with dynamic loading");
	        return this.id(obj);
	      }, this)));
	    }

	    return order;
	  },

	  /*
	  	Filter datasource
	  	
	  	text - property, by which filter
	  	value - filter mask
	  	
	  	or
	  	
	  	text  - filter method
	  	
	  	Filter method will receive data object and must return true or false
	  */
	  _filter_reset: function (preserve) {
	    //remove previous filtering , if any
	    if (this._filter_order && !preserve) {
	      this.order = this._filter_order;
	      delete this._filter_order;
	    }
	  },
	  _filter_core: function (filter, value, preserve) {
	    var neworder = toArray();
	    var freeze = this.$freeze || 0;

	    for (var i = 0; i < this.order.length; i++) {
	      var id = this.order[i];
	      if (i < freeze || filter(this.getItem(id), value)) neworder.push(id);
	    } //set new order of items, store original


	    if (!preserve || !this._filter_order) this._filter_order = this.order;
	    this.order = neworder;
	  },
	  find: function (config, first) {
	    var result = [];

	    for (var i in this.pull) {
	      var data = this.pull[i];
	      var match = true;

	      if (_typeof(config) == "object") {
	        for (var key in config) {
	          if (data[key] != config[key]) {
	            match = false;
	            break;
	          }
	        }
	      } else if (!config(data)) match = false;

	      if (match) result.push(data);
	      if (first && result.length) return result[0];
	    }

	    return first ? null : result;
	  },
	  filter: function (text, value, preserve) {
	    //unfilter call but we already in not-filtered state
	    if (!text && !this._filter_order && !this._filter_branch) return;
	    if (!this.callEvent("onBeforeFilter", [text, value])) return;

	    this._filter_reset(preserve);

	    if (!this.order.length) return; //if text not define -just unfilter previous state and exit

	    if (text) {
	      var filter = text;
	      value = value || "";

	      if (typeof text == "string") {
	        text = text.replace(/#/g, "");
	        if (typeof value == "function") filter = function (obj) {
	          return value(obj[text]);
	        };else {
	          value = value.toString().toLowerCase();

	          filter = function (obj, value) {
	            //default filter - string start from, case in-sensitive
	            assert(obj, "Client side filtering can't be used with dynamic loading");
	            return (obj[text] || "").toString().toLowerCase().indexOf(value) != -1;
	          };
	        }
	      }

	      this._filter_core(filter, value, preserve, this._filterMode);
	    } //repaint self


	    this.refresh();
	    this.callEvent("onAfterFilter", []);
	  },

	  /*
	  	Iterate through collection
	  */
	  _obj_array: function () {
	    var data = [];

	    for (var i = this.order.length - 1; i >= 0; i--) {
	      data[i] = this.pull[this.order[i]];
	    }

	    return data;
	  },
	  each: function (method, master, all) {
	    var order = this.order;
	    if (all) order = this._filter_order || order;

	    for (var i = 0; i < order.length; i++) {
	      if (order[i]) method.call(master || this, this.getItem(order[i]), i);
	    }
	  },
	  _methodPush: function (object, method) {
	    return function () {
	      return object[method].apply(object, arguments);
	    };
	  },

	  /*
	  	map inner methods to some distant object
	  */
	  provideApi: function (target, eventable) {
	    if (eventable) {
	      this.mapEvent({
	        onbeforesort: target,
	        onaftersort: target,
	        onbeforeadd: target,
	        onafteradd: target,
	        onbeforedelete: target,
	        onafterdelete: target,
	        ondataupdate: target
	        /*,
	        onafterfilter:	target,
	        onbeforefilter:	target*/

	      });
	    }

	    var list = ["sort", "add", "remove", "exists", "getIdByIndex", "getIndexById", "getItem", "updateItem", "refresh", "count", "filter", "find", "getNextId", "getPrevId", "clearAll", "getFirstId", "getLastId", "serialize", "sync"];

	    for (var i = 0; i < list.length; i++) {
	      target[list[i]] = this._methodPush(this, list[i]);
	    }
	  },
	  addMark: function (id, mark, css, value, silent) {
	    var obj = this._marks[id] || {};
	    this._marks[id] = obj;

	    if (!obj[mark]) {
	      obj[mark] = value || true;

	      if (css) {
	        var old_css = obj.$css || "";
	        obj.$css = old_css + " " + mark;
	      }

	      if (!silent) this.refresh(id);
	    }

	    return obj[mark];
	  },
	  removeMark: function (id, mark, css, silent) {
	    var obj = this._marks[id];

	    if (obj) {
	      if (obj[mark]) delete obj[mark];

	      if (css) {
	        var current_css = obj.$css;

	        if (current_css) {
	          obj.$css = current_css.replace(mark, "").replace("  ", " ");
	        }
	      }

	      if (!silent) this.refresh(id);
	    }
	  },
	  getMark: function (id, mark) {
	    var obj = this._marks[id];
	    return obj ? obj[mark] : false;
	  },
	  clearMark: function (name, css, silent) {
	    for (var id in this._marks) {
	      var obj = this._marks[id];

	      if (obj[name]) {
	        delete obj[name];
	        if (css && obj.$css) obj.$css = obj.$css.replace(name, "").replace("  ", " ");
	        if (!silent) this.refresh(id);
	      }
	    }
	  },

	  /*
	  	serializes data to a json object
	  */
	  serialize: function (all) {
	    var ids = this.order;
	    if (all && this._filter_order) ids = this._filter_order;
	    var result = [];

	    for (var i = 0; i < ids.length; i++) {
	      var el = this.pull[ids[i]];

	      if (this._scheme_serialize) {
	        el = this._scheme_serialize(el);
	        if (el === false) continue;
	      }

	      result.push(el);
	    }

	    return result;
	  },
	  sorting: {
	    create: function (config) {
	      return this._dir(config.dir, this._by(config.by, config.as));
	    },
	    as: {
	      //handled by dataFeed
	      "server": function () {
	        return false;
	      },
	      "date": function (a, b) {
	        a = a - 0;
	        b = b - 0;
	        return a > b ? 1 : a < b ? -1 : 0;
	      },
	      "int": function (a, b) {
	        a = a * 1;
	        b = b * 1;
	        return a > b ? 1 : a < b ? -1 : 0;
	      },
	      "string_strict": function (a, b) {
	        a = a.toString();
	        b = b.toString();
	        return a > b ? 1 : a < b ? -1 : 0;
	      },
	      "string": function (a, b) {
	        if (!b) return 1;
	        if (!a) return -1;
	        a = a.toString().toLowerCase();
	        b = b.toString().toLowerCase();
	        return a > b ? 1 : a < b ? -1 : 0;
	      },
	      "raw": function (a, b) {
	        return a > b ? 1 : a < b ? -1 : 0;
	      }
	    },
	    _by: function (prop, method) {
	      if (!prop) return method;
	      if (typeof method != "function") method = this.as[method || "string"];
	      assert(method, "Invalid sorting method");
	      return function (a, b) {
	        return method(a[prop], b[prop]);
	      };
	    },
	    _dir: function (prop, method) {
	      if (prop == "asc" || !prop) return method;
	      return function (a, b) {
	        return method(a, b) * -1;
	      };
	    }
	  }
	};

	/*
		Behavior:DataLoader - load data in the component
		
		@export
			load
			parse
	*/

	var DataLoader = exports.proto({
	  $init: function (config) {
	    //prepare data store
	    config = config || "";
	    this._feed_last = {};
	    this._data_generation = 1;
	    this.data = new DataStore();
	    this.data.attachEvent("onClearAll", bind(this._call_onclearall, this));
	    this.data.attachEvent("onServerConfig", bind(this._call_on_config, this));
	    this.attachEvent("onDestruct", this._call_onclearall);
	    this.data.feed = this._feed;
	    this.data.owner = config.id;
	  },
	  _feed: function (from, count, callback, defer) {
	    //allow only single request at same time
	    if (this._load_count) {
	      defer = Deferred.defer();
	      this._load_count = [from, count, callback, defer]; //save last ignored request

	      return defer;
	    } else this._load_count = true;

	    this._feed_last.from = from;
	    this._feed_last.count = count;
	    return this._feed_common.call(this, from, count, callback, false, false, defer);
	  },
	  _feed_common: function (from, count, callback, url, details, defer) {
	    var _this = this;

	    var state = null;
	    url = url || this.data.url;
	    if (from < 0) from = 0;
	    if (!details) details = {
	      start: from,
	      count: count
	    };
	    if (this.count()) details["continue"] = "true";
	    if (this.getState) state = this.getState(); // proxy

	    if (url && typeof url != "string") {
	      if (state) {
	        if (state.sort) details.sort = state.sort;
	        if (state.filter) details.filter = state.filter;
	      }

	      return this.load(url, 0, details).then(function (data) {
	        return _this._feed_on_load(data, callback, defer);
	      }, function () {
	        return _this._feed_callback();
	      });
	    } else {
	      // GET
	      url = url + (url.indexOf("?") == -1 ? "?" : "&");
	      var params = [];

	      for (var d in details) {
	        params.push(d + "=" + details[d]);
	      }

	      if (state) {
	        if (state.sort) params.push("sort[" + state.sort.id + "]=" + encodeURIComponent(state.sort.dir));
	        if (state.filter) for (var key in state.filter) {
	          var filterValue = state.filter[key];
	          if (_typeof(filterValue) == "object") filterValue = ajax().stringify(filterValue); //server daterangefilter

	          params.push("filter[" + key + "]=" + encodeURIComponent(filterValue));
	        }
	      }

	      url += params.join("&");

	      if (this._feed_last.url !== url) {
	        this._feed_last.url = url;
	        return this.load(url).then(function (data) {
	          return _this._feed_on_load(data, callback, defer);
	        }, function () {
	          return _this._feed_callback();
	        });
	      } else {
	        this._load_count = false;
	        return Deferred.reject();
	      }
	    }
	  },
	  _feed_on_load: function (data, callback, defer) {
	    var _this2 = this;

	    delay(function () {
	      return _this2._feed_callback();
	    }, "", "", 100);
	    if (callback) ajax.$callback(this, callback, data);
	    if (defer) defer.resolve(data);
	    return data;
	  },
	  _feed_callback: function () {
	    //after loading check if we have some ignored requests
	    var temp = this._load_count;
	    this._load_count = false;
	    if (_typeof(temp) == "object") this.data.feed.apply(this, temp); //load last ignored request
	  },
	  //loads data from external URL
	  load: function (url) {
	    url = proxy$a.$parse(url);
	    var ajax$$1 = AtomDataLoader.load.apply(this, arguments); //prepare data feed for dyn. loading

	    if (!this.data.url) this.data.url = url;
	    return ajax$$1;
	  },
	  //load next set of data rows
	  loadNext: function (count, start, callback, url, now) {
	    var config = this._settings;

	    if (config.datathrottle && !now) {
	      if (this._throttle_request) window.clearTimeout(this._throttle_request);
	      var defer = Deferred.defer();
	      this._throttle_request = delay(function () {
	        defer.resolve(this.loadNext(count, start, callback, url, true));
	      }, this, 0, config.datathrottle);
	      return defer;
	    }

	    if (!start && start !== 0) start = this.count();
	    if (!count) count = config.datafetch || this.count();
	    this.data.url = this.data.url || url;
	    if (this.callEvent("onDataRequest", [start, count, callback, url]) && this.data.url) return this.data.feed.call(this, start, count, callback);
	    return Deferred.reject();
	  },
	  _maybe_loading_already: function (count, from) {
	    var last = this._feed_last;

	    if (this._load_count && last.url) {
	      if (last.from <= from && last.count + last.from >= count + from) return true;
	    }

	    return false;
	  },
	  removeMissed_setter: function (value) {
	    return this.data._removeMissed = value;
	  },
	  //init of dataprocessor delayed after all settings processing
	  //because it need to be the last in the event processing chain
	  //to get valid validation state
	  _init_dataprocessor: function () {
	    var url = this._settings.save;
	    if (url === true) url = this._settings.save = this._settings.url;
	    var obj = {
	      master: this
	    };
	    if (url && url.url) exports.extend(obj, url);else obj.url = url;
	    dp(obj);
	  },
	  save_setter: function (value) {
	    if (value) this.$ready.push(this._init_dataprocessor);
	    return value;
	  },
	  waitSave: function (handler) {
	    var _this3 = this;

	    return dp(this)._promise(function () {
	      handler.call(_this3);
	    }).then(function (many) {
	      return many.length == 1 ? many[0] : many;
	    });
	  },
	  scheme_setter: function (value) {
	    this.data.scheme(value);
	  },
	  dataFeed_setter: function (value) {
	    value = proxy$a.$parse(value);
	    this.data.attachEvent("onBeforeFilter", bind(function (text, filtervalue) {
	      var _this4 = this;

	      //complex filtering, can't be routed to dataFeed
	      if (typeof text == "function") return true; //we have dataFeed and some text

	      if (this._settings.dataFeed && (text || filtervalue)) {
	        text = text || "id";
	        if (filtervalue && _typeof(filtervalue) == "object") filtervalue = filtervalue.id;
	        this.clearAll();
	        var url = this._settings.dataFeed; //url data feed

	        if (typeof url == "string") {
	          var urldata = "filter[" + text + "]=" + encodeURIComponent(filtervalue);
	          this.load(url + (url.indexOf("?") < 0 ? "?" : "&") + urldata, this._settings.datatype);
	        } //js data feed
	        else {
	            var filter = {};
	            filter[text] = filtervalue;

	            if (typeof url == "function") {
	              url.call(this, filtervalue, filter);
	            } else if (url.$proxy) {
	              if (url.load) {
	                url.load(this, {
	                  filter: filter
	                }).then(function (data) {
	                  return _this4._onLoad(data);
	                }, function (x) {
	                  return _this4._onLoadError(x);
	                });
	              }
	            }
	          }

	        return false;
	      }
	    }, this));
	    return value;
	  },
	  _call_onready: function () {
	    if (this._settings.ready && !this._ready_was_used) {
	      var code = toFunctor(this._settings.ready, this.$scope);
	      if (code) delay(code, this, arguments);
	      if (this.callEvent) delay(this.callEvent, this, ["onReady", []]);
	      this._ready_was_used = true;
	    }
	  },
	  _call_onclearall: function (soft) {
	    this._data_generation++;

	    if (!soft) {
	      this._load_count = false;
	      this._feed_last = {};
	      this.waitData = Deferred.defer();
	    }
	  },
	  _call_on_config: function (config) {
	    this._parseSeetingColl(config);
	  }
	}, AtomDataLoader);

	var DataState = {
	  getState: function () {
	    var cols_n = this.config.columns.length;
	    var columns = this.config.columns;
	    var settings = {
	      ids: [],
	      size: [],
	      select: this.getSelectedId(true),
	      scroll: this.getScrollState()
	    };

	    for (var i = 0; i < cols_n; i++) {
	      var col = columns[i];
	      settings.ids.push(col.id);
	      settings.size.push(col.fillspace || col.adjust ? -1 : col.width);
	    }

	    settings.order = [].concat(this._hidden_column_order.length ? this._hidden_column_order : settings.ids);

	    if (this._last_sorted) {
	      settings.sort = {
	        id: this._last_sorted,
	        dir: this._last_order
	      };
	    } //this method will try to access the rendered values
	    //just ignore it if grid is not rendered yet


	    if (this._filter_elements && this._dtable_fully_ready) {
	      var filter = {};
	      var any_filter = 0;

	      for (var key in this._filter_elements) {
	        if (this._hidden_column_hash[key]) continue;
	        var f = this._filter_elements[key];
	        f[1].value = filter[key] = f[2].getValue(f[0]);
	        any_filter = 1;
	      }

	      if (any_filter) settings.filter = filter;
	    }

	    settings.hidden = [];

	    for (var _key in this._hidden_column_hash) {
	      settings.hidden.push(_key);
	    }

	    return settings;
	  },
	  setState: function (obj) {
	    var columns = this.config.columns;
	    if (!obj) return;
	    this._last_sorted = null;
	    this.blockEvent();

	    if (obj.order && obj.order.length) {
	      this._hidden_column_order = [].concat(obj.order);
	      this._hidden_split = [this._settings.leftSplit, obj.order.length - this._settings.rightSplit];
	    }

	    if (obj.hidden) {
	      var hihash = {};

	      for (var i = 0; i < obj.hidden.length; i++) {
	        hihash[obj.hidden[i]] = true;
	        if (!this._hidden_column_order.length) this.hideColumn(obj.hidden[i]);
	      }

	      if (this._hidden_column_order.length) {
	        for (var _i = 0; _i < this._hidden_column_order.length; _i++) {
	          var hikey = this._hidden_column_order[_i];
	          if (!!hihash[hikey] == !this._hidden_column_hash[hikey]) this.hideColumn(hikey, {}, false, !!hihash[hikey]);
	        }
	      }
	    }

	    if (obj.ids) {
	      var reorder = false;
	      var cols = this.config.columns;

	      for (var _i2 = 0; _i2 < cols.length; _i2++) {
	        if (cols[_i2].id != obj.ids[_i2]) reorder = true;
	      }

	      if (reorder) {
	        for (var _i3 = 0; _i3 < obj.ids.length; _i3++) {
	          cols[_i3] = this.getColumnConfig(obj.ids[_i3]) || cols[_i3];
	        }

	        this.refreshColumns();
	      }
	    }

	    if (obj.size) {
	      var cols_n = Math.min(obj.size.length, columns.length);

	      for (var _i4 = 0; _i4 < cols_n; _i4++) {
	        var col = columns[_i4];

	        if (col && obj.size[_i4] > 0 && col.width != obj.size[_i4]) {
	          delete col.fillspace;
	          delete col.adjust;

	          this._setColumnWidth(_i4, obj.size[_i4], true);
	        }
	      }
	    }

	    this.unblockEvent();
	    var silent = !(this._settings.leftSplit || this._settings.rightSplit);

	    this._updateColsSizeSettings(silent);

	    this.callEvent("onStructureUpdate", []);

	    if (obj.sort) {
	      var column = columns[this.getColumnIndex(obj.sort.id)];
	      if (column) this._sort(obj.sort.id, obj.sort.dir, column.sort);
	    }

	    if (obj.filter) {
	      //temporary disable filtering 
	      var temp = this.filterByAll;

	      this.filterByAll = function () {}; //apply defined filters


	      for (var key in obj.filter) {
	        var value = obj.filter[key];
	        if (!value) continue;
	        if (!this._filter_elements[key]) continue;
	        var f = this._filter_elements[key];
	        f[2].setValue(f[0], value);
	        var contentid = f[1].contentId;
	        if (contentid) this._active_headers[contentid].value = value;
	      } //remove old filters


	      for (var _key2 in this._filter_elements) {
	        if (!obj.filter[_key2]) {
	          var _f = this._filter_elements[_key2];

	          _f[2].setValue(_f[0], "");
	        }
	      } //restore and apply filtering


	      this.filterByAll = temp;
	      this.filterByAll();
	    }

	    if (obj.select && this.select) {
	      var select = obj.select;
	      this.unselect();

	      for (var _i5 = 0; _i5 < select.length; _i5++) {
	        if (!select[_i5].row || this.exists(select[_i5].row)) this._select(select[_i5], true);
	      }
	    }

	    if (obj.scroll) this.scrollTo(obj.scroll.x, obj.scroll.y);
	  }
	};

	/*
		Behavior:DragItem - adds ability to move items by dnd
		
		dnd context can have next properties
			from - source object
			to - target object
			source - id of dragged item(s)
			target - id of drop target, null for drop on empty space
			start - id from which DND was started
	*/

	var DragItem = {
	  //helper - defines component's container as active zone for dragging and for dropping
	  _initHandlers: function (obj, source, target) {
	    if (!source) DragControl.addDrop(obj._contentobj, obj, true);
	    if (!target) DragControl.addDrag(obj._contentobj, obj);
	    this.attachEvent("onDragOut", function (a, b) {
	      this.$dragMark(a, b);
	    });
	    this.attachEvent("onBeforeAutoScroll", function () {
	      var context = DragControl.getContext();
	      return !!(DragControl._active && context && (context.to === this || this._auto_scroll_force));
	    });
	  },
	  drag_setter: function (value) {
	    if (value) {
	      exports.extend(this, AutoScroll, true);
	      if (value == "order" || value == "move") exports.extend(this, use("DragOrder"), true);
	      if (value == "inner" || value == "order") this._inner_drag_only = true;

	      this._initHandlers(this, value == "source", value == "target");

	      delete this.drag_setter; //prevent double initialization
	    }

	    return value;
	  },

	  /*
	  	s - source html element
	  	t - target html element
	  	d - drop-on html element ( can be not equal to the target )
	  	e - native html event 
	  */
	  //called when drag moved over possible target
	  $dragIn: function (s, t, e) {
	    var id = this.locate(e) || null;
	    var context = DragControl._drag_context; //in inner drag mode - ignore dnd from other components

	    if ((this._inner_drag_only || context.from._inner_drag_only) && context.from !== this) return false;
	    var to = DragControl.getMaster(t); //previous target

	    var html = this.getItemNode(id, e) || this._dataobj; //prevent double processing of same target


	    if (html == DragControl._landing) return html;
	    context.target = id;
	    context.to = to;
	    if (this._auto_scroll_delay) this._auto_scroll_delay = window.clearTimeout(this._auto_scroll_delay);
	    this._auto_scroll_delay = delay(function (pos$$1, id) {
	      this._drag_pause(id);

	      this._auto_scroll(pos$$1, id);
	    }, this, [pos(e), id], 250);

	    if (!this.$dropAllow(context, e) || !this.callEvent("onBeforeDragIn", [context, e])) {
	      context.to = context.target = null;
	      if (this._auto_scroll_delay) this._auto_scroll_delay = window.clearTimeout(this._auto_scroll_delay);
	      return null;
	    } //mark target only when landing confirmed


	    this.$dragMark(context, e);
	    return html;
	  },
	  $dropAllow: function () {
	    return true;
	  },
	  _drag_pause: function () {//may be reimplemented in some components
	    // tree for example
	  },
	  _target_to_id: function (target) {
	    return target && _typeof(target) === "object" ? target.toString() : target;
	  },
	  //called when drag moved out from possible target
	  $dragOut: function (s, t, n, e) {
	    var id = (this._viewobj.contains(n) ? this.locate(e) : null) || null;
	    var context = DragControl._drag_context; //still over previous target

	    if ((context.target || "").toString() == (id || "").toString()) return null;

	    if (this._auto_scroll_delay) {
	      this._auto_scroll_force = null;
	      this._auto_scroll_delay = window.clearTimeout(this._auto_scroll_delay);
	    } //unmark previous target


	    context.target = context.to = null;
	    this.callEvent("onDragOut", [context, e]);
	    return null;
	  },
	  //called when drag moved on target and button is released
	  $drop: function (s, t, e) {
	    if (this._auto_scroll_delay) this._auto_scroll_delay = window.clearTimeout(this._auto_scroll_delay);
	    var context = DragControl._drag_context; //finalize context details

	    context.to = this;

	    this._define_index(s, t, context); //unmark last target


	    this.$dragMark({}, e);

	    if (context.from && context.from != context.to && context.from.callEvent) {
	      if (!context.from.callEvent("onBeforeDropOut", [context, e])) return;
	    }

	    if (!this.callEvent("onBeforeDrop", [context, e])) return; //moving

	    this._context_to_move(context, e);

	    this.callEvent("onAfterDrop", [context, e]);
	  },
	  _define_index: function (s, t, context) {
	    var target = this._target_to_id(context.target);

	    if (this.getBranchIndex) {
	      if (target) {
	        context.parent = this.getParentId(target);
	        context.index = this.getBranchIndex(target);
	      } else context.index = -1;
	    } else context.index = target ? this.getIndexById(target) : this.count();
	  },
	  _context_to_move: function (context) {
	    assert(context.from, "Unsopported d-n-d combination");

	    if (context.from && context.from.move) {
	      //from different component with item dnd
	      var details = {
	        parent: context.parent,
	        mode: context.pos
	      };
	      context.from.move(context.source, context.index, context.to, details);
	    }
	  },
	  _getDragItemPos: function (pos$$1, e) {
	    if (this.getItemNode) {
	      var id = this.locate(e, true); //in some case, node may be outiside of dom ( spans in datatable for example )
	      //so getItemNode can return null

	      var node = id ? this.getItemNode(id) : null;
	      return node ? offset(node) : node;
	    }
	  },
	  //called when drag action started
	  $drag: function (s, e) {
	    var id = this.locate(e, true);

	    if (id) {
	      var list = [id];

	      if (this.getSelectedId) {
	        //has selection model
	        //if dragged item is one of selected - drag all selected
	        var selection = this.getSelectedId(true, true);

	        if (selection && selection.length > 1 && PowerArray.find.call(selection, id) != -1) {
	          var hash = {};
	          list = [];

	          for (var i = 0; i < selection.length; i++) {
	            hash[selection[i]] = true;
	          }

	          for (var _i = 0; _i < this.data.order.length; _i++) {
	            var hash_id = this.data.order[_i];
	            if (hash[hash_id]) list.push(hash_id);
	          }
	        }
	      } //save initial dnd params


	      var context = DragControl._drag_context = {
	        source: list,
	        start: id
	      };
	      context.fragile = this.addRowCss && env.touch && (env.isWebKit || env.isFF);
	      context.from = this;

	      if (this.callEvent("onBeforeDrag", [context, e])) {
	        if (Touch) Touch._start_context = null; //set drag representation

	        return context.html || this.$dragHTML(this.getItem(id), e, context);
	      }
	    }

	    return null;
	  },
	  $dragHTML: function (obj, e, context) {
	    var html = this._toHTML(obj);

	    if (isArray(context.source) && context.source.length > 1) html = this._toMultipleHTML(html, context.source.length);
	    return html;
	  },
	  _toMultipleHTML: function (html, len) {
	    html = "<div class='webix_drag_main'>" + html + "</div>";
	    var multiple = "<div class='webix_drag_multiple'></div>";
	    if (len > 2) multiple = "<div class='webix_drag_multiple_last'></div>" + multiple;
	    return multiple + html + "<span class='webix_badge'>" + len + "</span>";
	  },
	  $dragMark: function (context) {
	    var target = null;
	    if (context.target) target = this._target_to_id(context.target); //touch webkit will stop touchmove event if source node removed
	    //datatable can't repaint rows without repainting

	    if (this._marked && this._marked != target) {
	      if (!context.fragile) this.removeCss(this._marked, "webix_drag_over");
	      this._marked = null;
	    }

	    if (!this._marked && target) {
	      this._marked = target;
	      if (!context.fragile) this.addCss(target, "webix_drag_over");
	      return target;
	    }

	    if (context.to) {
	      return true;
	    } else return false;
	  },
	  // methods used in order/move modes
	  $dropHTML: function () {
	    return "";
	  },
	  _set_drop_area: function (target, t) {
	    var node = this.getItemNode(target);

	    if (node) {
	      node.parentNode.insertBefore(DragControl._dropHTML[0], node);
	    } else t.children[0].appendChild(DragControl._dropHTML[0]);
	  }
	};

	var DragOrder = {
	  $drag: function (s, e) {
	    var html = DragItem.$drag.apply(this, arguments);
	    if (!html) return html;
	    var context = DragControl._drag_context;
	    if (this._close_branches) this._close_branches(context);
	    if (this._inner_drag_only && this.getBranchIndex) this._drag_order_stored_left = this._drag_order_complex ? (this.getItem(context.start).$level + 1) * 20 + 8 : 0;

	    if (isArray(context.source) && !context.fragile) {
	      DragControl._setDragOffset(e);

	      this._add_css(context.source);
	    }

	    return html;
	  },
	  _add_css: function (source) {
	    for (var i = 0; i < source.length; i++) {
	      this.addCss(source[i], "webix_invisible");
	    }
	  },
	  _remove_css: function (source) {
	    for (var i = 0; i < source.length; i++) {
	      this.removeCss(source[i], "webix_invisible");
	    }
	  },
	  $dragIn: function (s, t, e) {
	    var html = DragItem.$dragIn.apply(this, arguments);
	    if (!html) return html;
	    if (!DragControl._dropHTML) DragControl._dropHTML = this._init_drop_area();
	    var context = DragControl._drag_context;
	    var target = "$webix-last";
	    if (context.target) target = this._target_to_id(context.target);

	    if (target != "$webix-last" && target != "$webix-drop") {
	      var settings = {
	        direction: this._settings.layout || this._drag_direction || "y",
	        x: "width",
	        y: "height"
	      };
	      var ofs = offset(html);
	      var direction = pos(e)[settings.direction] - ofs[settings.direction];
	      if (direction * 2 > ofs[settings[settings.direction]]) target = this.getNextId(target) || "$webix-last";
	    }

	    if (target == this._marked_item_id || target == "$webix-drop") return html;
	    this._marked_item_id = target;

	    this._set_drop_area(target, t);

	    return html;
	  },
	  $dragPos: function (pos$$1) {
	    if (!this._inner_drag_only) {
	      var context = DragControl._drag_context;
	      pos$$1.y += context.y_offset;
	      pos$$1.x += context.x_offset;
	      return;
	    }

	    var box = offset(this.$view);
	    var xdrag = this._settings.layout == "x";

	    if (xdrag) {
	      box.x -= 12;
	      pos$$1.y = box.y - 8;
	      pos$$1.x = pos$$1.x - 18;
	      if (pos$$1.x < box.x) pos$$1.x = box.x;else {
	        var max = box.x + box.width;
	        if (pos$$1.x > max) pos$$1.x = max;
	      }
	    } else {
	      box.y += (this._header_height || 0) - 12;
	      pos$$1.x = (this._drag_order_stored_left || box.x) + 8;
	      pos$$1.y = pos$$1.y - 18;
	      if (pos$$1.y < box.y) pos$$1.y = box.y;else {
	        var _max = box.y + box.height - (this._header_height || 0);

	        if (pos$$1.y > _max) pos$$1.y = _max;
	      }
	    }
	  },
	  $dragOut: function (s, ot, nt) {
	    if (ot != nt) {
	      remove(DragControl._dropHTML);
	      this._marked_item_id = DragControl._dropHTML = null;
	    }

	    return DragItem.$dragOut.apply(this, arguments);
	  },
	  _define_index: function (s, t, context) {
	    var target = this._marked_item_id == "$webix-last" ? null : this._marked_item_id;

	    if (this.getBranchIndex) {
	      if (target) {
	        context.parent = this.getParentId(target);
	        context.index = this.getBranchIndex(target);
	        if (s == t && this.getParentId(context.start) == context.parent && this.getBranchIndex(context.start) < context.index) context.index -= 1;
	      } else context.index = -1;
	    } else {
	      context.index = target ? this.getIndexById(target) : this.count();
	      context.index -= s == t && this.getIndexById(context.start) < context.index ? 1 : 0;
	    }
	  },
	  $dragDestroy: function () {
	    var context = DragControl._drag_context;
	    if (isArray(context.source) && !context.fragile) this._remove_css(context.source);
	    remove(DragControl._html);
	  },
	  _init_drop_area: function () {
	    var node = document.createElement("div");
	    node.className = "webix_drop_area";
	    node.style.width = this.type.width + "px";
	    node.style.height = this.type.height + "px";
	    node.innerHTML = this.$dropHTML();
	    node.setAttribute(this._id, "$webix-drop");
	    return [node];
	  },
	  $dragMark: function () {
	    return false;
	  }
	};
	define("DragOrder", DragOrder);

	var Undo = {
	  $init: function () {
	    this._undoHistory = exports.extend([], PowerArray, true);
	    this._undoCursor = -1;
	  },
	  undo_setter: function (value) {
	    if (value) {
	      this._init_undo();

	      this._init_undo = function () {};
	    }

	    return value;
	  },
	  _init_undo: function () {
	    var view = this; // drag-n-drop

	    this.attachEvent("onBeforeDrop", function (context) {
	      if (context.from == context.to) {
	        var item = view._draggedItem = copy(this.getItem(context.start));

	        if (this.data.branch) {
	          item.$index = this.getBranchIndex(item.id);
	        } else item.$index = this.getIndexById(item.id);
	      }
	    });
	    this.data.attachEvent("onDataMove", function (sid) {
	      if (view._draggedItem && view._draggedItem.id == sid) {
	        var data = view._draggedItem;
	        view._draggedItem = null;

	        view._addToHistory(sid, data, "move");
	      }
	    }); // add, remove

	    this.data.attachEvent("onBeforeDelete", function (id) {
	      if (this.getItem(id)) {
	        var item = view._deletedItem = copy(this.getItem(id));

	        if (this.branch) {
	          item.$index = this.getBranchIndex(id);
	          if (this.branch[id]) item.$branch = copy(this.serialize(id));
	        } else item.$index = this.getIndexById(id);
	      }
	    });
	    this.data.attachEvent("onDataUpdate", function (id, data, old) {
	      view._addToHistory(id + "", old, "update");
	    });
	    this.data.attachEvent("onStoreUpdated", function (id, item, mode) {
	      var data = null;

	      if (id) {
	        if (mode == "add") {
	          data = copy(item);
	        } else if (mode == "delete") {
	          data = view._deletedItem;
	        }

	        if (data) view._addToHistory(id, data, mode);
	      }
	    }); // id change

	    this.data.attachEvent("onIdChange", function (oldId, newId) {
	      if (_typeof(oldId) == "object") oldId = oldId.row;

	      for (var i = 0; i < view._undoHistory.length; i++) {
	        if (view._undoHistory[i].id == oldId) {
	          view._undoHistory[i].id = newId;
	        }
	      }
	    });
	  },
	  _addToHistory: function (id, data, action) {
	    if (!this._skipHistory && this._settings.undo) {
	      this._undoHistory.push({
	        id: id,
	        action: action,
	        data: data
	      });

	      if (this._undoHistory.length == 20) this._undoHistory.splice(0, 1);
	      if (!this._skipCursorInc) this._undoCursor = this._undoHistory.length - 1;
	    }
	  },
	  ignoreUndo: function (func, master) {
	    this._skipHistory = true;
	    func.call(master || this);
	    this._skipHistory = false;
	  },
	  removeUndo: function (id) {
	    for (var i = this._undoHistory.length - 1; i >= 0; i--) {
	      if (this._undoHistory[i].id == id) {
	        if (this._undoHistory[i].action == "id") {
	          id = this._undoHistory[i].data;
	        }

	        this._undoHistory.removeAt(i);
	      }
	    }

	    this._undoCursor = this._undoHistory.length - 1;
	  },
	  undo: function (id) {
	    if (id) {
	      this.ignoreUndo(function () {
	        var data, i;

	        for (i = this._undoHistory.length - 1; !data && i >= 0; i--) {
	          if (this._undoHistory[i].id == id) data = this._undoHistory[i];
	        }

	        if (data) {
	          /*if(data.action == "id")
	          	id = data.data;*/
	          this._undoAction(data);

	          this._undoHistory.removeAt(i + 1);

	          this._undoCursor = this._undoHistory.length - 1;
	        }
	      });
	    } else {
	      var data = this._undoHistory[this._undoCursor];

	      if (data) {
	        this.ignoreUndo(function () {
	          this._undoAction(data);

	          this._undoHistory.removeAt(this._undoCursor);
	        });
	        this._undoCursor--;
	        /*if(data.action == "id")
	        	this.undo();*/
	      }
	    }
	  },
	  _undoAction: function (obj) {
	    if (obj.action == "delete") {
	      var branch = null,
	          parentId = obj.data.$parent;

	      if (obj.data.$branch) {
	        branch = {
	          parent: obj.id,
	          data: copy(obj.data.$branch)
	        };
	        delete obj.data.$branch;
	        if (parentId && !this.data.branch[parentId]) parentId = 0;
	      }

	      this.add(obj.data, obj.data.$index, parentId);

	      if (branch) {
	        this.parse(branch);
	      }
	    } else if (obj.action == "add") {
	      this.remove(obj.id);
	    } else if (obj.action == "update") {
	      this.updateItem(obj.id, obj.data);
	    } else if (obj.action == "move") {
	      if (obj.data.$parent) {
	        if (this.getItem(obj.data.$parent)) this.move(obj.id, obj.data.$index, null, {
	          parent: obj.data.$parent
	        });
	      } else this.move(obj.id, obj.data.$index);
	    }
	    /*else if(obj.action == "id"){
	    	this.data.changeId(obj.id, obj.data);
	    }*/

	  }
	};

	function init_suggest(editor, input) {
	  var suggest = editor.config.suggest;

	  if (suggest) {
	    var box = editor.config.suggest = create_suggest(suggest);
	    var boxobj = $$(box);
	    if (boxobj && input) boxobj.linkInput(input);
	  }
	}

	function create_suggest(config) {
	  if (typeof config == "string") return config;
	  if (config.linkInput) return config._settings.id;

	  if (_typeof(config) == "object") {
	    if (isArray(config)) config = {
	      data: config
	    };
	    config.view = config.view || "suggest";
	  } else if (config === true) config = {
	    view: "suggest"
	  };

	  var obj = ui(config);
	  return obj.config.id;
	}

	function getLabel(config) {
	  var text = config.header && config.header[0] ? config.header[0].text : config.editValue || config.label;
	  return (text || "").toString().replace(/<[^>]*>/g, "");
	}
	/*
	this.node - html node, available after render call
	this.config - editor config
	this.value - original value
	this.popup - id of popup 
	*/


	var editors = {
	  "text": {
	    focus: function () {
	      this.getInputNode(this.node).focus();
	      this.getInputNode(this.node).select();
	    },
	    getValue: function () {
	      return this.getInputNode(this.node).value;
	    },
	    setValue: function (value) {
	      var input = this.getInputNode(this.node);
	      input.value = value;
	      init_suggest(this, input);
	    },
	    getInputNode: function () {
	      return this.node.firstChild;
	    },
	    render: function () {
	      return create("div", {
	        "class": "webix_dt_editor"
	      }, "<input type='text' aria-label='" + getLabel(this.config) + "'>");
	    }
	  },
	  "inline-checkbox": {
	    render: function () {
	      return {};
	    },
	    getValue: function () {
	      return this.node.checked;
	    },
	    setValue: function () {},
	    focus: function () {
	      this.node.focus();
	    },
	    getInputNode: function () {},
	    $inline: true
	  },
	  "inline-text": {
	    render: function () {
	      return {};
	    },
	    getValue: function () {
	      return this.node.value;
	    },
	    setValue: function () {},
	    focus: function () {
	      try {
	        //IE9
	        this.node.select();
	        this.node.focus();
	      } catch (e) {} //eslint-disable-line

	    },
	    getInputNode: function () {},
	    $inline: true
	  },
	  "checkbox": {
	    focus: function () {
	      this.getInputNode().focus();
	    },
	    getValue: function () {
	      return this.getInputNode().checked;
	    },
	    setValue: function (value) {
	      this.getInputNode().checked = !!value;
	    },
	    getInputNode: function () {
	      return this.node.firstChild.firstChild;
	    },
	    render: function () {
	      return create("div", {
	        "class": "webix_dt_editor"
	      }, "<div><input type='checkbox' aria-label='" + getLabel(this.config) + "'></div>");
	    }
	  },
	  "select": {
	    focus: function () {
	      this.getInputNode().focus();
	    },
	    getValue: function () {
	      return this.getInputNode().value;
	    },
	    setValue: function (value) {
	      this.getInputNode().value = value;
	    },
	    getInputNode: function () {
	      return this.node.firstChild;
	    },
	    render: function () {
	      var html = "";
	      var options = this.config.options || this.config.collection;
	      assert(options, "options not defined for select editor");
	      if (options.data && options.data.each) options.data.each(function (obj) {
	        html += "<option value='" + obj.id + "'>" + obj.value + "</option>";
	      });else {
	        if (isArray(options)) {
	          for (var i = 0; i < options.length; i++) {
	            var rec = options[i];
	            var isplain = isUndefined(rec.id);
	            var id = isplain ? rec : rec.id;
	            var label = isplain ? rec : rec.value;
	            html += "<option value='" + id + "'>" + label + "</option>";
	          }
	        } else for (var key in options) {
	          html += "<option value='" + key + "'>" + options[key] + "</option>";
	        }
	      }
	      return create("div", {
	        "class": "webix_dt_editor"
	      }, "<select aria-label='" + getLabel(this.config) + "'>" + html + "</select>");
	    }
	  },
	  popup: {
	    focus: function () {
	      this.getInputNode().focus();
	    },
	    destroy: function () {
	      this.getPopup().hide();
	    },
	    getValue: function () {
	      return this.getInputNode().getValue() || "";
	    },
	    setValue: function (value) {
	      this.getPopup().show(this.node);
	      this.getInputNode().setValue(value);
	    },
	    getInputNode: function () {
	      return this.getPopup().getChildViews()[0];
	    },
	    getPopup: function () {
	      if (!this.config.$popup) this.config.$popup = this.createPopup();
	      return $$(this.config.$popup);
	    },
	    createPopup: function () {
	      var popup = this.config.popup || this.config.suggest;

	      if (popup) {
	        var pobj;

	        if (_typeof(popup) == "object" && !popup.name) {
	          popup.view = popup.view || "suggest";
	          pobj = ui(copy(popup));
	        } else pobj = $$(popup); //custom popup may be linked already


	        if (!pobj._linked) {
	          if (pobj.linkInput) pobj.linkInput(document.body);else if (this.linkInput) this.linkInput(document.body);
	          pobj._linked = true;
	        }

	        return pobj;
	      }

	      var type = editors.$popup[this.popupType];

	      if (typeof type != "string" && !type.name) {
	        type = editors.$popup[this.popupType] = ui(type);
	        this.popupInit(type);
	        if (!type.linkInput) this.linkInput(document.body);
	      }

	      return type._settings.id;
	    },
	    linkInput: function (node) {
	      _event(toNode(node), "keydown", bind(function (e) {
	        //abort, when editor was not initialized yet
	        if (!this.config.$popup) return;
	        var code = e.which || e.keyCode,
	            list = this.getInputNode();
	        if (!list.isVisible()) return;

	        if (list.moveSelection && code < 41 && code > 32) {
	          var dir;
	          if (code == 33) dir = "pgup";
	          if (code == 34) dir = "pgdown";
	          if (code == 35) dir = "bottom";
	          if (code == 36) dir = "top";
	          if (code == 37) dir = "left";
	          if (code == 38) dir = "up";
	          if (code == 39) dir = "right";
	          if (code == 40) dir = "down";
	          list.moveSelection(dir);
	        } // shift+enter support for 'popup' editor
	        else if (code === 13 && (e.target.nodeName !== "TEXTAREA" || !e.shiftKey)) callEvent("onEditEnd", []);
	      }, this));
	    },
	    popupInit: function () {},
	    popupType: "text",
	    render: function () {
	      return {};
	    },
	    $inline: true
	  }
	};
	editors.color = exports.extend({
	  focus: function () {},
	  popupType: "color",
	  popupInit: function (popup) {
	    popup.getChildViews()[0].attachEvent("onItemClick", function (value) {
	      callEvent("onEditEnd", [value]);
	    });
	  }
	}, editors.popup);
	editors.date = exports.extend({
	  focus: function () {},
	  popupType: "date",
	  setValue: function (value) {
	    this._is_string = this.config.stringResult || value && typeof value == "string";
	    editors.popup.setValue.call(this, value);
	  },
	  getValue: function () {
	    return this.getInputNode().getValue(this._is_string ? i18n.parseFormatStr : "") || "";
	  },
	  popupInit: function (popup) {
	    popup.getChildViews()[0].attachEvent("onDateSelect", function (value) {
	      callEvent("onEditEnd", [value]);
	    });
	  }
	}, editors.popup);
	editors.combo = exports.extend({
	  _create_suggest: function (config) {
	    if (this.config.popup) {
	      return this.config.popup.config.id;
	    } else if (config) {
	      return create_suggest(config);
	    } else return this._shared_suggest(config);
	  },
	  _shared_suggest: function () {
	    var e = editors.combo;
	    return e._suggest = e._suggest || this._create_suggest(true);
	  },
	  render: function () {
	    var node = create("div", {
	      "class": "webix_dt_editor"
	    }, "<input type='text' role='combobox' aria-label='" + getLabel(this.config) + "'>"); //save suggest id for future reference		

	    var suggest = this.config.suggest = this._create_suggest(this.config.suggest);

	    if (suggest) {
	      $$(suggest).linkInput(node.firstChild, true);

	      _event(node.firstChild, "click", bind(this.showPopup, this));
	    }

	    return node;
	  },
	  getPopup: function () {
	    return $$(this.config.suggest);
	  },
	  showPopup: function () {
	    var popup = this.getPopup();
	    var list = popup.getList();
	    var input = this.getInputNode();
	    var value = this._initial_value;
	    popup.show(input);
	    input.setAttribute("aria-expanded", "true");

	    if (value) {
	      assert(list.exists(value), "Option with ID " + value + " doesn't exist");

	      if (list.exists(value)) {
	        list.select(value);
	        list.showItem(value);
	      }
	    } else {
	      list.unselect();
	      list.showItem(list.getFirstId());
	    }

	    popup._last_input_target = input;
	  },
	  afterRender: function () {
	    this.showPopup();
	  },
	  setValue: function (value) {
	    this._initial_value = value;

	    if (this.config.suggest) {
	      var sobj = $$(this.config.suggest);
	      var data = this.config.collection || this.config.options;
	      if (data) sobj.getList().data.importData(data);
	      this.getInputNode(this.node).value = sobj.getItemText(value);
	    }
	  },
	  getValue: function () {
	    var value = this.getInputNode().value;

	    if (this.config.suggest) {
	      var suggest = $$(this.config.suggest),
	          list = suggest.getList();
	      if (value || list.getSelectedId && list.getSelectedId()) value = suggest.getSuggestion(value);
	    }

	    return value;
	  }
	}, editors.text);
	editors.richselect = exports.extend({
	  focus: function () {},
	  getValue: function () {
	    return this.getPopup().getValue();
	  },
	  setValue: function (value) {
	    var suggest = this.config.collection || this.config.options;
	    this.getInputNode();
	    if (suggest) this.getPopup().getList().data.importData(suggest);
	    this.getPopup().show(this.node);
	    this.getPopup().setValue(value);
	  },
	  getInputNode: function () {
	    return this.getPopup().getList();
	  },
	  popupInit: function (popup) {
	    popup.linkInput(document.body);
	  },
	  popupType: "richselect"
	}, editors.popup);
	editors.password = exports.extend({
	  render: function () {
	    return create("div", {
	      "class": "webix_dt_editor"
	    }, "<input type='password' aria-label='" + getLabel(this.config) + "'>");
	  }
	}, editors.text);
	editors.$popup = {
	  text: {
	    view: "popup",
	    width: 250,
	    height: 150,
	    body: {
	      view: "textarea"
	    }
	  },
	  color: {
	    view: "popup",
	    body: {
	      view: "colorboard"
	    }
	  },
	  date: {
	    view: "popup",
	    width: 250,
	    height: 250,
	    padding: 0,
	    body: {
	      view: "calendar",
	      icons: true,
	      borderless: true
	    }
	  },
	  richselect: {
	    view: "suggest",
	    body: {
	      view: "list",
	      select: true
	    }
	  },
	  multiselect: {
	    view: "multisuggest",
	    suggest: {
	      button: true
	    }
	  }
	};

	/*
		Behavior:EditAbility - enables item operation for the items
		
		@export
			edit
			stopEdit
	*/

	var EditAbility = {
	  defaults: {
	    editaction: "click"
	  },
	  $init: function (config) {
	    this._editors = {};
	    this._in_edit_mode = 0;
	    this._edit_open_time = 0;
	    this._contentobj.style.position = "relative";
	    if (config) config.onDblClick = config.onDblClick || {};
	    this.attachEvent("onAfterRender", this._refocus_inline_editor); //when we call extend the editable prop can be already set

	    if (this._settings.editable) this._init_edit_events_once();
	    exports.extend(this, Undo);
	  },
	  _refocus_try: function (newnode) {
	    try {
	      //Chrome throws an error if selectionStart is not accessible
	      if (typeof newnode.selectionStart == "number") {
	        newnode.selectionStart = newnode.selectionEnd = newnode.value.length;
	      } else if (typeof newnode.createTextRange != "undefined") {
	        var range = newnode.createTextRange();
	        range.collapse(false);
	        range.select();
	      }
	    } catch (e) {} // eslint-disable-line

	  },
	  _refocus_inline_editor: function () {
	    var editor = this.getEditor();

	    if (editor && editor.$inline && !editor.getPopup) {
	      var newnode = this._locateInput(editor);

	      if (newnode && newnode != editor.node) {
	        var text = editor.node.value;
	        editor.node = newnode;
	        newnode.value = text;
	        newnode.focus();

	        this._refocus_try(newnode);
	      } else this.editStop();
	    }
	  },
	  editable_setter: function (value) {
	    if (value) this._init_edit_events_once();
	    return value;
	  },
	  _init_edit_events_once: function () {
	    //will close editor on any click outside
	    attachEvent("onEditEnd", bind(function () {
	      if (this._in_edit_mode) this.editStop();
	    }, this));
	    attachEvent("onClick", bind(function (e) {
	      //but ignore click which opens editor
	      if (this._in_edit_mode && new Date() - this._edit_open_time > 200) {
	        if (!this._last_editor || this._last_editor.popupType || !e || !this._last_editor.node || !this._last_editor.node.contains(e.target || e.srcElement)) this.editStop();
	      }
	    }, this)); //property sheet has simple data object, without events

	    if (this.data.attachEvent) this.data.attachEvent("onIdChange", bind(function (oldid, newid) {
	      this._changeEditorId(oldid, newid);
	    }, this)); //when clicking on row - will start editor

	    this.attachEvent("onItemClick", function (id) {
	      if (this._settings.editable && this._settings.editaction == "click") this.edit(id);
	    });
	    this.attachEvent("onItemDblClick", function (id) {
	      if (this._settings.editable && this._settings.editaction == "dblclick") this.edit(id);
	    }); //each time when we clicking on input, reset timer to prevent self-closing

	    this._reset_active_editor = bind(function () {
	      this._edit_open_time = new Date();
	    }, this);

	    this._init_edit_events_once = function () {};

	    if (this._component_specific_edit_init) this._component_specific_edit_init();
	  },
	  _handle_live_edits: function () {
	    delay(function () {
	      var editor = this.getEditor();

	      if (editor && editor.config.liveEdit) {
	        var state$$1 = {
	          value: editor.getValue(),
	          old: editor.value
	        };
	        if (state$$1.value == state$$1.old) return;
	        editor.value = state$$1.value;

	        this._set_new_value(editor, state$$1.value, false);

	        this.callEvent("onLiveEdit", [state$$1, editor]);
	      }
	    }, this);
	  },
	  _show_editor_form: function (id) {
	    var form = this._settings.form;
	    if (typeof form != "string") this._settings.form = form = ui(form).config.id;
	    form = $$(form);
	    var realform = form.setValues ? form : form.getChildViews()[0];
	    realform.setValues(this.getItem(id.row || id));
	    form.config.master = this.config.id;
	    form.show(this.getItemNode(id));
	    var first = realform.getChildViews()[0];
	    if (first.focus) first.focus();
	  },
	  edit: function (id, preserve, show) {
	    if (!this._settings.editable || !this.callEvent("onBeforeEditStart", [id])) return;
	    if (this._settings.form) return this._show_editor_form(id);

	    var editor = this._get_editor_type(id);

	    if (editor) {
	      if (this.getEditor(id)) return;
	      if (!preserve) this.editStop(); //render html input

	      assert(editors[editor], "Invalid editor type: " + editor);
	      var type = exports.extend({}, editors[editor]);

	      var node = this._init_editor(id, type, show);

	      if (type.config.liveEdit) this._live_edits_handler = this.attachEvent("onKeyPress", this._handle_live_edits);
	      var area = type.getPopup ? type.getPopup(node)._viewobj : node;
	      if (area) _event(area, "click", this._reset_active_editor);
	      if (node) _event(node, "change", this._on_editor_change, {
	        bind: {
	          view: this,
	          id: id
	        }
	      });
	      if (show !== false) type.focus();
	      if (this.$fixEditor) this.$fixEditor(type); //save time of creation to prevent instant closing from the same click

	      this._edit_open_time = state.edit_open_time = new Date();
	      UIManager.setFocus(this, true);
	      this.callEvent("onAfterEditStart", [id]);
	      return type;
	    }

	    return null;
	  },
	  getEditor: function (id) {
	    if (!id) return this._last_editor;
	    return this._editors[id];
	  },
	  _changeEditorId: function (oldid, newid) {
	    var editor = this._editors[oldid];

	    if (editor) {
	      this._editors[newid] = editor;
	      editor.id = newid;
	      delete this._editors[oldid];
	    }
	  },
	  _on_editor_change: function () {
	    if (this.view.hasEvent("onEditorChange")) this.view.callEvent("onEditorChange", [this.id, this.view.getEditorValue(this.id)]);
	  },
	  _get_edit_config: function () {
	    return this._settings;
	  },
	  _init_editor: function (id, type, show) {
	    type.config = this._get_edit_config(id);
	    var node = type.render();
	    if (type.$inline) node = this._locateInput(id);
	    type.node = node;
	    var item = this.getItem(id); //value can be configured by editValue option

	    var value = item[this._settings.editValue || "value"]; //if property was not defined - use empty value

	    if (isUndefined(value)) value = "";
	    type.setValue(value, item);
	    type.value = value;

	    this._addEditor(id, type); //show it over cell


	    if (show !== false) this.showItem(id);
	    if (!type.$inline) this._sizeToCell(id, node, true);
	    if (type.afterRender) type.afterRender();
	    return node;
	  },
	  _locate_cell: function (id) {
	    return this.getItemNode(id);
	  },
	  _locateInput: function (id) {
	    var cell = this._locate_cell(id);

	    if (cell) cell = cell.getElementsByTagName("input")[0] || cell;
	    return cell;
	  },
	  _get_editor_type: function () {
	    return this._settings.editor;
	  },
	  _addEditor: function (id, type) {
	    type.id = id;
	    this._editors[id] = this._last_editor = type;
	    this._in_edit_mode++;
	  },
	  _removeEditor: function (editor) {
	    if (this._last_editor == editor) this._last_editor = 0;
	    if (editor.destroy) editor.destroy();
	    delete editor.popup;
	    delete editor.node;
	    delete this._editors[editor.id];
	    this._in_edit_mode--;
	  },
	  focusEditor: function () {
	    var editor = this.getEditor.apply(this, arguments);
	    if (editor && editor.focus) editor.focus();
	  },
	  editCancel: function () {
	    this.editStop(null, null, true);
	  },
	  _applyChanges: function (el) {
	    if (el) {
	      var ed = this.getEditor();
	      if (ed && ed.getPopup && ed.getPopup() == el.getTopParentView()) return;
	    }

	    this.editStop();
	  },
	  editStop: function (id) {
	    if (this._edit_stop) return;
	    this._edit_stop = 1;
	    var cancel = arguments[2];
	    var result = 1;

	    if (!id) {
	      this._for_each_editor(function (editor) {
	        result = result * this._editStop(editor, cancel);
	      });
	    } else result = this._editStop(this._editors[id], cancel);

	    this._edit_stop = 0;
	    return result;
	  },
	  _cellPosition: function (id) {
	    var html = this.getItemNode(id);
	    return {
	      left: html.offsetLeft,
	      top: html.offsetTop,
	      height: html.offsetHeight,
	      width: html.offsetWidth,
	      parent: this._contentobj
	    };
	  },
	  _sizeToCell: function (id, node, inline) {
	    //fake inputs
	    if (!node.style) return;

	    var pos$$1 = this._cellPosition(id);

	    node.style.top = pos$$1.top + "px";
	    node.style.left = pos$$1.left + "px";
	    node.style.width = pos$$1.width - 1 + "px";
	    node.style.height = pos$$1.height - 1 + "px";
	    node.top = pos$$1.top; //later will be used during y-scrolling

	    if (inline) pos$$1.parent.appendChild(node);
	    return pos$$1;
	  },
	  _for_each_editor: function (handler) {
	    for (var editor in this._editors) {
	      handler.call(this, this._editors[editor]);
	    }
	  },
	  _editStop: function (editor, ignore) {
	    if (!editor || state._final_destruction) return;
	    var state$$1 = {
	      value: this._get_new_value(editor),
	      old: editor.value
	    };

	    if (this.callEvent("onBeforeEditStop", [state$$1, editor, ignore])) {
	      if (!ignore) {
	        //special case, state.old = 0, state.value = ""
	        //we need to state.old to string, to detect the change
	        var old = state$$1.old;
	        if (typeof state$$1.value == "string") old += "";

	        if (old != state$$1.value || editor.config.liveEdit) {
	          var item = this._set_new_value(editor, state$$1.value, true);

	          this.updateItem(editor.row || editor.id, item);
	        }
	      }

	      if (editor.$inline) editor.node = null;else remove(editor.node);
	      var popup = editor.config.suggest;
	      if (popup && typeof popup == "string") $$(popup).hide();

	      this._removeEditor(editor);

	      if (this._live_edits_handler) this.detachEvent(this._live_edits_handler);
	      this.callEvent("onAfterEditStop", [state$$1, editor, ignore]);
	      return 1;
	    }

	    return 0;
	  },
	  validateEditor: function (id) {
	    var result = true;

	    if (this._settings.rules) {
	      var editor = this.getEditor(id);
	      var key = editor.column || this._settings.editValue || "value";
	      var rule = this._settings.rules[key];
	      var all = this._settings.rules.$all;

	      if (rule || all) {
	        var obj = this.data.getItem(editor.row || editor.id);
	        var value = editor.getValue();
	        var input = editor.getInputNode();
	        if (rule) result = rule.call(this, value, obj, key);
	        if (all) result = all.call(this, value, obj, key) && result;
	        if (result) removeCss(input, "webix_invalid");else addCss(input, "webix_invalid");
	        callEvent("onLiveValidation", [editor, result, obj, value]);
	      }
	    }

	    return result;
	  },
	  getEditorValue: function (id) {
	    var editor;
	    if (arguments.length === 0) editor = this._last_editor;else editor = this.getEditor(id);
	    if (editor) return editor.getValue();
	  },
	  getEditState: function () {
	    return this._last_editor || false;
	  },
	  editNext: function (next, from) {
	    next = next !== false; //true by default

	    if (this._in_edit_mode == 1 || from) {
	      //only if one editor is active
	      var editor_next = this._find_cell_next(this._last_editor || from, function (id) {
	        if (this._get_editor_type(id)) return true;
	        return false;
	      }, next);

	      if (this.editStop()) {
	        //if we was able to close previous editor
	        if (editor_next) {
	          //and there is a new target
	          this.edit(editor_next); //init new editor

	          this._after_edit_next(editor_next);
	        }

	        return false;
	      }
	    }
	  },
	  //stab, used in datatable
	  _after_edit_next: function () {},
	  _find_cell_next: function (start, check, direction) {
	    var row = this.getIndexById(start.id);
	    var order = this.data.order;

	    if (direction) {
	      for (var i = row + 1; i < order.length; i++) {
	        if (check.call(this, order[i])) return order[i];
	      }
	    } else {
	      for (var _i = row - 1; _i >= 0; _i--) {
	        if (check.call(this, order[_i])) return order[_i];
	      }
	    }

	    return null;
	  },
	  _get_new_value: function (editor) {
	    return editor.getValue();
	  },
	  _set_new_value: function (editor, new_value, copy$$1) {
	    var item = copy$$1 ? {} : this.getItem(editor.id);
	    item[this._settings.editValue || "value"] = new_value;
	    return item;
	  }
	};

	var FlexLayout = {
	  $init: function () {
	    this.$view.className += " webix_flexlayout";
	  },
	  _fix_vertical_layout: function () {},
	  _beforeResetBorders: function () {},
	  _afterResetBorders: function () {},
	  $getSize: function () {
	    debug_size_box_start(this, true);
	    var w = 0,
	        h = 0,
	        g = this._settings.gravity;
	    this._sizes = [];

	    for (var i = 0; i < this._cells.length; i++) {
	      var size = this._cells[i].$getSize(0, 0);

	      this._sizes.push(size);

	      w = Math.max(w, size[0]);
	      h = Math.max(h, size[2]);
	    }

	    w += this._padding.left + this._padding.right;
	    h += this._padding.top + this._padding.bottom;
	    if (this._settings.width) w = Math.max(w, this._settings.width);
	    if (this._settings.height) h = Math.max(h, this._settings.height);
	    var self_size = [w, 100000, h, 100000, g];
	    debug_size_box_end(this, self_size);
	    return self_size;
	  },
	  render: function () {
	    this.resize();
	  },
	  _set_child_size: function () {
	    if (!this.isVisible(this._settings.id)) return;
	    var st = this.$view.style;
	    var margin = Math.round(this._margin / 2);
	    st.paddingTop = this._padding.top - margin + "px";
	    st.paddingBottom = this._padding.bottom - margin + "px";
	    st.paddingLeft = this._padding.left - margin + "px";
	    st.paddingRight = this._padding.right - margin + "px";

	    for (var i = 0; i < this._cells.length; i++) {
	      if (this._cells[i]._settings.hidden) continue;
	      var view = this._cells[i].$view;
	      var size = this._sizes[i];
	      var config = this._cells[i]._settings;

	      if (view) {
	        view.style.minWidth = size[0] + "px";
	        if (size[1] < 100000 && size[1] != size[0]) view.style.maxWidth = size[1] + "px";
	        view.style.flexBasis = config.flexBasis || size[0] + "px";
	        view.style.flexGrow = config.flexGrow || (size[1] != size[0] ? size[4] : 0);
	        view.style.height = size[3] != size[2] ? "auto" : size[2] + "px";
	        view.style.minHeight = size[2] + "px";
	        if (size[3] < 100000 && size[3] != size[2]) view.style.maxHeight = size[3] + "px";
	        view.style.margin = margin + "px";
	      }
	    }

	    var whs = [];

	    for (var _i = 0; _i < this._cells.length; _i++) {
	      if (this._cells[_i]._settings.hidden) continue;
	      var _view = this._cells[_i].$view;
	      whs[_i] = [_view.offsetWidth, _view.offsetHeight];
	    }

	    for (var _i2 = 0; _i2 < this._cells.length; _i2++) {
	      if (this._cells[_i2]._settings.hidden) continue;
	      var cell = this._cells[_i2];
	      var _view2 = cell.$view;

	      if (_view2) {
	        cell._settings.flex = true;
	        var _size = this._sizes[_i2];
	        var h = _size[2] == _size[3] ? _size[2] : whs[_i2][1];
	        cell.$setSize(whs[_i2][0], h);
	        cell._settings.flex = false;
	      }
	    }

	    this.$height = this._content_height = this.$view.scrollHeight;
	    this.$view.style.height = this._content_height + "px";
	  }
	};

	var GroupMethods = {
	  sum: function (property, data) {
	    data = data || this;
	    var summ = 0;

	    for (var i = 0; i < data.length; i++) {
	      var num = parseFloat(property(data[i]), 10);
	      if (!isNaN(num)) summ += num;
	    }

	    return summ;
	  },
	  min: function (property, data) {
	    data = data || this;
	    var min = Infinity;

	    for (var i = 0; i < data.length; i++) {
	      var num = parseFloat(property(data[i]), 10);
	      if (isNaN(num)) continue;
	      if (num < min) min = num;
	    }

	    return min === Infinity ? 0 : min * 1;
	  },
	  max: function (property, data) {
	    data = data || this;
	    var max = -Infinity;

	    for (var i = 0; i < data.length; i++) {
	      var num = parseFloat(property(data[i]), 10);
	      if (isNaN(num)) continue;
	      if (num > max) max = num;
	    }

	    return max === -Infinity ? 0 : max * 1;
	  },
	  count: function (property, data) {
	    var count = 0;

	    for (var i = 0; i < data.length; i++) {
	      var some = property(data[i]);
	      if (some !== null && typeof some !== "undefined") count++;
	    }

	    return count;
	  },
	  any: function (property, data) {
	    return property(data[0]);
	  },
	  string: function (property) {
	    return property.$name;
	  }
	};

	var GroupStore = {
	  $init: function () {
	    this.attachEvent("onClearAll", this._reset_groups);
	  },
	  _reset_groups: function () {
	    if (this.getBranchIndex) this._not_grouped_branches = this._created_groupes = null;else this._not_grouped_order = this._not_grouped_pull = null;
	  },
	  ungroup: function (skipRender) {
	    if (this.getBranchIndex) return this._ungroup_tree.apply(this, arguments);

	    if (this._not_grouped_order) {
	      this.order = this._not_grouped_order;
	      this.pull = this._not_grouped_pull;
	      this._not_grouped_pull = this._not_grouped_order = null;
	    }

	    if (!skipRender) this.callEvent("onStoreUpdated", []);
	  },
	  _group_processing: function (scheme) {
	    this.blockEvent();
	    this.group(scheme);
	    this.unblockEvent();
	  },
	  _group_prop_accessor: function (val) {
	    if (typeof val == "function") return val;

	    var acc = function (obj) {
	      return obj[val];
	    };

	    acc.$name = val;
	    return acc;
	  },
	  group: function (config, target) {
	    assert(config, "Empty config");
	    var input;

	    if (typeof config === "string") {
	      input = config;
	      config = {
	        by: this._group_prop_accessor(config),
	        map: {}
	      };
	    } else if (typeof config === "function") {
	      config = {
	        by: config,
	        map: {}
	      };
	    } else if (typeof config.by === "string") {
	      input = config.by;
	      config.by = this._group_prop_accessor(config.by);
	    }

	    config.map = config.map || {};
	    if (input && !config.map[input]) config.map[input] = [input];
	    config.missing = config.missing === undefined ? true : config.missing;
	    if (this.getBranchIndex) return this._group_tree(config, target);
	    var groups = {};
	    var labels = [];
	    var missed = [];
	    this.each(function (data) {
	      var current = config.by(data);
	      if (!current && current !== 0) if (config.missing === false) return;else if (config.missing === true) {
	        missed.push(data);
	        return;
	      } else current = config.missing;

	      if (!groups[current]) {
	        labels.push({
	          id: current,
	          value: current,
	          $group: true,
	          $row: config.row
	        });
	        groups[current] = toArray();
	      }

	      groups[current].push(data);
	    });

	    for (var i = 0; i < labels.length; i++) {
	      var group = labels[i];

	      this._map_group(config.map, group, groups[labels[i].id]);

	      if (this.hasEvent("onGroupCreated")) this.callEvent("onGroupCreated", [group.id, group.value, groups[labels[i].id]]);
	    }

	    if (!this._not_grouped_order) {
	      this._not_grouped_order = this.order;
	      this._not_grouped_pull = this.pull;
	    }

	    this.order = toArray();
	    this.pull = {};

	    this._fill_pull(labels);

	    this._fill_pull(missed);

	    this.callEvent("onStoreUpdated", []);
	  },
	  _fill_pull: function (arr) {
	    for (var i = 0; i < arr.length; i++) {
	      var id = this.id(arr[i]);
	      if (this.pull[id]) id = arr[i].id = uid();
	      this.pull[id] = arr[i];
	      this.order.push(id);
	      if (this._scheme_init) this._scheme_init(arr[i]);
	    }
	  },
	  _map_group: function (map, group, data) {
	    for (var prop in map) {
	      var functor = map[prop][1] || "any";

	      var property = this._group_prop_accessor(map[prop][0]);

	      if (typeof functor != "function") {
	        assert(GroupMethods[functor], "Unknown grouping rule: " + functor);
	        functor = GroupMethods[functor];
	      }

	      group[prop] = functor.call(this, property, data);
	    }
	  },
	  _group_tree: function (config, parent) {
	    //prepare
	    var level = 0;
	    if (parent) level = this.getItem(parent).$level;else parent = 0;

	    if (!this._not_grouped_branches) {
	      this._not_grouped_branches = copy(this.branch);
	      this._created_groupes = [];
	    } //run


	    var topbranch = [];
	    var labels = [];
	    var missed = [];
	    var order = this.branch[parent];

	    for (var i = 0; i < order.length; i++) {
	      var data = this.getItem(order[i]);
	      var current = config.by(data);
	      if (!current && current !== 0) if (config.missing === false) continue;else if (config.missing === true) {
	        missed.push(data.id);
	        continue;
	      } else current = config.missing;
	      var current_id = level + "$" + current;
	      var ancestor = this.branch[current_id];

	      if (!ancestor) {
	        var newitem = this.pull[current_id] = {
	          id: current_id,
	          value: current,
	          $group: true,
	          $row: config.row
	        };
	        if (this._scheme_init) this._scheme_init(newitem);
	        labels.push(newitem);
	        ancestor = this.branch[current_id] = [];
	        ancestor._formath = [];
	        topbranch.push(current_id);
	      }

	      ancestor.push(data.id);

	      ancestor._formath.push(data);
	    }

	    this._created_groupes = this._created_groupes.concat(topbranch);
	    this.branch[parent] = topbranch.concat(missed);

	    for (var _i = 0; _i < labels.length; _i++) {
	      var group = labels[_i];

	      this._map_group(config.map, group, this.branch[labels[_i].id]._formath);

	      if (this.hasEvent("onGroupCreated")) this.callEvent("onGroupCreated", [group.id, group.value, this.branch[group.id]._formath]);

	      if (config.footer) {
	        var id = "footer$" + group.id;
	        var footer = this.pull[id] = {
	          id: id,
	          $footer: true,
	          value: group.value,
	          $level: level,
	          $count: 0,
	          $parent: group.id,
	          $row: config.footer.row
	        };

	        this._map_group(config.footer, footer, this.branch[labels[_i].id]._formath);

	        this.branch[group.id].push(footer.id);
	        this.callEvent("onGroupFooter", [footer.id, footer.value, this.branch[group.id]._formath]);
	      }

	      delete this.branch[group.id]._formath;
	    }

	    this._fix_group_levels(this.branch[parent], parent, level + 1);

	    this.callEvent("onStoreUpdated", []);
	  },
	  _ungroup_tree: function (skipRender) {
	    if (this._not_grouped_branches) {
	      this.branch = this._not_grouped_branches;

	      while (this._created_groupes.length) {
	        delete this.pull[this._created_groupes.pop()];
	      }

	      this._not_grouped_branches = this._created_groupes = null;

	      this._fix_group_levels(this.branch[0], 0, 1);
	    }

	    if (!skipRender) this.callEvent("onStoreUpdated", []);
	  },
	  _fix_group_levels: function (branch, parent, level) {
	    if (parent) this.getItem(parent).$count = branch.length;

	    for (var i = 0; i < branch.length; i++) {
	      var item = this.pull[branch[i]];
	      item.$level = level;
	      item.$parent = parent;
	      var next = this.branch[item.id];
	      if (next) this._fix_group_levels(next, item.id, level + 1);
	    }
	  }
	};

	var Group = {
	  $init: function () {
	    exports.extend(this.data, GroupStore);
	  },
	  group: function (config, target) {
	    if (!target && target !== 0) this.data.ungroup(true);
	    this.data.group(config, target);
	  },
	  ungroup: function (skipRender) {
	    this.data.ungroup(skipRender);
	  }
	};

	/*aria-style handling for options of multiple-value controls (radio, segmented, tabbar)*/

	var HTMLOptions = {
	  $init: function () {
	    var _this = this;

	    if ($active.customRadio || this.addOption) this.$ready.push(function () {
	      _event(_this.$view, "keydown", _this._moveSelection, {
	        bind: _this
	      });
	    });
	  },
	  _focus: function () {
	    if (!UIManager.canFocus(this)) return false;

	    var input = this._getInputNode();

	    if (input) {
	      for (var i = 0; i < input.length; i++) {
	        if (input[i].getAttribute("tabindex") == "0") {
	          input[i].focus();
	        }
	      }
	    }
	  },
	  _blur: function () {
	    var input = this._getInputNode();

	    if (input) for (var i = 0; i < input.length; i++) {
	      if (input[i].getAttribute("tabindex") == "0") input[i].blur();
	    }
	  },
	  _moveSelection: function (e) {
	    var code = e.which || e.keyCode;
	    var startCode = this.addOption ? 34 : 36;

	    if (code > startCode && code < 41) {
	      preventEvent(e);
	      var index$$1;

	      var inp = this._getInputNode();

	      if (code == 35) index$$1 = inp.length - 1;else if (code === 36) index$$1 = 0;else {
	        var dir = code === 37 || code === 38 ? -1 : 1;

	        for (var i = 0; i < inp.length; i++) {
	          if (inp[i].getAttribute("tabindex") == "0") {
	            index$$1 = i + dir;
	            if (index$$1 < 0) index$$1 = inp.length - 1;else if (index$$1 >= inp.length) index$$1 = 0;
	            break;
	          }
	        }
	      }

	      if (!isUndefined(index$$1)) {
	        var id = inp[index$$1].getAttribute(
	        /*@attr*/
	        "button_id");
	        this.setValue(id);
	        inp[index$$1].focus();
	      }
	    }
	  },
	  _get_tooltip_data: function (t, e) {
	    var node = e.target || e.srcElement;

	    while (node && !node.webix_tooltip) {
	      var id = node.getAttribute("webix_t_id");
	      if (id) return this.getOption(id);
	      node = node.parentNode;
	    }

	    return null;
	  },
	  getOption: function (id) {
	    var options = this._check_options(this._settings.options);

	    for (var i = 0; i < options.length; i++) {
	      if (options[i].id == id) return options[i];
	    }

	    return null;
	  }
	};

	var HtmlMap = exports.proto({
	  $init: function (key) {
	    this._id = "map_" + uid();
	    this._key = key;
	    this._map = [];
	    this._areas = [];
	  },
	  addRect: function (id, points, userdata) {
	    this._createMapArea(id, "RECT", points, userdata);
	  },
	  addPoly: function (id, points, userdata) {
	    this._createMapArea(id, "POLY", points, userdata);
	  },
	  _createMapArea: function (id, shape, coords, userdata) {
	    var extra_data = "";
	    if (arguments.length == 4) extra_data = "userdata='" + userdata + "'";

	    this._map.push("<area " + this._key + "='" + id + "' shape='" + shape + "' coords='" + coords.join() + "' " + extra_data + "></area>");

	    this._areas.push({
	      index: userdata,
	      points: coords
	    });
	  },
	  addSector: function (id, alpha0, alpha1, x, y, R, ky, userdata) {
	    var points = [];
	    points.push(x);
	    points.push(Math.floor(y * ky));

	    for (var i = alpha0; i < alpha1; i += Math.PI / 18) {
	      points.push(Math.floor(x + R * Math.cos(i)));
	      points.push(Math.floor((y + R * Math.sin(i)) * ky));
	    }

	    points.push(Math.floor(x + R * Math.cos(alpha1)));
	    points.push(Math.floor((y + R * Math.sin(alpha1)) * ky));
	    points.push(x);
	    points.push(Math.floor(y * ky));
	    return this.addPoly(id, points, userdata);
	  },
	  hide: function (obj, data, mode) {
	    if (obj.querySelectorAll) {
	      var nodes = obj.querySelectorAll("area[userdata=\"" + data + "\"]");

	      for (var i = 0; i < nodes.length; i++) {
	        var nod = nodes[i];

	        if (mode) {
	          if (nod.getAttribute("coords")) {
	            nod.coordsdis = nod.getAttribute("coords");
	            nod.setAttribute("coords", "");
	            nod.coords = "";
	          }
	        } else if (!mode) {
	          if (nod.coordsdis) {
	            nod.setAttribute("coords", nod.coordsdis);
	            nod.coords = nod.coordsdis;
	            nod.coordsdis = "";
	          }
	        }

	        nodes[i].style.display = mode ? "none" : "";
	      }
	    }
	  },
	  render: function (obj) {
	    var d = create("DIV");
	    d.style.cssText = "position:absolute; width:100%; height:100%; top:0px; left:0px;";
	    obj.appendChild(d);
	    var src = env.isIE ? "" : "src='data:image/gif;base64,R0lGODlhEgASAIAAAP///////yH5BAUUAAEALAAAAAASABIAAAIPjI+py+0Po5y02ouz3pwXADs='";
	    d.innerHTML = "<map id='" + this._id + "' name='" + this._id + "'>" + this._map.join("\n") + "</map><img " + src + " class='webix_map_img' usemap='#" + this._id + "'>";
	    obj._htmlmap = d; //for clearing routine

	    this._map = [];
	  }
	});

	var IdSpace = {
	  $init: function () {
	    this._elements = {};
	    this._translate_ids = {};
	    this.getTopParentView = this._get_self = bind(function () {
	      return this;
	    }, this);

	    this._run_inner_init_logic();

	    this.$ready.push(this._run_after_inner_init_logic);
	  },
	  $$: function (id) {
	    return this._elements[id];
	  },
	  innerId: function (id) {
	    return this._translate_ids[id];
	  },
	  _run_inner_init_logic: function () {
	    this._prev_global_col = state._global_collection;
	    state._global_collection = this;
	  },
	  _run_after_inner_init_logic: function () {
	    for (var name in this._elements) {
	      var input = this._elements[name];
	      if (this.callEvent && input.mapEvent && !input._evs_map.onitemclick) input.mapEvent({
	        onitemclick: this
	      });
	      input.getTopParentView = this._get_self;
	    }

	    state._global_collection = this._prev_global_col;
	    this._prev_global_col = 0;
	  },
	  _destroy_child: function (id) {
	    delete this._elements[id];
	  },
	  ui: function () {
	    this._run_inner_init_logic();

	    var temp = ui.apply(this, arguments);

	    this._run_after_inner_init_logic();

	    return temp;
	  }
	};

	var KeysNavigation = {
	  $init: function () {
	    if (this.getSelectedId) this.attachEvent("onAfterRender", this._set_focusable_item);
	    if (this.moveSelection) this.attachEvent("onTabFocus", this._set_item_focus);
	  },
	  _set_item_focus: function () {
	    if (this.getSelectedId) {
	      var sel = this.getSelectedId(true);
	      if (!sel.length || !this.getItemNode(sel[0])) this.moveSelection("down"); //select and show
	    }
	  },
	  _set_focusable_item: function () {
	    var sel = this.getSelectedId(true);

	    if (!sel.length || !this.getItemNode(sel[0])) {
	      var node = this._dataobj.querySelector("[" + this._id + "]");

	      if (node) node.setAttribute("tabindex", "0");
	    }
	  },
	  _navigation_helper: function (mode) {
	    return function (view, e) {
	      var tag = e.srcElement || e.target; //ignore clipboard listener

	      if (!tag.getAttribute(
	      /*@attr*/
	      "webixignore")) {
	        //ignore hotkeys if focus in the common input
	        //to allow normal text edit operations
	        var name = tag.tagName;
	        if (name == "INPUT" || name == "TEXTAREA" || name == "SELECT") return true;
	      }

	      if (view && view.moveSelection && view.config.navigation && !view._in_edit_mode) {
	        preventEvent(e);
	        return view.moveSelection(mode, {
	          shift: e.shiftKey,
	          ctrl: e.ctrlKey
	        });
	      }

	      return true;
	    };
	  },
	  moveSelection: function (mode, details, focus) {
	    var config = this._settings;
	    if (config.disabled) return; //get existing selection

	    var selected = this.getSelectedId(true);
	    var x_layout = this.count && (config.layout == "x" || config.xCount > 1);

	    if ((mode == "right" || mode == "left") && this._parent_menu) {
	      var parent = $$(this._parent_menu);

	      parent._hide_sub_menu(true);

	      if (parent.config.layout === "x") parent.moveSelection(mode);else UIManager.setFocus(parent);
	      return;
	    }

	    if (!selected.length && this.count()) {
	      if (mode == "down" || mode == "right" && x_layout) mode = "top";else if (mode == "up" || mode == "left" && x_layout) mode = "bottom";else return;
	      selected = [this.getFirstId()];
	    }

	    if (selected.length == 1) {
	      //if we have a selection
	      selected = selected[0];
	      var prev = selected;
	      if (mode == "left" && this.close) return this.close(selected);
	      if (mode == "right" && this.open) return this.open(selected);else if (mode == "top") {
	        selected = this.getFirstId();
	      } else if (mode == "bottom") {
	        selected = this.getLastId();
	      } else if (mode == "up" || mode == "left" || mode == "pgup") {
	        var index$$1 = this.getIndexById(selected);
	        var step = mode == "pgup" ? 10 : 1;
	        selected = this.getIdByIndex(Math.max(0, index$$1 - step));
	      } else if (mode == "down" || mode == "right" || mode == "pgdown") {
	        var _index = this.getIndexById(selected);

	        var _step = mode == "pgdown" ? 10 : 1;

	        selected = this.getIdByIndex(Math.min(this.count() - 1, _index + _step));
	      } else {
	        assert(false, "Not supported selection moving mode");
	        return;
	      }
	      if (this._skip_item) selected = this._skip_item(selected, prev, mode);
	      this.showItem(selected);
	      this.select(selected);
	      if (this.getSubMenu && this.getSubMenu(selected)) this._mouse_move_activation(selected, this.getItemNode(selected));

	      if (!this.config.clipboard && focus !== false) {
	        var node = this.getItemNode(selected);
	        if (node) node.focus();
	      }
	    }

	    return false;
	  },
	  navigation_setter: function (value) {
	    //using global flag to apply hotkey only once
	    if (value && !UIManager._global_nav_grid_hotkeys) {
	      UIManager._global_nav_grid_hotkeys = true; //hotkeys will react on any component but will not work in edit mode
	      //you can define moveSelection method to handle navigation keys

	      UIManager.addHotKey("up", this._navigation_helper("up"));
	      UIManager.addHotKey("down", this._navigation_helper("down"));
	      UIManager.addHotKey("right", this._navigation_helper("right"));
	      UIManager.addHotKey("left", this._navigation_helper("left"));
	      UIManager.addHotKey("shift+up", this._navigation_helper("up"));
	      UIManager.addHotKey("shift+down", this._navigation_helper("down"));
	      UIManager.addHotKey("shift+right", this._navigation_helper("right"));
	      UIManager.addHotKey("shift+left", this._navigation_helper("left"));
	      UIManager.addHotKey("ctrl+shift+up", this._navigation_helper("up"));
	      UIManager.addHotKey("ctrl+shift+down", this._navigation_helper("down"));
	      UIManager.addHotKey("ctrl+shift+right", this._navigation_helper("right"));
	      UIManager.addHotKey("ctrl+shift+left", this._navigation_helper("left"));
	      UIManager.addHotKey("pageup", this._navigation_helper("pgup"));
	      UIManager.addHotKey("pagedown", this._navigation_helper("pgdown"));
	      UIManager.addHotKey("home", this._navigation_helper("top"));
	      UIManager.addHotKey("end", this._navigation_helper("bottom"));
	    }

	    return value;
	  }
	};

	/*Data collection mapping logic */

	var MapCollection = {
	  $init: function () {
	    this.$ready.push(this._create_scheme_init);
	    this.attachEvent("onStructureUpdate", this._create_scheme_init);
	    this.attachEvent("onStructureLoad", function () {
	      if (!this._scheme_init_order.length) this._create_scheme_init();
	    });
	  },
	  _create_scheme_init: function () {
	    var stack = this._scheme_init_order = [];
	    var config = this._settings;
	    if (config.columns) this._build_data_map(config.columns);
	    if (this._settings.map) this._process_field_map(config.map);

	    if (stack.length) {
	      this.data._scheme_init = function (obj) {
	        for (var i = 0; i < stack.length; i++) {
	          stack[i](obj);
	        }
	      };
	    }
	  },
	  _process_field_map: function (map) {
	    for (var key in map) {
	      this._scheme_init_order.push(this._process_single_map(key, map[key]));
	    }
	  },
	  _process_single_map: function (target, map, extra) {
	    var source = map.replace(/^(\s|)\((date|number)\)/, "");
	    var getSource;

	    if (source === "") {
	      getSource = function (a) {
	        return a[target];
	      };
	    } else {
	      if (source.indexOf("#") === -1 && source.indexOf("{") === -1) {
	        source = "#" + source + "#";
	      }

	      getSource = template(source);
	    }

	    if (map.indexOf("(date)") === 0) {
	      if (extra && !extra.format) extra.format = i18n.dateFormatStr;
	      return function (obj) {
	        var dateStr = (getSource(obj) || "").toString();
	        obj[target] = i18n.parseFormatDate(dateStr);
	      };
	    } else if (map.indexOf("(number)") === 0) {
	      return function (obj) {
	        obj[target] = getSource(obj) * 1;
	      };
	    } else {
	      return function (obj) {
	        obj[target] = getSource(obj) || "";
	      };
	    }
	  },
	  _build_data_map: function (columns) {
	    //for datatable
	    for (var i = 0; i < columns.length; i++) {
	      var map = columns[i].map;
	      var id = columns[i].id;

	      if (!id) {
	        id = columns[i].id = "i" + uid();
	        if (!columns[i].header) columns[i].header = "";
	      }

	      if (map) this._scheme_init_order.push(this._process_single_map(id, map, columns[i]));

	      this._map_options(columns[i]);
	    }
	  },
	  _create_collection: function (options) {
	    if (typeof options === "string") {
	      var options_view = $$(options); //id of some other view

	      if (!options_view) {
	        //or url
	        options = new (use("DataCollection"))({
	          url: options
	        });

	        this._destroy_with_me.push(options);
	      } else options = options_view;

	      if (options.getBody) //if it was a view, special check for suggests
	        options = options_view.getBody();
	    } else if (typeof options === "function" || options.$proxy) {
	      //proxy or function
	      options = new (use("DataCollection"))({
	        url: options
	      });

	      this._destroy_with_me.push(options);
	    } else if (!options.loadNext) {
	      var array = isArray(options);
	      var data = [];

	      if (array && _typeof(options[0]) !== "object") {
	        //["one", "two"]
	        for (var i = 0; i < options.length; i++) {
	          data.push({
	            id: options[i],
	            value: options[i]
	          });
	        }

	        options = data;
	      } else if (!array) {
	        //{ 1:"one", 2:"two" }
	        for (var _i in options) {
	          data.push({
	            id: _i,
	            value: options[_i]
	          });
	        }

	        options = data;
	      } // else [{ id:1, value:"one"}, ...]


	      options = new (use("DataCollection"))({
	        data: options
	      });

	      this._destroy_with_me.push(options);
	    } // else data collection or view


	    return options;
	  },
	  _map_options: function (column) {
	    var options = column.options || column.collection;

	    if (options) {
	      options = this._create_collection(options);

	      this._bind_collection(options, column);
	    }

	    if (column.header) {
	      this._map_header_options(column.header);

	      this._map_header_options(column.footer);
	    }
	  },
	  _map_header_options: function (arr) {
	    var _this = this;

	    arr = arr || [];

	    var _loop = function (i) {
	      var config = arr[i];

	      if (config && config.options) {
	        var options = config.options;
	        if (!options.loadNext) options = config.options = _this._create_collection(options);
	        var id = options.data.attachEvent("onStoreUpdated", function () {
	          if (_this.refreshFilter) _this.refreshFilter(config.columnId);
	        });

	        _this.attachEvent("onDestruct", function () {
	          if (!options.$destructed) options.data.detachEvent(id);
	        });
	      }
	    };

	    for (var i = 0; i < arr.length; i++) {
	      _loop(i);
	    }
	  },
	  _bind_collection: function (options, column) {
	    var _this2 = this;

	    if (column) {
	      delete column.options;
	      column.collection = options;
	      column.template = column.template || this._bind_template(options, column.id, column.optionslist);
	      var id = options.data.attachEvent("onStoreUpdated", function () {
	        _this2.refresh();

	        if (_this2.refreshFilter) _this2.refreshFilter(column.id);
	      });
	      this.attachEvent("onDestruct", function () {
	        if (!options.$destructed) options.data.detachEvent(id);
	      });
	    }
	  },
	  _bind_template: function (options, columnId, multi) {
	    columnId = this.getColumnConfig ? columnId : "value";

	    if (multi) {
	      var separator = typeof multi == "string" ? multi : ",";
	      return function (obj) {
	        var value = obj[columnId];
	        if (!value) return "";
	        var ids = value.toString().split(separator);

	        for (var i = 0; i < ids.length; i++) {
	          var data = options.data.pull[ids[i]];
	          ids[i] = data ? data.value || "" : "";
	        }

	        return ids.join(", ");
	      };
	    } else {
	      return function (obj) {
	        var data = options.data.pull[obj[columnId]];
	        if (data && (data.value || data.value === 0)) return data.value;
	        return "";
	      };
	    }
	  }
	};

	var MouseEvents = {
	  $init: function (config) {
	    config = config || {};
	    this._clickstamp = 0;
	    this._dbl_sensetive = 300;
	    this._item_clicked = null;

	    this._mouse_action_extend(config.onClick, "on_click");

	    this._mouse_action_extend(config.onContext, "on_context");

	    this._mouse_action_extend(config.onDblClick, "on_dblclick");

	    this._mouse_action_extend(config.onMouseMove, "on_mouse_move"); //attach dom events if related collection is defined


	    if (this.on_click) {
	      _event(this._contentobj, "click", this._onClick, {
	        bind: this
	      });

	      if (env.isIE8 && this.on_dblclick) _event(this._contentobj, "dblclick", this._onDblClick, {
	        bind: this
	      });
	    }

	    if (this.on_context) _event(this._contentobj, "contextmenu", this._onContext, {
	      bind: this
	    });
	    if (this.on_mouse_move) this._enable_mouse_move();
	  },
	  _enable_mouse_move: function () {
	    if (!this._mouse_move_enabled) {
	      this.on_mouse_move = this.on_mouse_move || {};

	      _event(this._contentobj, "mousemove", this._onMouse, {
	        bind: this
	      });

	      _event(this._contentobj, env.isIE ? "mouseleave" : "mouseout", this._onMouse, {
	        bind: this
	      });

	      this._mouse_move_enabled = 1;
	      this.attachEvent("onDestruct", function () {
	        if (this._mouse_move_timer) window.clearTimeout(this._mouse_move_timer);
	      });
	    }
	  },
	  _mouse_action_extend: function (config, key) {
	    if (config) {
	      var now = this[key];
	      var step = now ? exports.extend({}, now) : {};
	      this[key] = exports.extend(step, config);
	    }
	  },
	  //inner onclick object handler
	  _onClick: function (e) {
	    if (!this.isEnabled()) return false;

	    UIManager._focus_action(this);

	    if (this.on_dblclick) {
	      // emulates double click
	      var stamp = new Date().valueOf();

	      if (stamp - this._clickstamp <= this._dbl_sensetive && this.locate) {
	        var item = this.locate(e);

	        if ("" + item == "" + this._item_clicked) {
	          this._clickstamp = 0;
	          return this._onDblClick(e);
	        }
	      }

	      this._clickstamp = stamp;
	    }

	    var result = this._mouseEvent(e, this.on_click, "ItemClick");

	    return result;
	  },
	  //inner ondblclick object handler
	  _onDblClick: function (e) {
	    return this._mouseEvent(e, this.on_dblclick, "ItemDblClick");
	  },
	  //process oncontextmenu events
	  _onContext: function (e) {
	    this._mouseEvent(e, this.on_context, "BeforeContextMenu", "AfterContextMenu");
	  },

	  /*
	  	event throttler - ignore events which occurs too fast
	  	during mouse moving there are a lot of event firing - we need no so much
	  	also, mouseout can fire when moving inside the same html container - we need to ignore such fake calls
	  */
	  _onMouse: function (e) {
	    if (this.$destructed) return;
	    if (document.createEventObject) //make a copy of event, will be used in timed call
	      e = document.createEventObject(event);else if (!isUndefined(e.movementY) && !e.movementY && !e.movementX) return; //logitech mouse driver can send false signals in Chrome

	    if (this._mouse_move_timer) //clear old event timer
	      window.clearTimeout(this._mouse_move_timer); //this event just inform about moving operation, we don't care about details

	    this.callEvent("onMouseMoving", [e]); //set new event timer

	    this._mouse_move_timer = window.setTimeout(bind(function () {
	      //called only when we have at least 100ms after previous event
	      if (e.type == "mousemove") this._onMouseMove(e);else this._onMouseOut(e);
	    }, this), this._settings.mouseEventDelay || 500);
	  },
	  //inner mousemove object handler
	  _onMouseMove: function (e) {
	    if (!this._mouseEvent(e, this.on_mouse_move, "MouseMove")) this.callEvent("onMouseOut", [e || event]);
	  },
	  //inner mouseout object handler
	  _onMouseOut: function (e) {
	    this.callEvent("onMouseOut", [e || event]);
	  },
	  //common logic for click and dbl-click processing
	  _mouseEvent: function (e, hash, name, pair) {
	    e = e || event;
	    if (e.processed || !this._viewobj) return;
	    e.processed = true;
	    var trg = e.target || e.srcElement; //IE8 can't modify event object
	    //so we need to stop event bubbling to prevent double processing

	    if (env.isIE8) {
	      var vid = this._settings.id;
	      var wid = trg.w_view;
	      if (!wid) trg.w_view = vid;else if (wid !== vid) return;
	    }

	    var css = "";
	    var id = null;
	    var found = false; //loop through all parents
	    //we need to check for this._viewobj as some handler can destroy the view

	    while (trg && trg.parentNode && this._viewobj && trg != this._viewobj.parentNode) {
	      if (!found && trg.getAttribute) {
	        //if element with ID mark is not detected yet
	        id = trg.getAttribute(this._id); //check id of current one

	        if (id) {
	          this._item_clicked = id;

	          if (this.callEvent) {
	            //it will be triggered only for first detected ID, in case of nested elements
	            if (!this.callEvent("on" + name, [id, e, trg])) return;
	            if (pair) this.callEvent("on" + pair, [id, e, trg]);
	          } //set found flag


	          found = true;
	        }
	      }

	      css = _getClassName(trg);

	      if (css) {
	        //check if pre-defined reaction for element's css name exists
	        css = css.toString().split(" ");

	        for (var i = 0; i < css.length; i++) {
	          if (hash[css[i]]) {
	            var functor = toFunctor(hash[css[i]], this.$scope);
	            var res = functor.call(this, e, id || locate(e, this._id), trg);
	            if (res === false) return found;
	          }
	        }
	      }

	      trg = trg.parentNode;
	    }

	    return found; //returns true if item was located and event was triggered
	  }
	};

	/*
	    UI: navigation control
	*/

	var NavigationButtons = {
	  $init: function () {
	    this.$ready.push(function () {
	      this.attachEvent("onKeyPress", this._onKeyPress);
	    });
	  },
	  _moveActive: function (code, e) {
	    if (code === 37 || code === 39) {
	      preventEvent(e);

	      this._showNavItem(code === 37 ? -1 : 1);

	      var node = this._navPanel.querySelector("[tabindex='0']");

	      if (node) node.focus();
	    }
	  },
	  _renderPanel: function () {
	    remove(this._navPanel);
	    this._navPanel = create("DIV", {
	      "class": "webix_nav_panel " + "webix_nav_panel_" + this._settings.navigation.type,
	      "role": "tablist"
	    }, "");

	    this._viewobj.appendChild(this._navPanel);

	    this._renderNavItems();

	    this._renderNavButtons();

	    this._setLinkEventHandler();
	  },
	  _setLinkEventHandler: function () {
	    var h = [];
	    if (this._navPanel) h[0] = event$1(this._navPanel, "click", bind(function (e) {
	      var elem = e.srcElement || e.target;
	      var found = false;

	      while (elem != this._navPanel && !found) {
	        var bindId = elem.getAttribute(this._linkAttr);

	        if (bindId) {
	          found = true;

	          this._showPanelBind(bindId);
	        }

	        elem = elem.parentNode;
	      }
	    }, this));
	    if (this._prevNavButton) h[1] = event$1(this._prevNavButton, "click", bind(function () {
	      this._showNavItem(-1);
	    }, this));
	    if (this._nextNavButton) h[1] = event$1(this._nextNavButton, "click", bind(function () {
	      this._showNavItem(1);
	    }, this));
	    this.attachEvent("onDestruct", function () {
	      for (var i = 0; i < h.length; i++) {
	        this.detachEvent(h[i]);
	      }

	      h = null;
	    });
	  },
	  _showNavItem: function (inc) {
	    if (this._cells) {
	      var index$$1 = this._active_cell + inc;

	      if (index$$1 >= this._cells.length || index$$1 < 0) {
	        index$$1 = index$$1 < 0 ? this._cells.length - 1 : 0;
	      }

	      this.setActiveIndex(index$$1);
	    }
	  },
	  _showPanelBind: function (id) {
	    if (this._cells) $$(id).show();
	  },
	  _renderNavItems: function () {
	    var item, config;
	    config = this._settings.navigation;

	    if (config.items) {
	      this._linkAttr = config.linkAttr ||
	      /*@attr*/
	      "bind_id";
	      if (!this._navPanel) this._renderPanel();else this._clearPanel();
	      var data = this._cells ? this._cells : this.data.order;

	      if (data.length > 1) {
	        for (var i = 0; i < data.length; i++) {
	          item = create("DIV", {
	            "class": "webix_nav_item webix_nav_" + (i == this._active_cell ? "active" : "inactive"),
	            "role": "tab",
	            "tabindex": i == this._active_cell ? "0" : "-1"
	          });
	          var id = this._cells ? this._cells[i]._settings.id : data[i];
	          if (id) item.setAttribute(this._linkAttr, id);

	          this._navPanel.appendChild(item);
	        }
	      }
	    }
	  },
	  _clearPanel: function () {
	    if (this._navPanel) {
	      var coll = this._navPanel.childNodes;

	      for (var i = coll.length - 1; i >= 0; i--) {
	        remove(coll[i]);
	      }
	    }
	  },
	  _renderNavButtons: function () {
	    var config = this._settings.navigation;

	    if (config.buttons) {
	      if (this._prevNavButton) remove(this._prevNavButton);
	      if (this._prevNavButton) remove(this._nextNavButton);
	      this._prevNavButton = create("DIV", {
	        "class": "webix_nav_button_" + config.type + " webix_nav_button_prev "
	      }, "<div role=\"button\" tabindex=\"0\" aria-label=\"" + i18n.aria.prevTab + "\" class=\"webix_nav_button_inner\"></div>");

	      this._viewobj.appendChild(this._prevNavButton);

	      this._nextNavButton = create("DIV", {
	        "class": "webix_nav_button_" + config.type + " webix_nav_button_next "
	      }, "<div role=\"button\" tabindex=\"0\" aria-label=\"" + i18n.aria.prevTab + "\" class=\"webix_nav_button_inner\"></div>");

	      this._viewobj.appendChild(this._nextNavButton);
	    }
	  }
	};

	var OverlayBox = {
	  showOverlay: function (message) {
	    if (!this._overlay) {
	      this._overlay = create("DIV", {
	        "class": "webix_overlay"
	      }, message || "");
	      insertBefore(this._overlay, this._viewobj.firstChild, this._viewobj);
	      this._viewobj.style.position = "relative";
	    } else this._overlay.innerHTML = message;
	  },
	  hideOverlay: function () {
	    if (this._overlay) {
	      remove(this._overlay);
	      this._overlay = null;
	    }
	  }
	};

	var PagingAbility = {
	  pager_setter: function (pager) {
	    if (typeof pager == "string") {
	      var ui_pager = $$(pager);

	      if (!ui_pager) {
	        this.$blockRender = true;
	        delay(function () {
	          var obj = $$(pager);
	          this._settings.pager = this.pager_setter(obj);
	          var s = obj._settings;
	          s.count = this.data._count_pager_total(s.level);
	          obj.refresh();
	          this.$blockRender = false;
	          this.render();
	        }, this);
	        return null;
	      }

	      pager = ui_pager;
	    }

	    function check_pager_sizes(repeat) {
	      if (pager.config.autosize && this.getVisibleCount) {
	        var count = this.getVisibleCount();

	        if (isNaN(count)) {
	          pager.config.size = 1;
	          delay(check_pager_sizes, this, [true]);
	        } else if (count != pager.config.size) {
	          pager.config.size = count;
	          pager.refresh();
	          if (repeat === true) this.refresh();
	        }
	      }

	      var s = this._settings.pager; //initial value of pager = -1, waiting for real value

	      if (s.page == -1) return false;
	      this.data.$min = this._count_pager_index(0, s.page * s.size); //affect data.getRange

	      this.data.$max = this._count_pager_index(this.data.$min, s.size);
	      this.data.$pagesize = this.data.$max - this.data.$min;
	      return true;
	    }

	    this.attachEvent("onBeforeRender", check_pager_sizes);

	    if (!pager.$view) {
	      pager.view = "pager";
	      pager = ui(pager);
	    }

	    this._pager = pager;
	    pager.$master = this;
	    this.data.attachEvent("onStoreUpdated", function () {
	      var s = pager._settings;
	      s.count = this._count_pager_total(s.level);
	      pager.refresh();
	    });
	    this.data._count_pager_total = this._count_pager_total;
	    return pager._settings;
	  },
	  _count_pager_total: function (level) {
	    if (level && level !== 0) {
	      var count = 0;
	      this.each(function (obj) {
	        if (obj.$level == level) count++;
	      });
	      return count;
	    } else return this.count();
	  },
	  _count_pager_index: function (start, count) {
	    var s = this._settings.pager;

	    if (s.level && s.level !== 0) {
	      var end = start;
	      var max = this.data.order.length;
	      if (count) while (end < max) {
	        if (this.data.order[end] && this.data.getItem(this.data.order[end]).$level == s.level) {
	          if (count === 0) break;else count--;
	        }

	        end++;
	      }
	      return end;
	    } else return start + count;
	  },
	  setPage: function (value) {
	    if (this._pager) this._pager.select(value);
	  },
	  getPage: function () {
	    return this._pager._settings.page;
	  },
	  getPager: function () {
	    return this._pager;
	  }
	};

	var ProgressBar = {
	  $init: function () {
	    var _this = this;

	    if (isUndefined(this._progress) && this.attachEvent) {
	      this.attachEvent("onBeforeLoad", function () {
	        return _this.showProgress();
	      });
	      this.attachEvent("onAfterLoad", function () {
	        return _this.hideProgress();
	      });
	      this._progress = null;
	    }
	  },
	  showProgress: function (config) {
	    // { position: 0 - 1, delay: 2000ms by default, css : name of css class to use }
	    var width;

	    if (!this._progress) {
	      config = exports.extend({
	        position: 0,
	        delay: 2000,
	        type: "icon",
	        icon: "wxi-sync",
	        hide: false
	      }, config || {}, true);
	      var incss = config.type == "icon" ? config.icon + " webix_spin" : "";
	      this._progress = create("DIV", {
	        "class": "webix_progress_" + config.type,
	        "role": "progressbar",
	        "aria-valuemin": "0",
	        "aria-valuemax": "100",
	        "tabindex": "0"
	      }, "<div class='webix_progress_state " + incss + "'></div>");
	      if (!this.setPosition) this._viewobj.style.position = "relative";
	      insertBefore(this._progress, this._viewobj.firstChild, this._viewobj);

	      this._viewobj.setAttribute("aria-busy", "true");

	      if (!Touch.$active) {
	        if (this.getScrollState) {
	          var scroll = this.getScrollState();

	          if (this._viewobj.scrollWidth != this.$width) {
	            this._progress.style.left = scroll.x + "px";
	          }

	          if (this._viewobj.scrollHeight != this.$height) {
	            if (config.type != "bottom") {
	              this._progress.style.top = scroll.y + "px";
	            } else {
	              this._progress.style.top = scroll.y + this.$height - this._progress.offsetHeight + "px";
	            }
	          }
	        }
	      }

	      this._progress_animate = config.type != "icon";
	    }

	    if (!config) return;

	    if (this._progress_animate) {
	      var position = config.position || 1; //check for css-transition support

	      if (this._progress.style[env.transitionDuration] !== undefined || !config.delay) {
	        if (config.delay) {
	          // force reflow
	          width = this._viewobj.firstChild.offsetWidth;
	          this._progress.firstChild.style[env.transitionDuration] = config.delay + "ms";
	        } // animate to new value


	        this._progress.firstChild.style.width = position * 100 + "%";
	      } else {
	        //if animation is not supported fallback to timeouts [IE9]
	        var count = 0,
	            start = 0,
	            step = position / config.delay * 30,
	            view = this;

	        if (this._progressTimer) {
	          //reset the existing progress
	          window.clearInterval(this._progressTimer);
	          start = this._progress.firstChild.offsetWidth / this._progress.offsetWidth * 100;
	        }

	        this._progressTimer = window.setInterval(function () {
	          if (count * 30 == config.delay) {
	            window.clearInterval(view._progressTimer);
	          } else {
	            if (view._progress && view._progress.firstChild) view._progress.firstChild.style.width = start + count * step * position * 100 + "%";
	            count++;
	          }
	        }, 30);
	      }
	    }

	    if (this._progress_hide) clearTimeout(this._progress_hide);
	    if (config.hide) this._progress_hide = delay(this.hideProgress, this, [1], config.delay); // necessary to prevent code optimization

	    return width;
	  },
	  hideProgress: function (now) {
	    if (this._progress) {
	      if (now || !this._progress_animate) {
	        if (this._progressTimer) window.clearInterval(this._progressTimer);
	        remove(this._progress);
	        this._progress = null;

	        this._viewobj.removeAttribute("aria-busy");
	      } else {
	        this.showProgress({
	          position: 1.1,
	          delay: 300,
	          hide: true
	        });
	      }
	    }
	  }
	};

	var RenderStack = {
	  $init: function () {
	    assert(this.data, "RenderStack :: Component doesn't have DataStore");
	    assert(template, "template :: template is not accessible"); //used for temporary HTML elements
	    //automatically nulified during destruction

	    this._html = document.createElement("DIV");
	    this.data.attachEvent("onIdChange", bind(this._render_change_id, this));
	    this.attachEvent("onItemClick", this._call_onclick); //create copy of default type, and set it as active one

	    if (!this.types) {
	      this.types = {
	        "default": this.type
	      };
	      this.type.name = "default";
	    }

	    this.type = clone(this.type);
	  },
	  customize: function (obj) {
	    type(this, obj);
	  },
	  item_setter: function (value) {
	    return this.type_setter(value);
	  },
	  type_setter: function (value) {
	    if (!this.types[value]) this.customize(value);else {
	      this.type = clone(this.types[value]);
	      if (this.type.css) this._contentobj.className += " " + this.type.css;
	    }
	    if (this.type.on_click) exports.extend(this.on_click, this.type.on_click);
	    return value;
	  },
	  template_setter: function (value) {
	    this.type.template = template(value);
	  },
	  //convert single item to HTML text (templating)
	  _toHTML: function (obj) {
	    var mark = this.data._marks[obj.id]; //check if related template exist

	    assert(!obj.$template || this.type["template" + obj.$template], "RenderStack :: Unknown template: " + obj.$template);
	    this.callEvent("onItemRender", [obj]);
	    return this.type.templateStart(obj, this.type, mark) + (obj.$template ? this.type["template" + obj.$template] : this.type.template)(obj, this.type, mark) + this.type.templateEnd(obj, this.type, mark);
	  },
	  //convert item to HTML object (templating)
	  _toHTMLObject: function (obj) {
	    this._html.innerHTML = this._toHTML(obj);
	    return this._html.firstChild;
	  },
	  _render_change_id: function (old, newid) {
	    var obj = this.getItemNode(old);

	    if (obj) {
	      obj.setAttribute(this._id, newid);
	      this._htmlmap[newid] = this._htmlmap[old];
	      delete this._htmlmap[old];
	    }
	  },
	  //calls function that is set in onclick property
	  _call_onclick: function () {
	    if (this._settings.click) {
	      var code = toFunctor(this._settings.click, this.$scope);
	      if (code && code.call) code.apply(this, arguments);
	    }
	  },
	  //return html container by its ID
	  //can return undefined if container doesn't exists
	  getItemNode: function (search_id) {
	    if (this._htmlmap) return this._htmlmap[search_id]; //fill map if it doesn't created yet

	    this._htmlmap = {};
	    var t = this._dataobj.childNodes;

	    for (var i = 0; i < t.length; i++) {
	      var id = t[i].getAttribute(this._id); //get item's

	      if (id) this._htmlmap[id] = t[i];
	    } //call locator again, when map is filled


	    return this.getItemNode(search_id);
	  },
	  //return id of item from html event
	  locate: function (e) {
	    return locate(e, this._id);
	  },

	  /*change scrolling state of top level container, so related item will be in visible part*/
	  showItem: function (id) {
	    var html = this.getItemNode(id);

	    if (html && this.scrollTo) {
	      var txmin = html.offsetLeft;
	      var txmax = txmin + html.offsetWidth;
	      var tymin = html.offsetTop;
	      var tymax = tymin + html.offsetHeight;
	      var state = this.getScrollState();
	      var x = state.x;
	      if (x > txmin || x + this._content_width < txmax) x = txmin;
	      var y = state.y;
	      if (y > tymin || y + this._content_height < tymax) y = tymin;
	      this.scrollTo(x, y);
	      if (this._setItemActive) this._setItemActive(id);
	    }
	  },
	  //update view after data update
	  //method calls low-level rendering for related items
	  //when called without parameters - all view refreshed
	  render: function (id, data, type$$1) {
	    if (!this.isVisible(this._settings.id) || this.$blockRender) return;

	    if (id) {
	      var cont = this.getItemNode(id); //get html element of updated item

	      switch (type$$1) {
	        case "paint":
	        case "update":
	          //in case of update - replace existing html with updated one
	          if (!cont) return;

	          var t1 = this._htmlmap[id] = this._toHTMLObject(data);

	          insertBefore(t1, cont);
	          remove(cont);
	          break;

	        case "delete":
	          //in case of delete - remove related html
	          if (!cont) return;
	          remove(cont);
	          delete this._htmlmap[id];
	          break;

	        case "add":
	          //in case of add - put new html at necessary position
	          var t2 = this._htmlmap[id] = this._toHTMLObject(data);

	          insertBefore(t2, this.getItemNode(this.data.getNextId(id)), this._dataobj);
	          break;

	        case "move":
	          //moving without repainting the item
	          insertBefore(this.getItemNode(id), this.getItemNode(this.data.getNextId(id)), this._dataobj);
	          break;

	        default:
	          assert(0, "Unknown render command: " + type$$1);
	          break;
	      }
	    } else {
	      //full reset
	      if (this.callEvent("onBeforeRender", [this.data])) {
	        //getRange - returns all elements
	        (this._renderobj || this._dataobj).innerHTML = this.data.getRange().map(this._toHTML, this).join("");
	        this._htmlmap = null; //clear map, it will be filled at first getItemNode

	        this.callEvent("onAfterRender", []);
	      }
	    }
	  }
	};

	var Scrollable = {
	  $init: function (config) {
	    //do not spam unwanted scroll containers for templates 
	    if (config && !config.scroll && this._one_time_scroll) return this._dataobj = this._dataobj || this._contentobj;

	    (this._dataobj || this._contentobj).appendChild(create("DIV", {
	      "class": "webix_scroll_cont"
	    }, ""));

	    this._dataobj = (this._dataobj || this._contentobj).firstChild;
	    if (!env.touch || env.touch === "native") _event(this._viewobj, "scroll", bind(function () {
	      if (this.callEvent) delay(function () {
	        this.callEvent("onAfterScroll", []);
	      }, this);
	    }, this));
	  },

	  /*defaults:{
	  	scroll:true
	  },*/
	  scroll_setter: function (value) {
	    if (!value) return false;
	    var auto = value === "auto";
	    var marker = value == "x" ? "x" : value == "xy" ? "xy" : auto ? "xy" : "y";

	    if (Touch.$active && env.touch != "native") {
	      this._dataobj.setAttribute("touch_scroll", marker);

	      if (this.attachEvent) this.attachEvent("onAfterRender", bind(this._refresh_scroll, this));
	      this._touch_scroll = true;
	    } else {
	      if (env.$customScroll) {
	        temp$1.enable(this, marker);
	      } else {
	        var node = this._dataobj.parentNode.style;

	        if (auto) {
	          node.overflowX = node.overflowY = "auto";
	        } else {
	          if (marker.indexOf("x") != -1) {
	            this._scroll_x = true;
	            node.overflowX = "scroll";
	          }

	          if (marker.indexOf("y") != -1) {
	            this._scroll_y = true;
	            node.overflowY = "scroll";
	          }
	        }
	      }
	    }

	    return marker;
	  },
	  _onoff_scroll: function (mode, dir) {
	    if (!!this._settings.scroll == !!mode) return;

	    if (!env.$customScroll) {
	      var style = this._dataobj.parentNode.style;
	      style[dir === "x" ? "overflowX" : "overflowY"] = mode ? "auto" : "hidden";
	    }

	    if (dir === "x") {
	      this._scroll_x = mode;
	    } else {
	      this._scroll_y = mode;
	    }

	    this._settings.scroll = mode ? dir : false;
	  },
	  getScrollState: function () {
	    if (Touch.$active) {
	      var temp = Touch._get_matrix(this._dataobj);

	      return {
	        x: -temp.e,
	        y: -temp.f
	      };
	    } else return {
	      x: this._dataobj.parentNode.scrollLeft,
	      y: this._dataobj.parentNode.scrollTop
	    };
	  },
	  scrollTo: function (x, y) {
	    if (Touch.$active && env.touch != "native") {
	      y = Math.max(0, Math.min(y, this._dataobj.offsetHeight - this._content_height));
	      x = Math.max(0, Math.min(x, this._dataobj.offsetWidth - this._content_width));

	      Touch._set_matrix(this._dataobj, -x, -y, this._settings.scrollSpeed || "100ms");
	    } else {
	      this._dataobj.parentNode.scrollLeft = x;
	      this._dataobj.parentNode.scrollTop = y;
	    }
	  },
	  _refresh_scroll: function () {
	    if (this._settings.scroll.toString().indexOf("x") != -1) {
	      var x = this._dataobj.scrollWidth;

	      if (x) {
	        //in hidden state we will have a Zero scrollWidth
	        this._dataobj.style.width = "100%";
	        this._dataobj.style.width = this._dataobj.scrollWidth + "px";
	      }
	    }

	    if (Touch.$active && this._touch_scroll) {
	      Touch._clear_artefacts();

	      Touch._scroll_end();

	      var s = this.getScrollState();
	      var dx = this._dataobj.offsetWidth - this.$width - s.x;
	      var dy = this._dataobj.offsetHeight - this.$height - s.y; //if current scroll is outside of data area

	      if (dx < 0 || dy < 0) {
	        //scroll to the end of data area
	        var _x = dx < 0 ? Math.min(-dx - s.x, 0) : -s.x;

	        var y = dy < 0 ? Math.min(-dy - s.y, 0) : -s.y;

	        Touch._set_matrix(this._dataobj, _x, y, 0);
	      }
	    }
	  }
	};

	/*
		Behavior:SelectionModel - manage selection states
		@export
			select
			unselect
			selectAll
			unselectAll
			isSelected
			getSelectedId
	*/

	var SelectionModel = {
	  $init: function () {
	    //collection of selected IDs
	    this._selected = toArray();
	    assert(this.data, "SelectionModel :: Component doesn't have DataStore"); //remove selection from deleted items

	    this.data.attachEvent("onStoreUpdated", bind(this._data_updated, this));
	    this.data.attachEvent("onStoreLoad", bind(this._data_loaded, this));
	    this.data.attachEvent("onAfterFilter", bind(this._data_filtered, this));
	    this.data.attachEvent("onSyncApply", bind(this._select_check, this));
	    this.data.attachEvent("onIdChange", bind(this._id_changed, this));
	    this.$ready.push(this._set_noselect);
	  },
	  _set_noselect: function () {
	    if (this._settings.select == "multiselect" || this._settings.multiselect || this._settings.select == "area") _event(this.$view, "mousedown", function (e) {
	      var shiftKey = (e || event$1).shiftKey;

	      if (shiftKey) {
	        state._noselect_element = this;
	        addCss(this, "webix_noselect", 1);
	      }
	    });
	  },
	  _id_changed: function (oldid, newid) {
	    for (var i = this._selected.length - 1; i >= 0; i--) {
	      if (this._selected[i] == oldid) this._selected[i] = newid;
	    }
	  },
	  _data_filtered: function () {
	    for (var i = this._selected.length - 1; i >= 0; i--) {
	      if (this.data.getIndexById(this._selected[i]) < 0) {
	        var id = this._selected[i];
	        this.removeCss(id, "webix_selected", true);

	        this._selected.splice(i, 1);

	        this.callEvent("onSelectChange", [id]);
	      }
	    }
	  },
	  //helper - linked to onStoreUpdated
	  _data_updated: function (id, obj, type) {
	    if (type == "delete") {
	      //remove selection from deleted items
	      if (this.loadBranch) {
	        //hierarchy, need to check all
	        this._select_check();
	      } else this._selected.remove(id);
	    } else if (!id && !this.data.count() && !this.data._filter_order) {
	      //remove selection for clearAll
	      this._selected = toArray();
	    }
	  },
	  _data_loaded: function () {
	    if (this._settings.select) this.data.each(function (obj) {
	      if (obj && obj.$selected) this.select(obj.id);
	    }, this);
	  },
	  _select_check: function () {
	    for (var i = this._selected.length - 1; i >= 0; i--) {
	      if (!this.exists(this._selected[i])) this._selected.splice(i, 1);
	    }
	  },
	  //helper - changes state of selection for some item
	  _select_mark: function (id, state$$1, refresh, need_unselect) {
	    var sname = state$$1 ? "onBeforeSelect" : "onBeforeUnSelect";
	    if (!this.callEvent(sname, [id, state$$1])) return false;

	    if (need_unselect) {
	      this._silent_selection = true;
	      this.unselectAll();
	      this._silent_selection = false;
	    }

	    if (state$$1) this.addCss(id, "webix_selected", true);else this.removeCss(id, "webix_selected", true);
	    if (refresh) refresh.push(id); //if we in the mass-select mode - collect all changed IDs
	    else {
	        if (state$$1) this._selected.push(id); //then add to list of selected items
	        else this._selected.remove(id);

	        this._refresh_selection(id); //othervise trigger repainting

	      }
	    var ename = state$$1 ? "onAfterSelect" : "onAfterUnSelect";
	    this.callEvent(ename, [id]);
	    return true;
	  },
	  //select some item
	  select: function (id, preserve) {
	    var ctrlKey = arguments[2];
	    var shiftKey = arguments[3]; //if id not provide - works as selectAll

	    if (!id) return this.selectAll(); //allow an array of ids as parameter

	    if (isArray(id)) {
	      for (var i = 0; i < id.length; i++) {
	        this.select(id[i], i ? 1 : preserve, ctrlKey, shiftKey);
	      }

	      return;
	    }

	    assert(this.data.exists(id), "Incorrect id in select command: " + id); //block selection mode

	    if (shiftKey && this._selected.length) return this.selectAll(this._selected[this._selected.length - 1], id); //single selection mode

	    var need_unselect = false;
	    if (!ctrlKey && !preserve && (this._selected.length != 1 || this._selected[0] != id)) need_unselect = true;

	    if (!need_unselect && this.isSelected(id)) {
	      if (ctrlKey) this.unselect(id); //ctrl-selection of already selected item

	      return;
	    }

	    this._select_mark(id, true, null, need_unselect);
	  },
	  //unselect some item
	  unselect: function (id) {
	    //if id is not provided  - unselect all items
	    if (!id) return this.unselectAll();
	    if (!this.isSelected(id)) return;

	    this._select_mark(id, false);
	  },
	  //select all items, or all in defined range
	  selectAll: function (from, to) {
	    var range;
	    var refresh = [];
	    if (from || to) range = this.data.getRange(from || null, to || null); //get limited set if bounds defined
	    else range = this.data.getRange(); //get all items in other case
	    //in case of paging - it will be current page only

	    range.each(function (obj) {
	      if (!this.data.getMark(obj.id, "webix_selected")) {
	        this._selected.push(obj.id);

	        this._select_mark(obj.id, true, refresh);
	      }
	    }, this); //repaint self

	    this._refresh_selection(refresh);
	  },
	  //remove selection from all items
	  unselectAll: function () {
	    var refresh = [];

	    this._selected.each(function (id) {
	      this._select_mark(id, false, refresh); //unmark selected only

	    }, this);

	    this._selected = toArray();

	    this._refresh_selection(refresh); //repaint self

	  },
	  //returns true if item is selected
	  isSelected: function (id) {
	    return this._selected.find(id) != -1;
	  },

	  /*
	  	returns ID of selected items or array of IDs
	  	to make result predictable - as_array can be used, 
	  		with such flag command will always return an array 
	  		empty array in case when no item was selected
	  */
	  getSelectedId: function (as_array) {
	    switch (this._selected.length) {
	      case 0:
	        return as_array ? [] : "";

	      case 1:
	        return as_array ? [this._selected[0]] : this._selected[0];

	      default:
	        return [].concat(this._selected);
	      //isolation
	    }
	  },
	  getSelectedItem: function (as_array) {
	    var sel = this.getSelectedId(true);

	    if (sel.length > 1 || as_array) {
	      for (var i = sel.length - 1; i >= 0; i--) {
	        sel[i] = this.getItem(sel[i]);
	      }

	      return sel;
	    } else if (sel.length) return this.getItem(sel[0]);
	  },
	  //detects which repainting mode need to be used
	  _is_mass_selection: function (obj) {
	    // crappy heuristic, but will do the job
	    return obj.length > 100 || obj.length > this.data.count / 2;
	  },
	  _refresh_selection: function (refresh) {
	    if (_typeof(refresh) != "object") refresh = [refresh];
	    if (!refresh.length) return; //nothing to repaint

	    if (this._is_mass_selection(refresh)) this.data.refresh(); //many items was selected - repaint whole view
	    else for (var i = 0; i < refresh.length; i++) {
	        //repaint only selected
	        this.render(refresh[i], this.data.getItem(refresh[i]), "update");
	      }
	    if (!this._silent_selection) this.callEvent("onSelectChange", [refresh]);
	  }
	};
	ready(function () {
	  event$1(document.body, "mouseup", function () {
	    if (state._noselect_element) {
	      removeCss(state._noselect_element, "webix_noselect");
	      state._noselect_element = null;
	    }
	  });
	});

	var color = {
	  _toHex: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"],
	  toHex: function (number, length) {
	    number = parseInt(number, 10);
	    var str = "";

	    while (number > 0) {
	      str = this._toHex[number % 16] + str;
	      number = Math.floor(number / 16);
	    }

	    while (str.length < length) {
	      str = "0" + str;
	    }

	    return str;
	  },
	  rgbToHex: function (rgb) {
	    var arr = [];
	    if (typeof rgb === "string") rgb.replace(/[\d+.]+/g, function (v) {
	      arr.push(parseFloat(v));
	    });else if (isArray(rgb)) arr = rgb; //transparent

	    if (arr[3] === 0) return "";
	    return arr.slice(0, 3).map(function (n) {
	      return color.toHex(Math.floor(n), 2);
	    }).join("");
	  },
	  hexToDec: function (hex) {
	    return parseInt(hex, 16);
	  },
	  toRgb: function (rgb) {
	    var r, g, b, rgbArr;

	    if (typeof rgb != "string") {
	      r = rgb[0];
	      g = rgb[1];
	      b = rgb[2];
	    } else if (rgb.indexOf("rgb") != -1) {
	      rgbArr = rgb.substr(rgb.indexOf("(") + 1, rgb.lastIndexOf(")") - rgb.indexOf("(") - 1).split(",");
	      r = rgbArr[0];
	      g = rgbArr[1];
	      b = rgbArr[2];
	    } else {
	      if (rgb.substr(0, 1) == "#") {
	        rgb = rgb.substr(1);
	      }

	      r = this.hexToDec(rgb.substr(0, 2));
	      g = this.hexToDec(rgb.substr(2, 2));
	      b = this.hexToDec(rgb.substr(4, 2));
	    }

	    r = parseInt(r, 10) || 0;
	    g = parseInt(g, 10) || 0;
	    b = parseInt(b, 10) || 0;
	    if (r < 0 || r > 255) r = 0;
	    if (g < 0 || g > 255) g = 0;
	    if (b < 0 || b > 255) b = 0;
	    return [r, g, b];
	  },
	  hsvToRgb: function (h, s, v) {
	    var hi, f, p, q, t, r, g, b;
	    hi = Math.floor(h / 60) % 6;
	    f = h / 60 - hi;
	    p = v * (1 - s);
	    q = v * (1 - f * s);
	    t = v * (1 - (1 - f) * s);
	    r = 0;
	    g = 0;
	    b = 0;

	    switch (hi) {
	      case 0:
	        r = v;
	        g = t;
	        b = p;
	        break;

	      case 1:
	        r = q;
	        g = v;
	        b = p;
	        break;

	      case 2:
	        r = p;
	        g = v;
	        b = t;
	        break;

	      case 3:
	        r = p;
	        g = q;
	        b = v;
	        break;

	      case 4:
	        r = t;
	        g = p;
	        b = v;
	        break;

	      case 5:
	        r = v;
	        g = p;
	        b = q;
	        break;

	      default:
	        break;
	    }

	    r = Math.floor(r * 255);
	    g = Math.floor(g * 255);
	    b = Math.floor(b * 255);
	    return [r, g, b];
	  },
	  rgbToHsv: function (r, g, b) {
	    var r0, g0, b0, min0, max0, s, h, v;
	    r0 = r / 255;
	    g0 = g / 255;
	    b0 = b / 255;
	    min0 = Math.min(r0, g0, b0);
	    max0 = Math.max(r0, g0, b0);
	    h = 0;
	    s = max0 === 0 ? 0 : 1 - min0 / max0;
	    v = max0;

	    if (max0 == min0) {
	      h = 0;
	    } else if (max0 == r0 && g0 >= b0) {
	      h = 60 * (g0 - b0) / (max0 - min0) + 0;
	    } else if (max0 == r0 && g0 < b0) {
	      h = 60 * (g0 - b0) / (max0 - min0) + 360;
	    } else if (max0 == g0) {
	      h = 60 * (b0 - r0) / (max0 - min0) + 120;
	    } else if (max0 == b0) {
	      h = 60 * (r0 - g0) / (max0 - min0) + 240;
	    }

	    return [h, s, v];
	  }
	};

	function joinAttributes(attrs) {
	  var result = " ";
	  if (attrs) for (var a in attrs) {
	    result += a + "=\"" + attrs[a] + "\" ";
	  }
	  return result;
	} // SVG


	var SVG = {};

	SVG.draw = function (content, width, height, css) {
	  var attrs = {
	    xmlns: "http://www.w3.org/2000/svg",
	    version: "1.1",
	    height: "100%",
	    width: "100%",
	    viewBox: "0 0 " + width + " " + height,
	    "class": css || ""
	  };
	  return "<svg " + joinAttributes(attrs) + ">" + content + "</svg>";
	};

	SVG.styleMap = {
	  "lineColor": "stroke",
	  "color": "fill"
	};

	SVG.group = function (path) {
	  return "<g>" + path + "</g>";
	};

	SVG._handlers = {
	  // MoveTo: {x:px,y:py}
	  "M": function (p) {
	    return " M " + p.x + " " + p.y;
	  },
	  // LineTo: {x:px,y:py}
	  "L": function (p) {
	    return " L " + p.x + " " + p.y;
	  },
	  // Curve: 3 points {x:px,y:py}: two control points and an end point
	  "C": function (cp0, cp1, p) {
	    return " C " + cp0.x + " " + cp0.y + " " + cp1.x + " " + cp1.y + " " + p.x + " " + p.y;
	  },
	  // Arc: center point {x:px,y:py}, radius, angle0, angle1
	  "A": function (p, radius, angle0, angle1) {
	    var x = p.x + Math.cos(angle1) * radius;
	    var y = p.y + Math.sin(angle1) * radius;
	    var bigCircle = angle1 - angle0 >= Math.PI;
	    return " A " + radius + " " + radius + " 0 " + (bigCircle ? 1 : 0) + " 1 " + x + " " + y;
	  }
	}; // points is an array of an array with two elements: {string} line type, {array}

	SVG.definePath = function (points, close) {
	  var path = "";

	  for (var i = 0; i < points.length; i++) {
	    assert(points[i][0] && typeof points[i][0] == "string", "Path type must be a string");
	    var type = points[i][0].toUpperCase();
	    assert(this._handlers[type], "Incorrect path type");
	    path += this._handlers[type].apply(this, points[i].slice(1));
	  }

	  if (close) path += " Z";
	  return path;
	};

	SVG._linePoints = function (points) {
	  var result = [];

	  for (var i = 0; i < points.length; i++) {
	    result.push([i ? "L" : "M", points[i]]);
	  }

	  return result;
	};

	SVG.setOpacity = function (rawColor, opacity) {
	  var rgbColor = color.toRgb(rawColor);
	  rgbColor.push(opacity);
	  return "rgba(" + rgbColor.join(",") + ")";
	};

	SVG._curvePoints = function (points) {
	  var result = [];

	  for (var i = 0; i < points.length; i++) {
	    var p = points[i];

	    if (!i) {
	      result.push(["M", p[0]]);
	    }

	    result.push(["C", p[1], p[2], p[3]]);
	  }

	  return result;
	};

	SVG.getPath = function (path, css, attrs) {
	  attrs = joinAttributes(attrs);
	  return "<path class=\"" + css + "\" vector-effect=\"non-scaling-stroke\" d=\"" + path + "\" " + attrs + "/>";
	};

	SVG.getSector = function (p, radius, angle0, angle1, css, attrs) {
	  attrs = joinAttributes(attrs);
	  var x0 = p.x + Math.cos(angle0) * radius;
	  var y0 = p.y + Math.sin(angle0) * radius;
	  var lines = [["M", p], ["L", {
	    x: x0,
	    y: y0
	  }], ["A", p, radius, angle0, angle1], ["L", p]];
	  return "<path class=\"" + css + "\" vector-effect=\"non-scaling-stroke\" d=\"" + SVG.definePath(lines, true) + "\" " + attrs + "/>";
	};

	SVG.getCurve = function (points, css, attrs) {
	  attrs = joinAttributes(attrs);
	  var path = this.definePath(this._curvePoints(points));
	  return "<path fill=\"none\" class=\"" + css + "\" vector-effect=\"non-scaling-stroke\" d=\"" + path + "\" " + attrs + "/>";
	};

	SVG.getLine = function (p0, p1, css, attrs) {
	  return this.getPath(this.definePath(this._linePoints([p0, p1]), true), css, attrs);
	};

	SVG.getCircle = function (p, radius, css, attrs) {
	  attrs = joinAttributes(attrs);
	  return "<circle class=\"" + css + "\" cx=\"" + p.x + "\" cy=\"" + p.y + "\" r=\"" + radius + "\" " + attrs + "/>";
	};

	SVG.getRect = function (x, y, width, height, css, attrs) {
	  attrs = joinAttributes(attrs);
	  return "<rect class=\"" + css + "\" rx=\"0\" ry=\"0\" x=\"" + x + "\" y=\"" + y + "\" width=\"" + width + "\" height=\"" + height + "\" " + attrs + "/>";
	};

	var defaults = {
	  paddingX: 6,
	  paddingY: 6,
	  radius: 2,
	  minHeight: 4,
	  eventRadius: 8
	};

	function Line(config) {
	  this.config = exports.extend(copy(defaults), config || {}, true);
	}

	Line.prototype.draw = function (data, width, height) {
	  var points = this.getPoints(data, width, height);
	  var config = this.config;
	  var renderer = SVG;
	  var styles = config.color ? this._applyColor(renderer, config.color) : null; // draw line

	  var path = renderer.definePath(this._getLinePoints(points));
	  var graph = renderer.group(renderer.getPath(path, "webix_sparklines_line" + (styles ? " " + styles.line : ""))); // draw items

	  graph += this._drawItems(renderer, points, config.radius, "webix_sparklines_item" + (styles ? " " + styles.item : "")); // draw event items

	  var eventRadius = Math.min(data.length ? (width - 2 * (config.paddingX || 0)) / data.length : 0, config.eventRadius);
	  graph += this._drawEventItems(renderer, points, eventRadius);
	  return renderer.draw(graph, width, height, "webix_sparklines_line_chart" + (config.css ? " " + config.css : ""));
	};

	Line.prototype._applyColor = function (renderer, color) {
	  var config = {
	    "line": {},
	    "item": {}
	  },
	      map = renderer.styleMap;

	  if (color) {
	    config.line[map.lineColor] = color;
	    config.item[map.color] = color;

	    for (var name in config) {
	      config[name] = createCss(config[name]);
	    }
	  }

	  return config;
	};

	Line.prototype._drawItems = function (renderer, points, radius, css, attrs) {
	  var items = [];

	  for (var i = 0; i < points.length; i++) {
	    items.push(renderer.getCircle(points[i], radius, css, attrs));
	  }

	  return renderer.group(items.join(""));
	};

	Line.prototype._drawEventItems = function (renderer, points, radius) {
	  var items = [];

	  for (var i = 0; i < points.length; i++) {
	    items.push(renderer.getCircle(points[i], radius, "webix_sparklines_event_area", {
	      webix_area: i
	    }));
	  }

	  return renderer.group(items.join(""));
	};

	Line.prototype._getLinePoints = function (points) {
	  var i,
	      type,
	      result = [];

	  for (i = 0; i < points.length; i++) {
	    type = i ? "L" : "M";
	    result.push([type, points[i]]);
	  }

	  return result;
	};

	Line.prototype.getPoints = function (data, width, height) {
	  var config = this.config;
	  var minValue = Math.min.apply(null, data);
	  if (typeof config.origin !== "undefined") minValue = Math.min(config.origin, minValue);
	  var maxValue = Math.max.apply(null, data);
	  var result = [];
	  var x = config.paddingX || 0;
	  var y = config.paddingY || 0;
	  width = (width || 100) - x * 2;
	  var minHeight = config.minHeight || 0;
	  height = (height || 100) - y * 2;

	  if (data.length) {
	    if (data.length == 1) result.push({
	      x: width / 2 + x,
	      y: height / 2 + x
	    });else {
	      var unitX = width / (data.length - 1);
	      var yNum = config.scale || maxValue - minValue;
	      var unitY = (height - minHeight) / (yNum ? yNum : 1);
	      if (!yNum) height /= 2;

	      for (var i = 0; i < data.length; i++) {
	        result.push({
	          x: Math.ceil(unitX * i) + x,
	          y: height - Math.ceil(unitY * (data[i] - minValue)) + y - minHeight
	        });
	      }
	    }
	  }

	  return result;
	};

	var defaults$1 = {
	  paddingX: 3,
	  paddingY: 4,
	  radius: 1,
	  minHeight: 4,
	  eventRadius: 8
	};

	function Area(config) {
	  this.config = exports.extend(copy(defaults$1), config || {}, true);
	}

	Area.prototype.draw = function (data, width, height) {
	  var eventRadius,
	      graph,
	      path,
	      points,
	      styles,
	      config = this.config,
	      Line$$1 = Line.prototype,
	      renderer = SVG; // draw area

	  points = this.getPoints(data, width, height);
	  path = renderer.definePath(Line$$1._getLinePoints(points), true);
	  if (config.color) styles = this._applyColor(renderer, config.color);
	  graph = renderer.group(renderer.getPath(path, "webix_sparklines_area" + (styles ? " " + styles.area : ""))); // draw line

	  points.splice(points.length - 3, 3);
	  path = renderer.definePath(Line$$1._getLinePoints(points));
	  graph += renderer.group(renderer.getPath(path, "webix_sparklines_line" + (styles ? " " + styles.line : ""))); // draw items

	  graph += Line$$1._drawItems(renderer, points, config.radius, "webix_sparklines_item" + (styles ? " " + styles.item : "")); // draw event areas

	  eventRadius = Math.min(data.length ? (width - 2 * (config.paddingX || 0)) / data.length : 0, config.eventRadius);
	  graph += Line$$1._drawEventItems(renderer, points, eventRadius);
	  return renderer.draw(graph, width, height, "webix_sparklines_area_chart" + (config.css ? " " + config.css : ""));
	};

	Area.prototype._applyColor = function (renderer, color) {
	  var config = {
	    "area": {},
	    "line": {},
	    "item": {}
	  },
	      map = renderer.styleMap;

	  if (color) {
	    config.area[map.color] = renderer.setOpacity(color, 0.2);
	    config.line[map.lineColor] = color;
	    config.item[map.color] = color;

	    for (var name in config) {
	      config[name] = createCss(config[name]);
	    }
	  }

	  return config;
	};

	Area.prototype.getPoints = function (data, width, height) {
	  var Line$$1 = Line.prototype;
	  var points = Line$$1.getPoints.call(this, data, width, height);
	  var x = this.config.paddingX || 0;
	  var y = this.config.paddingY || 0;
	  points.push({
	    x: width - x,
	    y: height - y
	  }, {
	    x: x,
	    y: height - y
	  }, {
	    x: x,
	    y: points[0].y
	  });
	  return points;
	};

	var defaults$2 = {
	  paddingX: 3,
	  paddingY: 4,
	  width: 20,
	  margin: 4,
	  minHeight: 4,
	  eventRadius: 8,
	  origin: 0,
	  itemCss: function (value) {
	    return value < (this.config.origin || 0) ? " webix_sparklines_bar_negative" : "";
	  }
	};

	function Bar(config) {
	  this.config = exports.extend(copy(defaults$2), config || {}, true);
	}

	Bar.prototype.draw = function (data, width, height) {
	  var i,
	      css,
	      p,
	      y,
	      padding,
	      config = this.config,
	      graph = "",
	      items = [],
	      points = this.getPoints(data, width, height),
	      renderer = SVG; // draw bars

	  for (i = 0; i < points.length; i++) {
	    css = typeof config.itemCss == "function" ? config.itemCss.call(this, data[i]) : config.itemCss || "";
	    if (config.negativeColor && data[i] < config.origin) css += " " + this._applyColor(renderer, config.negativeColor);else if (config.color) css += " " + this._applyColor(renderer, config.color);
	    p = points[i];
	    items.push(renderer.getRect(p.x, p.y, p.width, p.height, "webix_sparklines_bar " + css));
	  }

	  graph += renderer.group(items.join("")); // origin)

	  y = parseInt(this._getOrigin(data, width, height), 10) + 0.5;
	  padding = config.paddingX || 0;
	  graph += renderer.group(renderer.getLine({
	    x: padding,
	    y: y
	  }, {
	    x: width - padding,
	    y: y
	  }, "webix_sparklines_origin")); // event areas

	  var evPoints = this._getEventPoints(data, width, height);

	  var evItems = [];

	  for (i = 0; i < evPoints.length; i++) {
	    p = evPoints[i];
	    evItems.push(renderer.getRect(p.x, p.y, p.width, p.height, "webix_sparklines_event_area ", {
	      "webix_area": i
	    }));
	  }

	  graph += renderer.group(evItems.join(""));
	  return renderer.draw(graph, width, height, "webix_sparklines_bar_chart" + (config.css ? " " + config.css : ""));
	};

	Bar.prototype._applyColor = function (renderer, color) {
	  var config = {},
	      map = renderer.styleMap;
	  if (color) config[map.color] = color;
	  return createCss(config);
	};

	Bar.prototype._getOrigin = function (data, width, height) {
	  var config = this.config;
	  var y = config.paddingY || 0;
	  height = (height || 100) - y * 2;
	  var pos$$1 = y + height;

	  if (config.origin !== false) {
	    var minValue = Math.min.apply(null, data);
	    var maxValue = Math.max.apply(null, data);
	    var origin = config.origin || -0.000001;

	    if (origin >= maxValue) {
	      pos$$1 = y;
	    } else if (origin > minValue) {
	      var unitY = height / (maxValue - minValue);
	      pos$$1 -= unitY * (origin - minValue);
	    }
	  }

	  return pos$$1;
	};

	Bar.prototype._getEventPoints = function (data, width, height) {
	  var result = [];
	  var x = this.config.paddingX || 0;
	  var y = this.config.paddingY || 0;
	  width = (width || 100) - x * 2;
	  height = (height || 100) - y * 2;

	  if (data.length) {
	    var unitX = width / data.length;

	    for (var i = 0; i < data.length; i++) {
	      result.push({
	        x: Math.ceil(unitX * i) + x,
	        y: y,
	        height: height,
	        width: unitX
	      });
	    }
	  }

	  return result;
	};

	Bar.prototype.getPoints = function (data, width, height) {
	  var config = this.config;
	  var minValue = Math.min.apply(null, data);
	  if (config.origin < minValue) minValue = config.origin;
	  var maxValue = Math.max.apply(null, data);
	  var result = [];
	  var x = config.paddingX;
	  var y = config.paddingY;
	  var margin = config.margin;
	  var barWidth = config.width || 20;

	  var originY = this._getOrigin(data, width, height);

	  width = (width || 100) - x * 2;
	  height = (height || 100) - y * 2;

	  if (data.length) {
	    var unitX = width / data.length;
	    var yNum = config.scale || maxValue - minValue;
	    barWidth = Math.min(unitX - margin, barWidth);
	    margin = unitX - barWidth;
	    var minHeight = 0;
	    var origin = minValue;
	    if (config.origin !== false && config.origin > minValue) origin = config.origin || 0;else minHeight = config.minHeight;
	    var unitY = (height - minHeight) / (yNum ? yNum : 1);

	    for (var i = 0; i < data.length; i++) {
	      var h = Math.ceil(unitY * (data[i] - origin));
	      result.push({
	        x: Math.ceil(unitX * i) + x + margin / 2,
	        y: originY - (data[i] >= origin ? h : 0) - minHeight,
	        height: Math.abs(h) + minHeight,
	        width: barWidth
	      });
	    }
	  }

	  return result;
	};

	var defaults$3 = {
	  paddingY: 2
	};

	function Pie(config) {
	  this.config = exports.extend(defaults$3, config || {}, true);
	}

	Pie.prototype._defColorsCursor = 0;
	Pie.prototype._defColors = ["#f55b50", "#ff6d3f", "#ffa521", "#ffc927", "#ffee54", "#d3e153", "#9acb61", "#63b967", "#21a497", "#21c5da", "#3ea4f5", "#5868bf", "#7b53c0", "#a943ba", "#ec3b77", "#9eb0b8"];

	Pie.prototype._getColor = function (i, data) {
	  var count = data.length;
	  var colorsCount = this._defColors.length;

	  if (colorsCount > count) {
	    if (i) {
	      if (i < colorsCount - count) i = this._defColorsCursor + 2;else i = this._defColorsCursor + 1;
	    }

	    this._defColorsCursor = i;
	  } else i = i % colorsCount;

	  return this._defColors[i];
	};

	Pie.prototype.draw = function (data, width, height) {
	  var attrs,
	      graph,
	      i,
	      sectors,
	      config = this.config,
	      color = config.color || this._getColor,
	      points = this.getAngles(data),
	      renderer = SVG,
	      y = config.paddingY || 0,
	      // radius
	  r = height / 2 - y,
	      // center
	  x0 = width / 2,
	      y0 = height / 2; // draw sectors

	  if (typeof color != "function") color = function () {
	    return color;
	  };
	  sectors = "";

	  for (i = 0; i < points.length; i++) {
	    attrs = {};
	    attrs[renderer.styleMap["color"]] = color.call(this, i, data, this._context);
	    sectors += renderer.getSector({
	      x: x0,
	      y: y0
	    }, r, points[i][0], points[i][1], "webix_sparklines_sector", attrs);
	  }

	  graph = renderer.group(sectors); // draw event areas

	  sectors = "";

	  for (i = 0; i < points.length; i++) {
	    sectors += renderer.getSector({
	      x: x0,
	      y: y0
	    }, r, points[i][0], points[i][1], "webix_sparklines_event_area", {
	      "webix_area": i
	    });
	  }

	  graph += renderer.group(sectors);
	  return renderer.draw(graph, width, height, "webix_sparklines_pie_chart" + (config.css ? " " + config.css : ""));
	};

	Pie.prototype.getAngles = function (data) {
	  var a0 = -Math.PI / 2,
	      a1,
	      i,
	      result = [];

	  var ratios = this._getRatios(data);

	  for (i = 0; i < data.length; i++) {
	    a1 = -Math.PI / 2 + ratios[i] - 0.0001;
	    result.push([a0, a1]);
	    a0 = a1;
	  }

	  return result;
	};

	Pie.prototype._getTotalValue = function (data) {
	  var t = 0;

	  for (var i = 0; i < data.length; i++) {
	    t += data[i] * 1;
	  }

	  return t;
	};

	Pie.prototype._getRatios = function (data) {
	  var i,
	      value,
	      ratios = [],
	      prevSum = 0,
	      totalValue = this._getTotalValue(data);

	  for (i = 0; i < data.length; i++) {
	    value = data[i] * 1;
	    ratios[i] = Math.PI * 2 * (totalValue ? (value + prevSum) / totalValue : 1 / data.length);
	    prevSum += value;
	  }

	  return ratios;
	};

	var defaults$4 = {
	  paddingX: 3,
	  paddingY: 6,
	  radius: 2,
	  minHeight: 4,
	  eventRadius: 8
	};

	function Spline(config) {
	  this.config = exports.extend(copy(defaults$4), config || {}, true);
	}

	Spline.prototype.draw = function (data, width, height) {
	  var config = this.config,
	      graph = "",
	      Line$$1 = Line.prototype,
	      points = this.getPoints(data, width, height),
	      renderer = SVG,
	      styles = config.color ? this._applyColor(renderer, config.color) : null; // draw spline

	  graph += renderer.group(renderer.getCurve(points, "webix_sparklines_line" + (styles ? " " + styles.line : "")));
	  var linePoints = Line$$1.getPoints.call(this, data, width, height); // draw items

	  graph += Line$$1._drawItems(renderer, linePoints, config.radius, "webix_sparklines_item" + (styles ? " " + styles.item : "")); // draw event items

	  var eventRadius = Math.min(data.length ? (width - 2 * (config.paddingX || 0)) / data.length : 0, config.eventRadius);
	  graph += Line$$1._drawEventItems(renderer, linePoints, eventRadius);
	  return renderer.draw(graph, width, height, "webix_sparklines_line_chart" + (config.css ? " " + config.css : ""));
	};

	Spline.prototype._applyColor = function (renderer, color) {
	  var config = {
	    "line": {},
	    "item": {}
	  },
	      map = renderer.styleMap;

	  if (color) {
	    config.line[map.lineColor] = color;
	    config.item[map.color] = color;

	    for (var name in config) {
	      config[name] = createCss(config[name]);
	    }
	  }

	  return config;
	};

	Spline.prototype.getPoints = function (data, width, height) {
	  var i,
	      points,
	      px,
	      py,
	      result = [],
	      x = [],
	      y = [],
	      Line$$1 = Line.prototype;
	  points = Line$$1.getPoints.call(this, data, width, height);

	  for (i = 0; i < points.length; i++) {
	    x.push(points[i].x);
	    y.push(points[i].y);
	  }

	  px = this._getControlPoints(x);
	  py = this._getControlPoints(y);
	  /*updates path settings, the browser will draw the new spline*/

	  for (i = 0; i < points.length - 1; i++) {
	    result.push([points[i], {
	      x: px[0][i],
	      y: py[0][i]
	    }, {
	      x: px[1][i],
	      y: py[1][i]
	    }, points[i + 1]]);
	  }

	  return result;
	};
	/* code from https://www.particleincell.com/2012/bezier-splines/ */


	Spline.prototype._getControlPoints = function (points) {
	  var a = [],
	      b = [],
	      c = [],
	      r = [],
	      p1 = [],
	      p2 = [],
	      i,
	      m,
	      n = points.length - 1;
	  a[0] = 0;
	  b[0] = 2;
	  c[0] = 1;
	  r[0] = points[0] + 2 * points[1];

	  for (i = 1; i < n - 1; i++) {
	    a[i] = 1;
	    b[i] = 4;
	    c[i] = 1;
	    r[i] = 4 * points[i] + 2 * points[i + 1];
	  }

	  a[n - 1] = 2;
	  b[n - 1] = 7;
	  c[n - 1] = 0;
	  r[n - 1] = 8 * points[n - 1] + points[n];

	  for (i = 1; i < n; i++) {
	    m = a[i] / b[i - 1];
	    b[i] = b[i] - m * c[i - 1];
	    r[i] = r[i] - m * r[i - 1];
	  }

	  p1[n - 1] = r[n - 1] / b[n - 1];

	  for (i = n - 2; i >= 0; --i) {
	    p1[i] = (r[i] - c[i] * p1[i + 1]) / b[i];
	  }

	  for (i = 0; i < n - 1; i++) {
	    p2[i] = 2 * points[i + 1] - p1[i + 1];
	  }

	  p2[n - 1] = 0.5 * (points[n] + p1[n - 1]);
	  return [p1, p2];
	};

	var defaultsArea = {
	  paddingX: 3,
	  paddingY: 6,
	  radius: 1,
	  minHeight: 4,
	  eventRadius: 8
	}; // spline area

	function SplineArea(config) {
	  this.config = exports.extend(copy(defaultsArea), config || {}, true);
	}

	SplineArea.prototype = copy(Spline.prototype);

	SplineArea.prototype.draw = function (data, width, height) {
	  var config = this.config,
	      Line$$1 = Line.prototype,
	      renderer = SVG,
	      styles = config.color ? this._applyColor(renderer, config.color) : null;
	  var points = this.getPoints(data, width, height); // draw area

	  var linePoints = points.splice(points.length - 3, 3);

	  var linePath = renderer._linePoints(linePoints);

	  linePath[0][0] = "L";

	  var areaPoints = renderer._curvePoints(points).concat(linePath);

	  var graph = renderer.group(renderer.getPath(renderer.definePath(areaPoints), "webix_sparklines_area" + (styles ? " " + styles.area : ""), true)); // draw line

	  graph += renderer.group(renderer.getPath(renderer.definePath(renderer._curvePoints(points)), "webix_sparklines_line" + (styles ? " " + styles.line : "")));
	  var itemPoints = Line$$1.getPoints.call(this, data, width, height); // draw items

	  graph += Line$$1._drawItems(renderer, itemPoints, config.radius, "webix_sparklines_item" + (styles ? " " + styles.item : "")); // draw event items

	  var eventRadius = Math.min(data.length ? (width - 2 * (config.paddingX || 0)) / data.length : 0, config.eventRadius);
	  graph += Line$$1._drawEventItems(renderer, itemPoints, eventRadius);
	  return renderer.draw(graph, width, height, "webix_sparklines_splinearea_chart" + (config.css ? " " + config.css : ""));
	};

	SplineArea.prototype._applyColor = function (renderer, color) {
	  var config = {
	    "area": {},
	    "line": {},
	    "item": {}
	  },
	      map = renderer.styleMap;

	  if (color) {
	    config.area[map.color] = renderer.setOpacity(color, 0.2);
	    config.line[map.lineColor] = color;
	    config.item[map.color] = color;

	    for (var name in config) {
	      config[name] = createCss(config[name]);
	    }
	  }

	  return config;
	};

	SplineArea.prototype.getPoints = function (data, width, height) {
	  var points = Spline.prototype.getPoints.call(this, data, width, height);
	  var x = this.config.paddingX || 0;
	  var y = this.config.paddingY || 0;
	  points.push({
	    x: width - x,
	    y: height - y
	  }, {
	    x: x,
	    y: height - y
	  }, {
	    x: x,
	    y: points[0][0].y
	  });
	  return points;
	};

	function Sparklines() {}

	function getData(data) {
	  var values = [];

	  for (var i = data.length - 1; i >= 0; i--) {
	    var value = data[i];
	    values[i] = _typeof(value) === "object" ? value.value : value;
	  }

	  return values;
	}

	Sparklines.types = {};

	Sparklines.getTemplate = function (customConfig) {
	  var config = customConfig || {};
	  if (typeof customConfig == "string") config = {
	    type: customConfig
	  };
	  exports.extend(config, {
	    type: "line"
	  });
	  var slConstructor = this.types[config.type];
	  assert(slConstructor, "Unknown sparkline type");
	  return bind(this._template, new slConstructor(config));
	};

	Sparklines._template = function (item, common, data, column) {
	  if (column) return this.draw(getData(data), column.width, 33);else return this.draw(item.data || item, common.width, common.height);
	}; // add "sparklines" type


	attachEvent("onDataTable", function (table) {
	  table.type.sparklines = Sparklines.getTemplate();
	});
	Sparklines.types["area"] = Area;
	Sparklines.types["bar"] = Bar;
	Sparklines.types["line"] = Line;
	Sparklines.types["pie"] = Pie;
	Sparklines.types["spline"] = Spline;
	Sparklines.types["splineArea"] = SplineArea;

	var csv$1 = {
	  escape: true,
	  delimiter: {
	    rows: "\n",
	    cols: "\t"
	  },
	  parse: function (text, sep) {
	    sep = sep || this.delimiter;
	    if (!this.escape) return this._split_clip_data(text, sep);
	    var lines = text.replace(/\n$/, "").split(sep.rows);
	    var i = 0;

	    while (i < lines.length - 1) {
	      if (this._substr_count(lines[i], "\"") % 2 === 1) {
	        lines[i] += sep.rows + lines[i + 1];
	        delete lines[i + 1];
	        i++;
	      }

	      i++;
	    }

	    var csv = [];

	    for (i = 0; i < lines.length; i++) {
	      if (typeof lines[i] !== "undefined") {
	        var tline = lines[i];
	        var start = 0;
	        var line = [];
	        var quoted = false;

	        for (var j = 0; j <= tline.length; j++) {
	          if (!quoted && tline[j] === sep.cols || j === tline.length) {
	            var chunk = tline.substr(start, j - start);

	            if (chunk[0] === chunk[chunk.length - 1] && chunk[0] === "\"") {
	              chunk = chunk.substr(1, chunk.length - 2).replace("\"\"", "\"");
	            }

	            line.push(chunk);
	            start = j + 1;
	          }

	          if (tline[j] === "\"") {
	            quoted = !quoted;
	            continue;
	          }
	        }

	        csv.push(line);
	      }
	    }

	    return csv;
	  },
	  _split_clip_data: function (text, sep) {
	    var lines = text.split(sep.rows);

	    for (var i = 0; i < lines.length; i++) {
	      lines[i] = lines[i].split(sep.cols);
	    }

	    return lines;
	  },

	  /*! counts how many occurances substring in string **/
	  _substr_count: function (string, substring) {
	    var arr = string.split(substring);
	    return arr.length - 1;
	  },
	  stringify: function (data, sep) {
	    sep = sep || this.delimiter;

	    if (!this.escape) {
	      for (var i = 0; i < data.length; i++) {
	        data[i] = data[i].join(sep.cols);
	      }

	      return data.join(sep.rows);
	    }

	    var reg = /\n|"|;|,/;

	    for (var _i = 0; _i < data.length; _i++) {
	      for (var j = 0; j < data[_i].length; j++) {
	        var chunk = data[_i][j];
	        if (chunk instanceof Date) data[_i][j] = i18n.parseFormatStr(chunk);else if (reg.test(chunk)) data[_i][j] = "\"" + chunk.toString().replace(/"/g, "\"\"") + "\"";
	      }

	      data[_i] = data[_i].join(sep.cols);
	    }

	    data = data.join(sep.rows);
	    return data;
	  }
	};

	var TablePaste = {
	  clipboard_setter: function (value) {
	    if (value === true || value === 1) value = "block";
	    clipbuffer.init();
	    this.attachEvent("onSelectChange", this._sel_to_clip); // solution for clicks on selected items

	    this.attachEvent("onItemClick", function () {
	      if (document.activeElement && this.$view.contains(document.activeElement)) {
	        clipbuffer.focus();
	        UIManager.setFocus(this);
	      }
	    });
	    this.attachEvent("onPaste", this._clip_to_sel);
	    return value;
	  },
	  templateCopy_setter: template,
	  _sel_to_clip: function () {
	    var data = this._get_sel_text();

	    clipbuffer.set(data);
	    UIManager.setFocus(this);
	  },
	  _get_sel_text: function () {
	    var data = [];
	    var filter = this._settings.templateCopy;
	    this.mapSelection(function (value, row, col, row_ind) {
	      if (!data[row_ind]) data[row_ind] = [];
	      var newvalue = filter ? filter(value, row, col) : value;
	      data[row_ind].push(newvalue);
	      return value;
	    });
	    var value = data.length === 1 && data[0].length === 1 ? data[0][0] : csv$1.stringify(data, this._settings.delimiter);
	    return value;
	  },
	  _clip_to_sel: function (text) {
	    if (!isUndefined(this._paste[this._settings.clipboard])) {
	      var data = csv$1.parse(text, this._settings.delimiter);

	      this._paste[this._settings.clipboard].call(this, data);
	    }
	  },
	  _paste: {
	    block: function (data) {
	      var leftTop = this.mapSelection(null);
	      if (!leftTop) return; // filling cells with data

	      this.mapCells(leftTop.row, leftTop.column, data.length, null, function (value, row, col, row_ind, col_ind) {
	        if (data[row_ind] && data[row_ind].length > col_ind) {
	          return data[row_ind][col_ind];
	        }

	        return value;
	      });
	      this.render();
	    },
	    selection: function (data) {
	      this.mapSelection(function (value, row, col, row_ind, col_ind) {
	        if (data[row_ind] && data[row_ind].length > col_ind) return data[row_ind][col_ind];
	        return value;
	      });
	      this.render();
	    },
	    repeat: function (data) {
	      this.mapSelection(function (value, row, col, row_ind, col_ind) {
	        row = data[row_ind % data.length];
	        value = row[col_ind % row.length];
	        return value;
	      });
	      this.render();
	    },
	    custom: function () {}
	  }
	};

	var TreeAPI = {
	  open: function (id, show) {
	    if (!id) return; //ignore open for leaf items

	    var item = this.getItem(id);
	    assert(item, "Incorrect ID to open: " + id);
	    if (!item.$count || item.open) return;

	    if (this.callEvent("onBeforeOpen", [id])) {
	      item.open = true;
	      this.data.callEvent("onStoreUpdated", [id, 0, "branch"]);
	      this.callEvent("onAfterOpen", [id]);
	    }

	    if (show && id != "0") this.open(this.getParentId(id), show);
	  },
	  close: function (id) {
	    if (!id) return;
	    var item = this.getItem(id);
	    if (!item.open) return;

	    if (this.callEvent("onBeforeClose", [id])) {
	      item.open = false;
	      this.data.callEvent("onStoreUpdated", [id, 0, "branch"]);
	      this.callEvent("onAfterClose", [id]);
	    }
	  },
	  openAll: function (id) {
	    this.data.eachSubItem(id || 0, function (obj, branch) {
	      if (branch) obj.open = true;
	    });
	    this.data.refresh();
	  },
	  closeAll: function (id) {
	    this.data.eachSubItem(id || 0, function (obj, branch) {
	      if (branch) obj.open = false;
	    });
	    this.data.refresh();
	  },
	  _tree_check_uncheck: function (id, mode, e) {
	    if (this._settings.threeState) return this._tree_check_uncheck_3(id, mode !== null ? mode : "");
	    var value,
	        item = this.getItem(id),
	        trg = e ? e.target || e.srcElement : null; //read actual value from HTML tag when possible
	    //as it can be affected by dbl-clicks

	    if (trg && trg.type == "checkbox") value = trg.checked ? true : false;else value = mode !== null ? mode : !item.checked;
	    item.checked = value;
	    this.callEvent("onItemCheck", [id, item.checked, e]);
	  },
	  isBranchOpen: function (search_id) {
	    if (search_id == "0") return true;
	    var item = this.getItem(search_id);
	    if (item.open) return this.isBranchOpen(item.$parent);
	    return false;
	  },
	  getOpenItems: function () {
	    var open = [];

	    for (var id in this.data.branch) {
	      if (this.exists(id) && this.getItem(id).open) open.push(id);
	    }

	    return open;
	  },
	  getState: function () {
	    return {
	      open: this.getOpenItems(),
	      select: this.getSelectedId(true)
	    };
	  },
	  _repeat_set_state: function (tree, open) {
	    var event = this.data.attachEvent("onStoreLoad", function () {
	      tree.setState.call(tree, open);
	      tree.data.detachEvent(event);
	      tree = null;
	    });
	  },
	  setState: function (state) {
	    if (state.open) {
	      this.closeAll();
	      var open = state.open;

	      for (var i = 0; i < open.length; i++) {
	        var item = this.getItem(open[i]);

	        if (item && item.$count) {
	          item.open = true; //dynamic loading

	          if (item.$count == -1) {
	            //call the same method after data loading
	            this._repeat_set_state(this, state);

	            this.refresh();
	            return 0; //end processing
	          }
	        }
	      }

	      this.refresh();
	    }

	    if (state.select && this.select) {
	      var select = state.select;
	      this.unselect();

	      for (var _i = 0; _i < select.length; _i++) {
	        if (this.exists(select[_i])) this.select(select[_i], true);
	      }
	    }

	    return 1;
	  }
	};

	var TreeClick = {
	  webix_tree_open: function (e, id) {
	    this.close(id);
	    return false;
	  },
	  webix_tree_close: function (e, id) {
	    this.open(id);
	    return false;
	  },
	  webix_tree_checkbox: function (e, id) {
	    this._tree_check_uncheck(id, null, e);

	    return false;
	  }
	};

	var TreeDataLoader = {
	  $init: function () {
	    this.data.attachEvent("onStoreUpdated", bind(this._sync_hierarchy, this), null, true); //redefine methods

	    this._feed_common = this._feed_commonA;
	  },
	  _feed_commonA: function (id, count, callback, url) {
	    // branch loading
	    var details = count === 0 ? {
	      parent: encodeURIComponent(id)
	    } : null;
	    return DataLoader.prototype._feed_common.call(this, id, count, callback, url, details);
	  },
	  //load next set of data rows
	  loadBranch: function (id, callback, url) {
	    id = id || 0;
	    this.data.url = url || this.data.url;
	    if (this.callEvent("onDataRequest", [id, callback, this.data.url]) && this.data.url) return this.data.feed.call(this, id, 0, callback, url);
	    return Deferred.reject();
	  },
	  _sync_hierarchy: function (id, data, mode) {
	    if (!mode || mode == "add" || mode == "delete" || mode == "branch") {
	      this.data._sync_to_order(this);
	    }
	  }
	};

	/*
		Behavior:DataMove - allows to move and copy elements, heavily relays on DataStore.move
		@export
			copy
			move
	*/

	var TreeDataMove = {
	  $init: function () {
	    assert(this.data, "DataMove :: Component doesn't have DataStore");
	  },
	  //creates a copy of the item
	  copy: function (sid, tindex, tobj, details) {
	    details = details || {};
	    details.copy = true;
	    return this.move(sid, tindex, tobj, details);
	  },
	  _next_move_index: function (nid, next, source) {
	    if (next && nid) {
	      var new_index = this.getBranchIndex(nid);
	      var sameParent = this.getParentId(nid) == this.getParentId(next);
	      return new_index + (source == this && sameParent && source.getBranchIndex(next) < new_index ? 0 : 1);
	    }
	  },
	  _check_branch_child: function (parent, child) {
	    var t = this.data.branch[parent];

	    if (t && t.length) {
	      for (var i = 0; i < t.length; i++) {
	        if (t[i] == child) return true;
	        if (this._check_branch_child(t[i], child)) return true;
	      }
	    }

	    return false;
	  },
	  _remove_childs: function (ids) {
	    for (var i = 0; i < ids.length; i++) {
	      var id = ids[i];

	      while (this.getParentId(id)) {
	        id = this.getParentId(id);

	        if (PowerArray.find.call(ids, id) != -1) {
	          ids.splice(i, 1);
	          i--;
	          continue;
	        }
	      }
	    }

	    return ids;
	  },
	  //move item to the new position
	  move: function (sid, tindex, tobj, details) {
	    details = details || {};
	    tindex = tindex || 0;
	    var new_id = details.newId || sid;
	    var target_parent = details.parent || 0;
	    tobj = tobj || this;
	    assert(tobj.data, "moving attempt to component without datastore");
	    if (!tobj.data) return;

	    if (isArray(sid)) {
	      this._remove_childs(sid);

	      for (var i = 0; i < sid.length; i++) {
	        //increase index for each next item in the set, so order of insertion will be equal to order in the array
	        var nid = this.move(sid[i], tindex, tobj, details);
	        tindex = tobj._next_move_index(nid, sid[i + 1], this);
	      }

	      return;
	    }

	    if (this != tobj || details.copy) {
	      new_id = tobj.data.add(tobj._externalData(this.getItem(sid), new_id), tindex, target_parent || 0);

	      if (this.data.branch[sid] && tobj.getBranchIndex) {
	        var temp = this.data._scheme_serialize;

	        this.data._scheme_serialize = function (obj) {
	          var copy$$1 = copy(obj);
	          delete copy$$1.$parent;
	          delete copy$$1.$level;
	          delete copy$$1.$child;
	          if (tobj.data.pull[copy$$1.id]) copy$$1.id = uid();
	          return copy$$1;
	        };

	        var copy_data = {
	          data: this.serialize(sid, true),
	          parent: new_id
	        };
	        this.data._scheme_serialize = temp;
	        tobj.parse(copy_data);
	      }

	      if (!details.copy) this.data.remove(sid);
	    } else {
	      //move in self
	      if (sid == target_parent || this._check_branch_child(sid, target_parent)) return;
	      var source = this.getItem(sid);
	      var tbranch = this.data.branch[target_parent];
	      if (!tbranch) tbranch = this.data.branch[target_parent] = [];
	      var sbranch = this.data.branch[source.$parent];
	      var sindex = PowerArray.find.call(sbranch, sid);
	      if (tindex < 0) tindex = tbranch.length; //in the same branch

	      if (sbranch === tbranch && tindex === sindex) return; //same position

	      PowerArray.removeAt.call(sbranch, sindex);
	      PowerArray.insertAt.call(tbranch, sid, Math.min(tbranch.length, tindex));
	      if (!sbranch.length) delete this.data.branch[source.$parent];
	      if (source.$parent && source.$parent != "0") this.getItem(source.$parent).$count--;

	      if (target_parent && target_parent != "0") {
	        var target = tobj.getItem(target_parent);
	        target.$count++;

	        this._set_level_rec(source, target.$level + 1);
	      } else this._set_level_rec(source, 1);

	      source.$parent = target_parent;
	      tobj.data.callEvent("onDataMove", [sid, tindex, target_parent, tbranch[tindex + 1]]);
	    }

	    this.refresh();
	    return new_id; //return ID of item after moving
	  },
	  _set_level_rec: function (item, value) {
	    item.$level = value;
	    var branch = this.data.branch[item.id];
	    if (branch) for (var i = 0; i < branch.length; i++) {
	      this._set_level_rec(this.getItem(branch[i]), value + 1);
	    }
	  },
	  //reaction on pause during dnd
	  _drag_pause: function (id) {
	    if (id && !id.header && this.exists(id) && this._target_to_id(id) != DragControl._drag_context.start) //ignore drag other header
	      this.open(id);
	  },
	  $dropAllow: function (context) {
	    if (context.from != context.to) return true;

	    for (var i = 0; i < context.source.length; i++) {
	      if (this._check_branch_child(context.source, context.target)) return false;
	    }

	    return true;
	  },

	  /*
	  	this is a stub for future functionality
	  	currently it just makes a copy of data object, which is enough for current situation
	  */
	  _externalData: function (data, id) {
	    var new_data = DataMove._externalData.call(this, data, id);

	    delete new_data.open;
	    return new_data;
	  }
	};

	var TreeRenderStack = {
	  $init: function () {
	    assert(this.render, "TreeRenderStack :: Object must use RenderStack first");
	  },
	  _toHTMLItem: function (obj) {
	    var mark = this.data._marks[obj.id];
	    this.callEvent("onItemRender", [obj]);
	    return this.type.templateStart(obj, this.type, mark) + (obj.$template ? this.type["template" + obj.$template](obj, this.type, mark) : this.type.template(obj, this.type, mark)) + this.type.templateEnd();
	  },
	  _toHTMLItemObject: function (obj) {
	    this._html.innerHTML = this._toHTMLItem(obj);
	    return this._html.firstChild;
	  },
	  //convert single item to HTML text (templating)
	  _toHTML: function (obj) {
	    //check if related template exist
	    assert(!obj.$template || this.type["template" + obj.$template], "RenderStack :: Unknown template: " + obj.$template);

	    var html = "<div role='presentation' class='webix_tree_branch_" + obj.$level + "'>" + this._toHTMLItem(obj);

	    if (obj.open) html += this._toHTMLLevel(obj.id);
	    html += "</div>";
	    return html;
	  },
	  _toHTMLLevel: function (id) {
	    var html = "";
	    var leaves = this.data.branch[id];

	    if (leaves) {
	      html += "<div role='presentation' class='webix_tree_leaves'>";
	      var last = leaves.length - 1;

	      for (var i = 0; i <= last; i++) {
	        var obj = this.getItem(leaves[i]);
	        var state = this.type._tree_branch_render_state;
	        if (state !== 0) state[obj.$level] = i == last;
	        html += this._toHTML(obj);
	      }

	      html += "</div>";
	    }

	    return html;
	  },
	  //return true when some actual rendering done
	  render: function (id, data, type) {
	    TreeRenderStack._obj = this; //can be used from complex render

	    if (!this.isVisible(this._settings.id) || this.$blockRender) return;

	    if (id) {
	      var cont, node;
	      var item = this.getItem(id);

	      if (type != "add") {
	        cont = this.getItemNode(id);
	        if (!cont) return;
	      }

	      switch (type) {
	        case "branch":
	          var branch = cont.parentNode;
	          node = this._toHTMLObject(item);
	          insertBefore(node, branch);
	          remove(branch);
	          this._htmlmap = null;
	          break;

	        case "paint":
	        case "update":
	          node = this._htmlmap[id] = this._toHTMLItemObject(item);
	          insertBefore(node, cont);
	          remove(cont);
	          break;

	        case "delete":
	          //deleting not item , but full branch
	          remove(cont.parentNode);
	          break;

	        case "add":
	          var parent; //we want process both empty value and 0 as string
	          //jshint -W041:true

	          if (item.$parent == 0) {
	            parent = this._dataobj.firstChild;
	          } else if (this.getItem(item.$parent).open) {
	            parent = this.getItemNode(item.$parent);

	            if (parent) {
	              //when item created by the script, it will miss the container for child notes
	              //create it on demand
	              if (!parent.nextSibling) {
	                var leafs = create("DIV", {
	                  "class": "webix_tree_leaves"
	                }, "");
	                parent.parentNode.appendChild(leafs);
	              }

	              parent = parent.nextSibling;
	            }
	          }

	          if (parent) {
	            var next = this.data.getNextSiblingId(id);
	            next = this.getItemNode(next);
	            if (next) next = next.parentNode;
	            node = this._toHTMLObject(item);
	            this._htmlmap[id] = node.firstChild;
	            insertBefore(node, next, parent);
	          }

	          break;

	        default:
	          return false;
	      }

	      this.callEvent("onPartialRender", [id, data, type]);
	    } else {
	      //full reset
	      if (this.callEvent("onBeforeRender", [this.data])) {
	        //will be used for lines management
	        this.type._tree_branch_render_state = []; //getTopRange - returns all elements on top level

	        this._dataobj.innerHTML = this._toHTMLLevel(0);
	        this._htmlmap = null; //clear map, it will be filled at first getItemNode

	        this.callEvent("onAfterRender", []);
	      }
	    } //clear after usage


	    this.type._tree_branch_render_state = 0;
	    TreeRenderStack._obj = null;
	    return true;
	  },
	  getItemNode: function (search_id) {
	    if (this._htmlmap) return this._htmlmap[search_id]; //fill map if it doesn't created yet

	    this._htmlmap = {};

	    var t = this._dataobj.getElementsByTagName("DIV");

	    for (var i = 0; i < t.length; i++) {
	      var id = t[i].getAttribute(this._id); //get item's

	      if (id) this._htmlmap[id] = t[i];
	    } //call locator again, when map is filled


	    return this.getItemNode(search_id);
	  },
	  _branch_render_supported: 1
	};

	var TreeStateCheckbox = {
	  _init_render_tree_state: function () {
	    if (this._branch_render_supported) {
	      var old_render = this.render;

	      this.render = function (id, data) {
	        var updated = old_render.apply(this, arguments);
	        if (this._settings.threeState && updated && data != "checkbox") this._setThirdState.apply(this, arguments);
	      };

	      this._init_render_tree_state = function () {};
	    }
	  },
	  threeState_setter: function (value) {
	    if (value) this._init_render_tree_state();
	    return value;
	  },
	  _setThirdState: function (id) {
	    var i, leaves, parents, checkedParents, tree;
	    parents = [];
	    tree = this;
	    /*if item was removed*/

	    if (id && !tree.data.pull[id]) {
	      id = 0;
	    }
	    /*sets checkbox states*/

	    /*if branch or full reloading*/


	    if (!id || tree.data.pull[id].$count) {
	      leaves = this._getAllLeaves(id);
	      leaves.sort(function (a, b) {
	        return tree.data.pull[b].$level - tree.data.pull[a].$level;
	      });

	      for (i = 0; i < leaves.length; i++) {
	        if (!i || tree.data.pull[leaves[i]].$parent != tree.data.pull[leaves[i - 1]].$parent) parents = parents.concat(tree._setParentThirdState(leaves[i]));
	      }
	    } else {
	      /*an item is a leaf */
	      parents = parents.concat(tree._setParentThirdState(id));
	    }

	    checkedParents = {};

	    for (i = 0; i < parents.length; i++) {
	      if (!checkedParents[parents[i]]) {
	        checkedParents[parents[i]] = 1;

	        this._setCheckboxIndeterminate(parents[i]);
	      }
	    }

	    tree = null;
	  },
	  _setCheckboxIndeterminate: function (id) {
	    var chElem, elem;
	    elem = this.getItemNode(id);

	    if (elem) {
	      this.render(id, "checkbox", "update");
	      /*needed to get the new input obj and to set indeterminate state*/

	      if (this.getItem(id).indeterminate) {
	        elem = this.getItemNode(id);
	        chElem = elem.getElementsByTagName("input")[0];
	        if (chElem) chElem.indeterminate = this.getItem(id).indeterminate;
	      }
	    }
	  },
	  _setParentThirdState: function (itemId) {
	    //we need to use dynamic function creating
	    //jshint -W083:true
	    var checked, checkedCount, indeterminate, parentId, result, unsureCount, needrender;
	    parentId = this.getParentId(itemId);
	    result = [];

	    while (parentId && parentId != "0") {
	      unsureCount = 0;
	      checkedCount = 0;
	      this.data.eachChild(parentId, function (obj) {
	        if (obj.indeterminate) {
	          unsureCount++;
	        } else if (obj.checked) {
	          checkedCount++;
	        }
	      });
	      checked = indeterminate = needrender = false;
	      var item = this.getItem(parentId);

	      if (checkedCount == item.$count) {
	        checked = true;
	      } else if (checkedCount > 0 || unsureCount > 0) {
	        indeterminate = true;
	      } //we need to reset indeterminate in any case :(


	      if (indeterminate || indeterminate != item.indeterminate) needrender = true;
	      item.indeterminate = indeterminate;
	      if (checked || item.checked != checked) needrender = true;
	      item.checked = checked;

	      if (needrender) {
	        result.push(parentId);
	        parentId = this.getParentId(parentId);
	      } else parentId = 0;
	    }

	    return result;
	  },

	  /*get all checked items in tree*/
	  getChecked: function () {
	    var result = [];
	    var tree = this;
	    this.data.eachSubItem(0, function (obj) {
	      if (tree.isChecked(obj.id)) result.push(obj.id);
	    });
	    return result;
	  },
	  _tree_check_uncheck_3: function (id, mode) {
	    var item = this.getItem(id);

	    if (item) {
	      if (mode === "") mode = !item.checked;

	      if (item.checked != mode || item.indeterminate) {
	        item.checked = mode;

	        this._correctThreeState(id);

	        var parents = this._setParentThirdState(id);

	        if (this._branch_render_supported && parents.length < 5) {
	          for (var i = 0; i < parents.length; i++) {
	            this._setCheckboxIndeterminate(parents[i]);
	          }
	        } else this.refresh();

	        this.callEvent("onItemCheck", [id, mode]);
	      }
	    }
	  },

	  /*set checked state for item checkbox*/
	  checkItem: function (id) {
	    this._tree_check_uncheck(id, true);

	    this.updateItem(id);
	  },

	  /*uncheckes an item checkbox*/
	  uncheckItem: function (id) {
	    this._tree_check_uncheck(id, false);

	    this.updateItem(id);
	  },
	  _checkUncheckAll: function (id, mode, all) {
	    var method = mode ? "checkItem" : "uncheckItem";
	    if (!id) id = 0;else this[method](id);

	    if (this._settings.threeState) {
	      if (!id) this.data.eachChild(0, function (item) {
	        this[method](item.id);
	      }, this, all);
	    } else this.data.each(function (item) {
	      this[method](item.id);
	    }, this, all, id);
	  },

	  /*checkes checkboxes of all items in a branch/tree*/
	  checkAll: function (id, all) {
	    this._checkUncheckAll(id, true, all);
	  },

	  /*uncheckes checkboxes of all items in a branch/tree*/
	  uncheckAll: function (id, all) {
	    this._checkUncheckAll(id, false, all);
	  },
	  _correctThreeState: function (id) {
	    var state;
	    var item = this.getItem(id);
	    item.indeterminate = false;
	    state = item.checked;
	    this.data.eachSubItem(id, function (child) {
	      child.indeterminate = false;
	      child.checked = state;
	    });

	    if (this._branch_render_supported && this.isBranchOpen(item.$parent)) {
	      //for tree-render only
	      this.render(id, 0, "branch");
	    }
	  },

	  /*returns checked state of item checkbox*/
	  isChecked: function (id) {
	    return this.getItem(id).checked;
	  },

	  /*gets all leaves in a certain branch (in the whole tree if id is not set)*/
	  _getAllLeaves: function (parentId) {
	    var result = [];
	    this.data.eachSubItem(parentId, function (obj, branch) {
	      if (!branch) result.push(obj.id);
	    });
	    return result;
	  }
	};

	// #include core/bind.js
	// #include core/treemove.js

	var TreeStore = {
	  name: "TreeStore",
	  $init: function () {
	    this._filterMode = {
	      //level:1,
	      showSubItems: true
	    };
	    this.branch = {
	      0: []
	    };
	    this.attachEvent("onParse", function (driver) {
	      this._set_child_scheme(driver.child);
	    });
	    this.attachEvent("onClearAll", bind(function () {
	      this._filter_branch = null;
	    }, this));
	  },
	  filterMode_setter: function (mode) {
	    return exports.extend(this._filterMode, mode, true);
	  },
	  _filter_reset: function (preserve) {
	    //remove previous filtering , if any
	    if (this._filter_branch && !preserve) {
	      this.branch = this._filter_branch;
	      this.order = toArray(copy(this.branch[0]));

	      for (var key in this.branch) {
	        if (key != "0") //exclude 0 - virtual root
	          this.getItem(key).$count = this.branch[key].length;
	      }

	      delete this._filter_branch;
	    }
	  },
	  _filter_core: function (filter, value, preserve, filterMode) {
	    //for tree we have few filtering options
	    //- filter leafs only
	    //- filter data on specific level
	    //- filter data on all levels
	    //- in all cases we can show or hide empty folder
	    //- in all cases we can show or hide childs for matched item
	    //set new order of items, store original
	    if (!preserve || !this._filter_branch) {
	      this._filter_branch = this.branch;
	      this.branch = clone(this.branch);
	    }

	    this.branch[0] = this._filter_branch_rec(filter, value, this.branch[0], 1, filterMode || {});
	  },
	  _filter_branch_rec: function (filter, value, branch, level, config) {
	    //jshint -W041
	    var neworder = [];
	    var allow = config.level && config.level != level;

	    for (var i = 0; i < branch.length; i++) {
	      var id = branch[i];
	      var item = this.getItem(id);
	      var child_run = false;
	      var sub = this.branch[id];

	      if (allow) {
	        child_run = true;
	      } else if (filter(this.getItem(id), value)) {
	        neworder.push(id); // open all parents of the found item

	        if (config.openParents !== false) {
	          var parentId = this.getParentId(id);

	          while (parentId && parentId != "0") {
	            this.getItem(parentId).open = 1;
	            parentId = this.getParentId(parentId);
	          }
	        } //in case of of fixed level filtering - do not change child-items


	        if (config.level || config.showSubItems) continue;
	      } else {
	        //filtering level, not match
	        child_run = true;
	      } //if "filter by all levels" - filter childs


	      if (allow || !config.level) {
	        if (sub) {
	          var newsub = this.branch[id] = this._filter_branch_rec(filter, value, sub, level + 1, config);

	          item.$count = newsub.length;
	          if (child_run && newsub.length) neworder.push(id);
	        }
	      }
	    }

	    return neworder;
	  },
	  count: function () {
	    if (this.order.length) return this.order.length; //we must return some non-zero value, or logic of selection will think that we have not data at all

	    var count = 0;
	    this.eachOpen(function () {
	      count++;
	    });
	    return count;
	  },
	  _change_branch_id: function (branches, parent, old, newid) {
	    if (branches[old]) {
	      var branch = branches[newid] = branches[old];

	      for (var i = 0; i < branch.length; i++) {
	        this.getItem(branch[i]).$parent = newid;
	      }

	      delete branches[old];
	    }

	    if (branches[parent]) {
	      var index = PowerArray.find.call(branches[parent], old);
	      if (index >= 0) branches[parent][index] = newid;
	    }
	  },
	  changeId: function (old, newid) {
	    if (old == newid) return;
	    var parent = this.getItem(old).$parent;

	    this._change_branch_id(this.branch, parent, old, newid); //in case of filter applied, update id in filtered state as well


	    if (this._filter_branch) this._change_branch_id(this._filter_branch, parent, old, newid);
	    return DataStore.prototype.changeId.call(this, old, newid);
	  },
	  clearAll: function (soft) {
	    this.branch = {
	      0: []
	    };
	    DataStore.prototype.clearAll.call(this, soft);
	  },
	  getPrevSiblingId: function (id) {
	    var order = this.branch[this.getItem(id).$parent];
	    var pos = PowerArray.find.call(order, id) - 1;
	    if (pos >= 0) return order[pos];
	    return null;
	  },
	  getNextSiblingId: function (id) {
	    var order = this.branch[this.getItem(id).$parent];
	    var pos = PowerArray.find.call(order, id) + 1;
	    if (pos < order.length) return order[pos];
	    return null;
	  },
	  getParentId: function (id) {
	    return this.getItem(id).$parent;
	  },
	  getFirstChildId: function (id) {
	    var order = this.branch[id];
	    if (order && order.length) return order[0];
	    return null;
	  },
	  isBranch: function (parent) {
	    return !!this.branch[parent];
	  },
	  getBranchIndex: function (child) {
	    var t = this.branch[this.pull[child].$parent];
	    return PowerArray.find.call(t, child);
	  },
	  _set_child_scheme: function (parse_name) {
	    if (typeof parse_name == "string") this._datadriver_child = function (obj) {
	      var t = obj[parse_name];
	      if (t) delete obj[parse_name];
	      return t;
	    };else this._datadriver_child = parse_name;
	  },
	  _inner_parse: function (info, recs) {
	    var parent = info.parent || 0;

	    for (var i = 0; i < recs.length; i++) {
	      //get hash of details for each record
	      var temp = this.driver.getDetails(recs[i]);
	      var id = this.id(temp); //generate ID for the record

	      var update = !!this.pull[id]; //update mode

	      if (update) {
	        temp = exports.extend(this.pull[id], temp, true);
	        if (this._scheme_update) this._scheme_update(temp);
	      } else {
	        if (this._scheme_init) this._scheme_init(temp);
	        this.pull[id] = temp;
	      }

	      this._extraParser(temp, parent, 0, update, info.from ? info.from * 1 + i : 0);
	    } //fix state of top item after data loading


	    var pItem = this.pull[parent] || {};
	    var pBranch = this.branch[parent] || [];
	    pItem.$count = pBranch.length;
	    delete pItem.webix_kids;
	    if (info.size && info.size != pBranch.length) pBranch[info.size - 1] = undefined;
	  },
	  _extraParser: function (obj, parent, level, update, from) {
	    //processing top item
	    obj.$count = 0; //using soft check, as parent can be a both 0 and "0" ( second one in case of loading from server side ) 

	    obj.$parent = parent != "0" ? parent : 0;
	    obj.$level = level || (parent != "0" ? this.pull[parent].$level + 1 : 1);
	    var parent_branch = this.branch[obj.$parent];
	    if (!parent_branch) parent_branch = this.branch[obj.$parent] = [];
	    if (this._filter_branch) this._filter_branch[obj.$parent] = parent_branch;

	    if (!update) {
	      var pos = from || parent_branch.length;
	      parent_branch[pos] = obj.id;
	    }

	    var child = this._datadriver_child(obj);

	    if (obj.webix_kids) {
	      return obj.$count = -1;
	    }

	    if (!child) //ignore childless
	      return obj.$count = 0; //when loading from xml we can have a single item instead of an array

	    if (!isArray(child)) child = [child];

	    for (var i = 0; i < child.length; i++) {
	      //extra processing to convert strings to objects
	      var item = DataDriver.json.getDetails(child[i]);
	      var itemid = this.id(item);
	      update = !!this.pull[itemid];

	      if (update) {
	        item = exports.extend(this.pull[itemid], item, true);
	        if (this._scheme_update) this._scheme_update(item);
	      } else {
	        if (this._scheme_init) this._scheme_init(item);
	        this.pull[itemid] = item;
	      }

	      this._extraParser(item, obj.id, obj.$level + 1, update);
	    } //processing childrens


	    var branch = this.branch[obj.id];
	    if (branch) obj.$count = branch.length;
	  },
	  _sync_to_order: function (master) {
	    this.order = toArray();

	    this._sync_each_child(0, master);
	  },
	  _sync_each_child: function (start, master) {
	    var branch = this.branch[start];

	    for (var i = 0; i < branch.length; i++) {
	      var id = branch[i];
	      this.order.push(id);
	      var item = this.pull[id];

	      if (item) {
	        if (item.open) {
	          if (item.$count == -1) master.loadBranch(id);else if (item.$count) this._sync_each_child(id, master);
	        }
	      }
	    }
	  },
	  provideApi: function (target, eventable) {
	    var list = ["getPrevSiblingId", "getNextSiblingId", "getParentId", "getFirstChildId", "isBranch", "getBranchIndex", "filterMode_setter"];

	    for (var i = 0; i < list.length; i++) {
	      target[list[i]] = this._methodPush(this, list[i]);
	    }

	    if (!target.getIndexById) DataStore.prototype.provideApi.call(this, target, eventable);
	  },
	  getTopRange: function () {
	    return toArray([].concat(this.branch[0])).map(function (id) {
	      return this.getItem(id);
	    }, this);
	  },
	  eachChild: function (id, functor, master, all) {
	    var branch = this.branch;
	    if (all && this._filter_branch) branch = this._filter_branch;
	    var stack = branch[id];
	    if (stack) for (var i = 0; i < stack.length; i++) {
	      if (stack[i]) functor.call(master || this, this.getItem(stack[i]));
	    }
	  },
	  each: function (method, master, all, id) {
	    this.eachChild(id || 0, function (item) {
	      var branch = this.branch;
	      method.call(master || this, item);
	      if (all && this._filter_branch) branch = this._filter_branch;
	      if (item && branch[item.id]) this.each(method, master, all, item.id);
	    }, this, all);
	  },
	  eachOpen: function (method, master, id) {
	    this.eachChild(id || 0, function (item) {
	      method.call(master || this, item);
	      if (this.branch[item.id] && item.open) this.eachOpen(method, master, item.id);
	    });
	  },
	  eachSubItem: function (id, functor) {
	    var top = this.branch[id || 0];
	    if (top) for (var i = 0; i < top.length; i++) {
	      var key = top[i];

	      if (this.branch[key]) {
	        functor.call(this, this.getItem(key), true);
	        this.eachSubItem(key, functor);
	      } else functor.call(this, this.getItem(key), false);
	    }
	  },
	  eachLeaf: function (id, functor) {
	    var top = this.branch[id || 0];
	    if (top) for (var i = 0; i < top.length; i++) {
	      var key = top[i];

	      if (this.branch[key]) {
	        this.eachLeaf(key, functor);
	      } else functor.call(this, this.getItem(key), false);
	    }
	  },
	  _sort_core: function (sort, order) {
	    var sorter = this.sorting.create(sort);

	    for (var key in this.branch) {
	      var bset = this.branch[key];
	      var data = [];

	      for (var i = 0; i < bset.length; i++) {
	        data.push(this.pull[bset[i]]);
	      }

	      data.sort(sorter);

	      for (var _i = 0; _i < bset.length; _i++) {
	        data[_i] = data[_i].id;
	      }

	      this.branch[key] = data;
	    }

	    return order;
	  },
	  add: function (obj, index, pid) {
	    var refresh_parent = false;
	    var parent = this.getItem(pid || 0);

	    if (parent) {
	      //when adding items to leaf item - it need to be repainted
	      if (!this.branch[parent.id]) refresh_parent = true;
	      parent.$count++; //fix for the adding into dynamic loading branch
	      //dynamic branch has $count as -1

	      if (!parent.$count) parent.$count = 1;
	    }

	    this.branch[pid || 0] = this.order = toArray(this.branch[pid || 0]);
	    obj.$count = obj.webix_kids ? -1 : 0;
	    obj.$level = parent ? parent.$level + 1 : 1;
	    obj.$parent = parent ? parent.id : 0;

	    if (this._filter_branch) {
	      //adding during filtering
	      var origin = this._filter_branch[pid || 0]; //newly created branch

	      if (!origin) origin = this._filter_branch[pid] = this.order; //branch can be shared bettwen collections, ignore such cases

	      if (this.order !== origin) {
	        //we can't know the location of new item in full dataset, making suggestion
	        //put at end by default
	        var original_index = origin.length; //put at start only if adding to the start and some data exists

	        if (!index && this.branch[pid || 0].length) original_index = 0;
	        origin = toArray(origin);
	        obj.id = obj.id || uid();
	        origin.insertAt(obj.id, original_index);
	      }
	    } //call original adding logic


	    var result = DataStore.prototype.add.call(this, obj, index);
	    if (refresh_parent) this.refresh(pid);
	    return result;
	  },
	  _rec_remove: function (id) {
	    var obj = this.pull[id];

	    if (this.branch[obj.id] && this.branch[obj.id].length > 0) {
	      var branch = this.branch[id];

	      for (var i = 0; i < branch.length; i++) {
	        this._rec_remove(branch[i], true);
	      }
	    }

	    delete this.branch[id];
	    if (this._filter_branch) delete this._filter_branch[id];
	    delete this.pull[id];
	    if (this._marks[id]) delete this._marks[id];
	  },
	  _filter_removed: function (pull, parentId, id) {
	    var branch = pull[parentId];

	    if (branch.length == 1 && branch[0] == id && parentId) {
	      delete pull[parentId];
	    } else toArray(branch).remove(id);
	  },
	  remove: function (id) {
	    //id can be an array of IDs - result of getSelect, for example
	    if (isArray(id)) {
	      for (var i = 0; i < id.length; i++) {
	        this.remove(id[i]);
	      }

	      return;
	    }

	    assert(this.exists(id), "Not existing ID in remove command" + id);
	    var obj = this.pull[id];
	    var parentId = obj.$parent || 0;
	    if (this.callEvent("onBeforeDelete", [id]) === false) return false;

	    this._rec_remove(id);

	    this.callEvent("onAfterDelete", [id]);
	    var parent = this.pull[parentId];

	    this._filter_removed(this.branch, parentId, id);

	    if (this._filter_branch) this._filter_removed(this._filter_branch, parentId, id);
	    var refresh_parent = 0;

	    if (parent) {
	      parent.$count--;

	      if (parent.$count <= 0) {
	        parent.$count = 0;
	        parent.open = 0;
	        refresh_parent = 1;
	      }
	    } //repaint signal


	    this.callEvent("onStoreUpdated", [id, obj, "delete"]);
	    if (refresh_parent) this.refresh(parent.id);
	  },

	  /*
	  	serializes data to a json object
	  */
	  getBranch: function (id) {
	    var out = [];
	    var items = (this._filter_branch || this.branch)[id];
	    if (items) for (var i = 0; i < items.length; i++) {
	      out[i] = this.pull[items[i]];
	    }
	    return out;
	  },
	  serialize: function (id, all) {
	    var coll = this.branch; //use original collection of branches

	    if (all && this._filter_branch) coll = this._filter_branch;
	    var ids = coll[id || 0];
	    var result = [];

	    for (var i = 0; i < ids.length; i++) {
	      var obj = this.pull[ids[i]];
	      var rel;

	      if (this._scheme_serialize) {
	        rel = this._scheme_serialize(obj);
	        if (rel === false) continue;
	      } else rel = copy(obj);

	      if (coll[obj.id]) rel.data = this.serialize(obj.id, all);
	      result.push(rel);
	    }

	    return result;
	  }
	};

	var TreeTablePaste = {
	  insert: function (data) {
	    var parent = this.getSelectedId(true, true);

	    for (var i = 0; i < data.length; i++) {
	      var item = {};

	      for (var j = 0; j < this._settings.columns.length; j++) {
	        item[this._settings.columns[j].id] = data[i][j] || "";
	      }

	      if (!isUndefined(item.id) && this.exists(item.id)) item.id = uid();
	      this.add(item, null, parent[0]);
	    }
	  }
	};

	var TreeType = {
	  space: function (obj) {
	    var html = "";

	    for (var i = 1; i < obj.$level; i++) {
	      html += "<div class='webix_tree_none'></div>";
	    }

	    return html;
	  },
	  icon: function (obj) {
	    if (obj.$count) {
	      if (obj.open) return "<div class='webix_tree_open'></div>";else return "<div class='webix_tree_close'></div>";
	    } else return "<div class='webix_tree_none'></div>";
	  },
	  checkbox: function (obj) {
	    if (obj.nocheckbox) return "";
	    return "<input type='checkbox' class='webix_tree_checkbox' " + (obj.checked ? "checked" : "") + (obj.disabled ? " disabled" : "") + ">";
	  },
	  folder: function (obj) {
	    if (obj.icon) return "<div class='webix_tree_file webix_tree_" + obj.icon + "'></div>";

	    if (obj.$count) {
	      if (obj.open) return "<div class='webix_tree_folder_open'></div>";else return "<div class='webix_tree_folder'></div>";
	    }

	    return "<div class='webix_tree_file'></div>";
	  }
	};

	var UploadDriver = {
	  flash: {
	    $render: function () {
	      if (!window.swfobject) require("legacy/swfobject.js", true); // sync loading

	      var config = this._settings;
	      config.swfId = config.swfId || "webix_swf_" + uid();
	      this._getBox().innerHTML += "<div class='webix_upload_flash'><div id='" + config.swfId + "'></div></div>";
	      this._upload_area = this._getBox().lastChild; // add swf object

	      /* global swfobject */

	      swfobject.embedSWF(env.codebase + "/legacy/uploader.swf", config.swfId, "100%", "100%", "9", null, {
	        uploaderId: config.id,
	        ID: config.swfId,
	        enableLogs: config.enableLogs ? "1" : "",
	        paramName: config.inputName,
	        multiple: config.multiple ? "Y" : ""
	      }, {
	        wmode: "transparent"
	      });

	      _event(this._viewobj, "click", bind(function () {
	        var now_date = new Date();

	        if (now_date - (this._upload_timer_click || 0) > 250) {
	          this.fileDialog();
	        }
	      }, this));

	      this.files.attachEvent("onBeforeDelete", bind(this._stop_file, this));
	    },
	    $applyFlash: function (name, params) {
	      return this[name].apply(this, params);
	    },
	    getSwfObject: function () {
	      /* global swfobject */
	      return swfobject.getObjectById(this._settings.swfId);
	    },
	    fileDialog: function () {
	      if (this.getSwfObject()) this.getSwfObject().showDialog();
	    },
	    send: function (id) {
	      if (typeof id == "function") {
	        this._last_assigned_upload_callback = id;
	        id = 0;
	      }

	      if (!id) {
	        var order = this.files.data.order;
	        var complete = true;
	        if (order.length) for (var i = 0; i < order.length; i++) {
	          complete = this.send(order[i]) && complete;
	        }
	        if (complete) this._upload_complete();
	        return;
	      }

	      var item = this.files.getItem(id);
	      if (item.status !== "client") return false;
	      item.status = "transfer";

	      if (this.getSwfObject()) {
	        var url = this._get_active_url(item);

	        var globalData = this._settings.formData || {};
	        if (typeof globalData === "function") globalData = globalData.call(this);
	        var details = exports.extend(item.formData || {}, globalData);
	        this.getSwfObject().upload(id, url, details);
	      }

	      return true;
	    },
	    $beforeAddFileToQueue: function (id, name, size) {
	      var type = name.split(".").pop();

	      var format = this._format_size(size);

	      return this.callEvent("onBeforeFileAdd", [{
	        id: id,
	        name: name,
	        size: size,
	        sizetext: format,
	        type: type
	      }]);
	    },
	    $addFileToQueue: function (id, name, size) {
	      if (this.files.exists(id)) return false;
	      if (!this._settings.multiple) this.files.clearAll();
	      var type = name.split(".").pop();

	      var format = this._format_size(size);

	      var file_struct = {
	        name: name,
	        id: id,
	        size: size,
	        sizetext: format,
	        type: type,
	        status: "client"
	      };
	      this.files.add(file_struct);
	      this.callEvent("onAfterFileAdd", [file_struct]);
	      if (id && this._settings.autosend) this.send(id);
	    },
	    stopUpload: function (id) {
	      this._stop_file(id);
	    },
	    _stop_file: function (id) {
	      var item = this.files.getItem(id);

	      if (item.status == "transfer") {
	        this.getSwfObject().uploadStop(id);
	        item.status = "client";
	      }
	    },
	    _drop: function () {},
	    //drop of files is not supported in IE9-
	    $onUploadComplete: function () {
	      if (this._settings.autosend) {
	        this._upload_complete();
	      }
	    },
	    $onUploadSuccess: function (id, name, response) {
	      var item = this.files.getItem(id);

	      if (item) {
	        item.status = "server";
	        item.progress = 100;

	        if (response.text && typeof response.text == "string") {
	          DataDriver.json.toObject(response.text);
	          exports.extend(item, response, true);
	        }

	        this.callEvent("onFileUpload", [item, response]);
	        this.callEvent("onChange", []);
	        this.files.updateItem(id);
	      }
	    },
	    $onUploadFail: function (id) {
	      var item = this.files.getItem(id);
	      item.status = "error";
	      delete item.percent;
	      this.files.updateItem(id);
	      this.callEvent("onFileUploadError", [item, ""]);
	    }
	  },
	  html5: {
	    $render: function () {
	      if (this._upload_area) {
	        //firstChild is webix_el_box container, which have relative position
	        //as result, file control is placed under the button and not in the top corner
	        this._contentobj.firstChild.appendChild(this._upload_area);

	        return;
	      }

	      this.files.attachEvent("onBeforeDelete", this._stop_file);
	      var input_config = {
	        "type": "file",
	        "class": "webix_hidden_upload",
	        tabindex: -1
	      };
	      if (this._settings.accept) input_config.accept = this._settings.accept;
	      if (this._settings.multiple) input_config.multiple = "true";

	      if (this._settings.directory) {
	        input_config.webkitdirectory = "true";
	        input_config.mozdirectory = "true";
	        input_config.directory = "true";
	      }

	      var f = create("input", input_config);
	      this._upload_area = this._contentobj.firstChild.appendChild(f);

	      _event(this._viewobj, "drop", bind(function (e) {
	        this._drop(e);

	        preventEvent(e);
	      }, this));

	      _event(f, "change", bind(function () {
	        this._add_files(f.files);

	        if (env.isIE) {
	          var t = document.createElement("form");
	          t.appendChild(this._upload_area);
	          t.reset();

	          this._contentobj.firstChild.appendChild(f);
	        } else f.value = "";
	      }, this));

	      _event(this._viewobj, "click", bind(function () {
	        var now_date = new Date();

	        if (now_date - (this._upload_timer_click || 0) > 250) {
	          this.fileDialog();
	        }
	      }, this));

	      _event(this._viewobj, "dragenter", preventEvent);

	      _event(this._viewobj, "dragexit", preventEvent);

	      _event(this._viewobj, "dragover", preventEvent);
	    },
	    _directoryEntry: function (value) {
	      return value.isDirectory;
	    },
	    _directoryDrop: function (item, state, path) {
	      if (item.isFile) {
	        item.file(function (file) {
	          state.addFile(file, null, null, {
	            name: path + "/" + file.name
	          });
	        });
	      } else if (item.isDirectory) {
	        // Get folder contents
	        var dirReader = item.createReader();
	        dirReader.readEntries(function (entries) {
	          for (var i = 0; i < entries.length; i++) {
	            state._directoryDrop(entries[i], state, (path ? path + "/" : "") + item.name);
	          }
	        });
	      }
	    },
	    // adding files by drag-n-drop
	    _drop: function (e) {
	      var files = e.dataTransfer.files;
	      var items = e.dataTransfer.items;

	      if (this.callEvent("onBeforeFileDrop", [files, e])) {
	        items = items || files; //IE10+

	        for (var i = 0; i < items.length; i++) {
	          //https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem/webkitGetAsEntry
	          var item = items[i];

	          if (item.webkitGetAsEntry) {
	            item = item.webkitGetAsEntry();

	            if (item.isDirectory) {
	              this._directoryDrop(item, this, "");

	              continue;
	            }
	          }

	          this.addFile(files[i]);
	        }
	      }

	      this.callEvent("onAfterFileDrop", [files, e]);
	    },
	    fileDialog: function (context) {
	      this._upload_timer_click = new Date();
	      this._last_file_context = context;

	      var inputs = this._viewobj.getElementsByTagName("INPUT");

	      inputs[inputs.length - 1].click();
	    },
	    send: function (id) {
	      //alternative syntx send(callback)
	      if (typeof id == "function") {
	        this._last_assigned_upload_callback = id;
	        id = 0;
	      }

	      if (!id) {
	        var order = this.files.data.order;
	        var complete = true;
	        if (order.length) for (var i = 0; i < order.length; i++) {
	          complete = !this.send(order[i]) && complete;
	        }
	        if (complete) this._upload_complete();
	        return;
	      }

	      var item = this.files.getItem(id);
	      if (item.status !== "client") return false;
	      assert(this._settings.upload, "You need to define upload url for uploader component");
	      item.status = "transfer";
	      var formData = new FormData();
	      formData.append(this.config.inputName, item.file, item.name);
	      formData.append(this.config.inputName + "_fullpath", item.name);
	      var headers = {};
	      var globalData = this._settings.formData || {};
	      if (typeof globalData === "function") globalData = globalData.call(this);
	      var details = exports.extend(item.formData || {}, globalData);
	      var xhr = new XMLHttpRequest();

	      var url = this._get_active_url(item);

	      if (callEvent("onBeforeAjax", ["POST", url, details, xhr, headers, formData])) {
	        for (var key in details) {
	          formData.append(key, details[key]);
	        }

	        item.xhr = xhr;
	        xhr.upload.addEventListener("progress", bind(function (e) {
	          this.$updateProgress(id, e.loaded / e.total * 100);
	        }, this), false);
	        xhr.onload = bind(function () {
	          if (!xhr.aborted) this._file_complete(id);
	        }, this);
	        xhr.open("POST", url, true);

	        for (var _key in headers) {
	          xhr.setRequestHeader(_key, headers[_key]);
	        }

	        xhr.send(formData);
	      }

	      this.$updateProgress(id, 0);
	      return true;
	    },
	    _file_complete: function (id) {
	      var item = this.files.getItem(id);

	      if (item) {
	        var response = null;

	        if (item.xhr.status < 400) {
	          var driver = DataDriver[this._settings.datatype || "json"];
	          response = driver.toObject(item.xhr.responseText);
	          if (response) response = driver.getDetails(response);
	        }

	        if (!response || response.status == "error") {
	          // file upload error
	          item.status = "error";
	          delete item.percent;
	          this.files.updateItem(id);
	          this.callEvent("onFileUploadError", [item, response]);
	        } else {
	          // file upload complete
	          assert(!response.status || response.status == "server", "Not supported status value, use 'error' or 'server'");

	          this._complete(id, response);
	        }

	        delete item.xhr;
	      }
	    },
	    stopUpload: function (id) {
	      bind(this._stop_file, this.files)(id);
	    },
	    _stop_file: function (id) {
	      var item = this.getItem(id);

	      if (typeof item.xhr !== "undefined") {
	        item.xhr.aborted = true;
	        item.xhr.abort();
	        delete item.xhr;
	        item.status = "client";
	      }
	    }
	  }
	};

	var ValidateCollection = {
	  _validate_init_once: function () {
	    this.data.attachEvent("onStoreUpdated", bind(function (id, data, mode) {
	      if (id && (mode == "add" || mode == "update")) this.validate(id);
	    }, this));
	    this.data.attachEvent("onClearAll", bind(this.clearValidation, this));

	    this._validate_init_once = function () {};
	  },
	  rules_setter: function (value) {
	    if (value) {
	      this._validate_init_once();
	    }

	    return value;
	  },
	  clearValidation: function () {
	    this.data.clearMark("webix_invalid", true);
	  },
	  validate: function (id) {
	    var result = true;
	    if (!id) for (var key in this.data.pull) {
	      result = this.validate(key) && result;
	    } else {
	      this._validate_details = {};
	      var obj = this.getItem(id);
	      result = ValidateData.validate.call(this, null, obj);

	      if (result) {
	        if (this.callEvent("onValidationSuccess", [id, obj])) this._clear_invalid(id);
	      } else {
	        if (this.callEvent("onValidationError", [id, obj, this._validate_details])) this._mark_invalid(id, this._validate_details);
	      }
	    }
	    return result;
	  },
	  _validate: function (rule, data, obj, key) {
	    if (typeof rule == "string") rule = rules[rule];
	    var res = rule.call(this, data, obj, key);

	    if (!res) {
	      this._validate_details[key] = true;
	    }

	    return res;
	  },
	  _clear_invalid: function (id) {
	    this.data.removeMark(id, "webix_invalid", true);
	  },
	  _mark_invalid: function (id) {
	    this.data.addMark(id, "webix_invalid", true);
	  }
	};

	var Values = {
	  $init: function () {
	    this.elements = {};
	  },
	  focus: function (name) {
	    if (name) {
	      assert(this.elements[name], "unknown input name: " + name);

	      this._focus(this.elements[name]);
	    } else {
	      for (var n in this.elements) {
	        if (this._focus(this.elements[n]) !== false) return true;
	      }
	    }

	    return false;
	  },
	  _focus: function (target) {
	    if (target && target.focus) {
	      return target.focus();
	    }

	    return false;
	  },
	  setValues: function (data, update) {
	    var _this = this;

	    if (this._settings.complexData) data = CodeParser.collapseNames(data, "", {}, function (v) {
	      return isUndefined(_this._values[v]);
	    });

	    this._inner_setValues(data, update);
	  },
	  _inner_setValues: function (data, update) {
	    this._is_form_dirty = update; //prevent onChange calls from separate controls

	    this.blockEvent();
	    if (!update || !this._values) this._values = {};

	    for (var name in data) {
	      if (!this.elements[name]) this._values[name] = data[name];
	    }

	    for (var _name in this.elements) {
	      var input = this.elements[_name];

	      if (input) {
	        if (!isUndefined(data[_name])) input.setValue(data[_name]);else if (!update && input.$allowsClear) input.setValue("");
	        this._values[_name] = input.getValue();
	      }
	    }

	    this.unblockEvent();
	    this.callEvent("onValues", []);
	  },
	  isDirty: function () {
	    if (this._is_form_dirty) return true;
	    if (this.getDirtyValues(1) === 1) return true;
	    return false;
	  },
	  setDirty: function (flag) {
	    this._is_form_dirty = flag;
	    if (!flag) this._values = this._inner_getValues();
	  },
	  getDirtyValues: function () {
	    var result = {};

	    if (this._values) {
	      for (var name in this.elements) {
	        var value = this.elements[name].getValue();

	        if (this._values[name] != value) {
	          result[name] = value; //FIXME - used by isDirty

	          if (arguments[0]) return 1;
	        }
	      }
	    }

	    return result;
	  },
	  getCleanValues: function () {
	    return this._values;
	  },
	  getValues: function (filter) {
	    var data = this._inner_getValues(filter);

	    if (this._settings.complexData) data = CodeParser.expandNames(data);
	    return data;
	  },
	  _inner_getValues: function (filter) {
	    //get original data		
	    var success,
	        elem = null,
	        data = this._values ? copy(this._values) : {}; //update properties from linked controls

	    for (var name in this.elements) {
	      elem = this.elements[name];
	      success = true;

	      if (filter) {
	        if (_typeof(filter) == "object") {
	          if (filter.hidden === false) success = elem.isVisible();
	          if (success && filter.disabled === false) success = elem.isEnabled();
	        } else success = filter.call(this, elem);
	      }

	      if (success) data[name] = elem.getValue();else delete data[name]; //in case of this._values[name]
	    }

	    return data;
	  },
	  clear: function () {
	    this._is_form_dirty = false;
	    var data = {};

	    for (var name in this.elements) {
	      if (this.elements[name].$allowsClear) data[name] = "";
	    }

	    this._inner_setValues(data);
	  },
	  markInvalid: function (name, state) {
	    // remove 'invalid' mark
	    if (state === false) {
	      this._clear_invalid(name);
	    } // add 'invalid' mark
	    else {
	        // set invalidMessage
	        if (typeof state == "string") {
	          var input = this.elements[name];
	          if (input) input._settings.invalidMessage = state;
	        } //add mark to current validation process


	        if (this._validate_details) this._validate_details[name] = true;

	        this._mark_invalid(name);
	      }
	  },
	  _mark_invalid: function (id) {
	    var input = this.elements[id];

	    if (id && input) {
	      this._clear_invalid(id, true);

	      addCss(input._viewobj, "webix_invalid");
	      input._settings.invalid = true;
	      var message = input._settings.invalidMessage;
	      if (typeof message === "string" && input.setBottomText) input.setBottomText();
	    }
	  },
	  _clear_invalid: function (id, silent) {
	    var input = this.elements[id];

	    if (id && input && input.$view && input._settings.invalid) {
	      removeCss(input._viewobj, "webix_invalid");
	      input._settings.invalid = false;
	      var message = input._settings.invalidMessage;
	      if (typeof message === "string" && !silent && input.setBottomText) input.setBottomText();
	    }
	  }
	};

	/*
		Renders collection of items
		Always shows y-scroll
		Can be used with huge datasets
		
		@export
			show
			render
	*/

	var VirtualRenderStack = {
	  $init: function () {
	    assert(this.render, "VirtualRenderStack :: Object must use RenderStack first");
	    this._htmlmap = {}; //init map of rendered elements
	    //we need to repaint area each time when view resized or scrolling state is changed

	    _event(this._viewobj, "scroll", bind(this._render_visible_rows, this));

	    if (env.touch) {
	      this.attachEvent("onAfterScroll", bind(this._render_visible_rows, this));
	    } //here we store IDs of elemenst which doesn't loadede yet, but need to be rendered


	    this._unrendered_area = [];
	  },
	  //return html object by item's ID. Can return null for not-rendering element
	  getItemNode: function (search_id) {
	    //collection was filled in _render_visible_rows
	    return this._htmlmap[search_id];
	  },
	  //adjust scrolls to make item visible
	  showItem: function (id) {
	    var range = this._getVisibleRange();

	    var ind = this.data.getIndexById(id); //we can't use DOM method for not-rendered-yet items, so fallback to pure math

	    var dy = Math.floor(ind / range._dx) * range._y;

	    var state = this.getScrollState();
	    if (dy < state.y || dy + this._settings.height >= state.y + this._content_height) this.scrollTo(0, dy);
	  },
	  //repain self after changes in DOM
	  //for add, delete, move operations - render is delayed, to minify performance impact
	  render: function (id, data, type) {
	    if (!this.isVisible(this._settings.id) || this.$blockRender) return;

	    if (id) {
	      var cont = this.getItemNode(id); //old html element

	      switch (type) {
	        case "update":
	          if (!cont) return; //replace old with new

	          var t = this._htmlmap[id] = this._toHTMLObject(data);

	          insertBefore(t, cont);
	          remove(cont);
	          break;

	        default:
	          // "move", "add", "delete"

	          /*
	          	for all above operations, full repainting is necessary
	          	but from practical point of view, we need only one repainting per thread
	          	code below initiates double-thread-rendering trick
	          */
	          this._render_delayed();

	          break;
	      }
	    } else {
	      //full repainting
	      if (this.callEvent("onBeforeRender", [this.data])) {
	        this._htmlmap = {}; //nulify links to already rendered elements

	        this._render_visible_rows(null, true); // clear delayed-rendering, because we already have repaint view


	        this._wait_for_render = false;
	        this.callEvent("onAfterRender", []);
	      }
	    }
	  },
	  //implement double-thread-rendering pattern
	  _render_delayed: function () {
	    //this flag can be reset from outside, to prevent actual rendering 
	    if (this._wait_for_render) return;
	    this._wait_for_render = true;
	    window.setTimeout(bind(function () {
	      this.render();
	    }, this), 1);
	  },
	  //create empty placeholders, which will take space before rendering
	  _create_placeholder: function (height) {
	    if (env.maxHTMLElementSize) height = Math.min(env.maxHTMLElementSize, height);
	    var node = document.createElement("DIV");
	    node.style.cssText = "height:" + height + "px; width:100%; overflow:hidden;";
	    return node;
	  },

	  /*
	  	Methods get coordinatest of visible area and checks that all related items are rendered
	  	If, during rendering, some not-loaded items was detected - extra data loading is initiated.
	  	reset - flag, which forces clearing of previously rendered elements
	  */
	  _render_visible_rows: function (e, reset) {
	    this._unrendered_area = []; //clear results of previous calls

	    var viewport = this._getVisibleRange(); //details of visible view


	    if (!this._dataobj.firstChild || reset) {
	      //create initial placeholder - for all view space
	      this._dataobj.innerHTML = "";

	      this._dataobj.appendChild(this._create_placeholder(viewport._max)); //register placeholder in collection


	      this._htmlrows = [this._dataobj.firstChild];
	    }
	    /*
	    	virtual rendering breaks all view on rows, because we know widht of item
	    	we can calculate how much items can be placed on single row, and knowledge 
	    	of that, allows to calculate count of such rows
	    	
	    	each time after scrolling, code iterate through visible rows and render items 
	    	in them, if they are not rendered yet
	    	
	    	both rendered rows and placeholders are registered in _htmlrows collection
	    */
	    //position of first visible row


	    var t = viewport._from;

	    while (t <= viewport._height) {
	      //loop for all visible rows
	      //skip already rendered rows
	      while (this._htmlrows[t] && this._htmlrows[t]._filled && t <= viewport._height) {
	        t++;
	      } //go out if all is rendered


	      if (t > viewport._height) break; //locate nearest placeholder

	      var holder = t;

	      while (!this._htmlrows[holder]) {
	        holder--;
	      }

	      var holder_row = this._htmlrows[holder]; //render elements in the row			

	      var base = t * viewport._dx + (this.data.$min || 0); //index of rendered item

	      if (base > (this.data.$max || Infinity)) break; //check that row is in virtual bounds, defined by paging

	      var nextpoint = Math.min(base + viewport._dx - 1, this.data.$max ? this.data.$max - 1 : Infinity);

	      var node = this._create_placeholder(viewport._y); //all items in rendered row


	      var range = this.data.getIndexRange(base, nextpoint);
	      if (!range.length) break;
	      var loading = {
	        $template: "Loading"
	      };

	      for (var i = 0; i < range.length; i++) {
	        if (!range[i]) this._unrendered_area.push(base + i);
	        range[i] = this._toHTML(range[i] || loading);
	      }

	      node.innerHTML = range.join(""); //actual rendering

	      for (var _i = 0; _i < range.length; _i++) {
	        //register all new elements for later usage in getItemNode
	        this._htmlmap[this.data.getIdByIndex(base + _i)] = node.childNodes[_i];
	      } //correct placeholders


	      var h = parseFloat(holder_row.style.height, 10);
	      var delta = (t - holder) * viewport._y;
	      var delta2 = h - delta - viewport._y; //add new row to the DOOM

	      insertBefore(node, delta ? holder_row.nextSibling : holder_row, this._dataobj);
	      this._htmlrows[t] = node;
	      node._filled = true;
	      /*
	      	if new row is at start of placeholder - decrease placeholder's height
	      	else if new row takes whole placeholder - remove placeholder from DOM
	      	else 
	      		we are inserting row in the middle of existing placeholder
	      		decrease height of existing one, and add one more, 
	      		before the newly added row
	      */

	      if (delta <= 0 && delta2 > 0) {
	        holder_row.style.height = delta2 + "px";
	        this._htmlrows[t + 1] = holder_row;
	      } else {
	        if (delta < 0) remove(holder_row);else holder_row.style.height = delta + "px";

	        if (delta2 > 0) {
	          var new_space = this._htmlrows[t + 1] = this._create_placeholder(delta2);

	          insertBefore(new_space, node.nextSibling, this._dataobj);
	        }
	      }

	      t++;
	    } //when all done, check for non-loaded items


	    if (this._unrendered_area.length) {
	      //we have some data to load
	      //detect borders
	      var from = this._unrendered_area[0];
	      var to = this._unrendered_area.pop() + 1;

	      if (to > from) {
	        //initiate data loading
	        var count = to - from;
	        if (this._maybe_loading_already(count, from)) return;
	        count = Math.max(count, this._settings.datafetch || this._settings.loadahead || 0);
	        this.loadNext(count, from);
	      }
	    }
	  },
	  //calculates visible view
	  _getVisibleRange: function () {
	    var state = this.getScrollState();
	    var top = Math.max(0, state.y);
	    var width = this._content_width;
	    var height = this._content_height; //size of single item

	    var t = this.type;
	    var dx = Math.floor(width / t.width) || 1; //at least single item per row

	    var min = Math.floor(top / t.height); //index of first visible row

	    var dy = Math.ceil((height + top) / t.height) - 1; //index of last visible row
	    //total count of items, paging can affect this math

	    var count = this.data.$max ? this.data.$max - this.data.$min : this.data.count();
	    var max = Math.ceil(count / dx) * t.height; //size of view in rows

	    return {
	      _from: min,
	      _height: dy,
	      _top: top,
	      _max: max,
	      _y: t.height,
	      _dx: dx
	    };
	  },
	  _cellPosition: function (id) {
	    var html = this.getItemNode(id);

	    if (!html) {
	      this.showItem(id);

	      this._render_visible_rows();

	      html = this.getItemNode(id);
	    }

	    return {
	      left: html.offsetLeft,
	      top: html.offsetTop,
	      height: html.offsetHeight,
	      width: html.offsetWidth,
	      parent: this._contentobj
	    };
	  }
	};

	/*
		Renders collection of items on demand
	*/

	var VRenderStack = {
	  $init: function () {
	    this._htmlmap = {};

	    if (Touch.$active) {
	      this.attachEvent("onBeforeScroll", function () {
	        this._in_touch_scroll = true;
	      });
	      this.attachEvent("onAfterScroll", function () {
	        this.render(null, null, "paint");
	        this._in_touch_scroll = false;
	      });
	      this.attachEvent("onTouchMove", function () {
	        if (this._in_touch_scroll) {
	          this.blockEvent();
	          this.render(null, null, "paint");
	          this.unblockEvent();
	        }
	      });
	    } else {
	      _event(this._viewobj, "scroll", bind(function () {
	        this.render(null, null, "paint");
	      }, this));
	    }
	  },
	  _sync_scroll: function (x, y, t) {
	    if (this._settings.footer) Touch._set_matrix(this._footer.childNodes[1].firstChild, x, 0, t);
	    this.callEvent("onSyncScroll", [x, y, t]);
	  },
	  //return html container by its ID
	  //can return undefined if container doesn't exists
	  getItemNode: function (search_id) {
	    return this._htmlmap && this._htmlmap[search_id];
	  },

	  /*change scrolling state of top level container, so related item will be in visible part*/
	  showItem: function (id) {
	    var index$$1 = this.data.getIndexById(id);

	    if (index$$1 > -1) {
	      var top = index$$1 * this.type.height;
	      var bottom = top + this.type.height;
	      var scroll = this.getScrollState();
	      var box = offset(this.$view);
	      if (top < scroll.y) this.scrollTo(0, top);else if (bottom > scroll.y + box.height) this.scrollTo(0, bottom - box.height);
	    }
	  },
	  //update view after data update
	  //when called without parameters - all view refreshed
	  render: function (id, data, type) {
	    if (!this.isVisible(this._settings.id) || this.$blockRender) return;
	    var parent = this._renderobj || this._dataobj;

	    if (id) {
	      if (type == "paint" || type == "update") {
	        var cont = this.getItemNode(id); //get html element of updated item

	        if (cont) {
	          var t = this._htmlmap[id] = this._toHTMLObject(data);

	          t.style.top = cont.style.top;
	          t.style.position = "absolute";
	          t.style.left = 0;
	          t.style.width = "100%";
	          insertBefore(t, cont);
	          remove(cont);
	          return;
	        } //updating not rendered yet item


	        return;
	      }
	    }

	    if (type != "paint") {
	      //repaint all
	      this._htmlmap = {};
	      parent.innerHTML = "";
	    } //full reset


	    if (this.callEvent("onBeforeRender", [this.data])) {
	      var count = this.data.count();
	      var scroll = this.getScrollState();
	      var box = offset(this._viewobj);
	      var top = Math.floor(scroll.y / this.type.height) - 2;
	      var bottom = Math.ceil((scroll.y + box.height) / this.type.height) + 2;
	      top = Math.max(0, top);
	      bottom = Math.min(this.data.count() - 1, bottom);
	      var html = [];

	      for (var i = top; i <= bottom; i++) {
	        var sid = this.data.order[i];

	        if (!this._htmlmap[sid]) {
	          var item = this.data.getItem(sid);

	          if (!item) {
	            this._run_load_next({
	              count: bottom - i + (this._settings.loadahead || 0),
	              start: i
	            });

	            break;
	          }

	          html.push(this._toHTML(item));
	        } else {
	          html.push("<div></div>");
	        }
	      }

	      this._html.innerHTML = html.join("");
	      parent.style.position = "relative";
	      parent.style.height = count * this.type.height + "px";
	      var kids = this._html.childNodes;

	      for (var _i = kids.length - 1; _i >= 0; _i--) {
	        var child = kids[_i];
	        var cid = child.getAttribute(this._id);

	        if (cid) {
	          child.style.position = "absolute";
	          child.style.top = (top + _i) * this.type.height + "px";
	          child.style.left = 0;
	          child.style.width = "100%";
	          parent.appendChild(child);
	          this._htmlmap[cid] = child;
	        }
	      }

	      this.callEvent("onAfterRender", []);
	    }
	  },
	  $setSize: function () {
	    if (base.api.$setSize.apply(this, arguments)) {
	      this.render(null, null, "paint");
	    }
	  },
	  _run_load_next: function (conf) {
	    var count = Math.max(conf.count, this._settings.datafetch || this._settings.loadahead || 0);
	    if (this._maybe_loading_already(conf.count, conf.start)) return;
	    this.loadNext(count, conf.start);
	  }
	};

	var version$1 = "6.3.1";
	var name$1 = "core";

	var errorMessage = "non-existing view for export";

	function getDataHelper(key, column, raw) {
	  if (!raw && column.format) return function (obj) {
	    return column.format(obj[key]);
	  };
	  return function (obj) {
	    return obj[key];
	  };
	}

	function getExportScheme(view, options) {
	  var scheme = [];
	  var h_count = 0,
	      f_count = 0;
	  var isTable = view.getColumnConfig;
	  var columns = options.columns;
	  var raw = !!options.rawValues;
	  var isTree = view.data.name == "TreeStore";
	  var treeLines = options.treeLines;
	  if (treeLines === true || isUndefined(treeLines)) treeLines = "value";
	  scheme.heights = {};

	  if (!columns) {
	    if (isTable) columns = [].concat(view._columns);else {
	      columns = [];
	      var obj = view.data.pull[view.data.order[0]];

	      for (var key in obj) {
	        if (key !== "id" && key[0] != "$") columns.push({
	          id: key,
	          isTree: isTree && key === treeLines
	        });
	      }
	    }
	  } else if (!columns.length) {
	    //export options are set as - columns:{ rank:true, title:{ header:"custom"}}
	    var arr = [];

	    for (var _key in columns) {
	      arr.push(exports.extend({
	        id: _key
	      }, exports.extend({}, columns[_key])));
	    }

	    columns = arr;
	  }

	  if (options.ignore) for (var i = columns.length - 1; i >= 0; i--) {
	    if (options.ignore[columns[i].id]) columns.splice(i, 1);
	  }
	  if (options.id) scheme.push({
	    id: "id",
	    width: 50,
	    header: " ",
	    template: function (obj) {
	      return obj.id;
	    }
	  });

	  if (options.flatTree) {
	    var flatKey = options.flatTree.id;
	    var copy$$1 = [].concat(options.flatTree.columns);
	    var fill = [];
	    var fillMode = !!options.flatTree.fill;

	    for (var _i = 1; _i <= copy$$1.length; _i++) {
	      copy$$1[_i - 1].template = function (i) {
	        return function (obj) {
	          return obj.$level == i ? fill[i] = obj[flatKey] : fillMode && i < obj.$level ? fill[i] : "";
	        };
	      }(_i);
	    }

	    var index = 0;

	    for (var _i2 = columns.length - 1; _i2 >= 0; _i2--) {
	      if (columns[_i2].id === flatKey) index = _i2;
	    }

	    columns = [].concat(columns.slice(0, index)).concat(copy$$1).concat(columns.slice(index + 1));
	  }

	  var treeColumn;

	  for (var j = 0; j < columns.length; j++) {
	    var column = columns[j];
	    var _key2 = column.id;
	    if (column.noExport) continue; // raw mode has sense only for datatable
	    // in other cases we don't have built-in data templates

	    var rawColumn = raw && isTable;

	    if (isTable) {
	      var sourceColumn = view._columns_pull[_key2]; // when these's no column to take raw data from, or custom template defined - ignore raw mode

	      if (column.template && (!sourceColumn || sourceColumn.template != column.template)) rawColumn = false;
	      if (sourceColumn) column = exports.extend(exports.extend({}, column), sourceColumn);
	    }

	    var record = {
	      id: column.id,
	      template: rawColumn || !column.template ? getDataHelper(_key2, column, raw) : column.template,
	      width: (column.width || 200) * (options.export_mode === "excel" ? 8.43 / 70 : 1),
	      header: column.header !== false ? column.header || _key2 : ""
	    };
	    if (isTree && _key2 === treeLines) record.isTree = treeColumn = true;

	    if (options.export_mode === "excel") {
	      exports.extend(record, {
	        type: column.exportType || "",
	        format: column.exportFormat || ""
	      });
	    }

	    if (typeof record.header === "string") record.header = [{
	      text: record.header
	    }];else record.header = copy(record.header);

	    for (var _i3 = 0; _i3 < record.header.length; _i3++) {
	      var hcell = record.header[_i3] || {};
	      var text = hcell.contentId ? view.getHeaderContent(hcell.contentId).getValue(true) : hcell.text;
	      record.header[_i3] = (text || "").toString().replace(/<[^>]*>/gi, "");
	    }

	    h_count = Math.max(h_count, record.header.length);

	    if (view._settings.footer) {
	      var footer = column.footer || "";
	      if (typeof footer == "string") footer = [{
	        text: footer
	      }];else footer = copy(footer);

	      for (var _i4 = 0; _i4 < footer.length; _i4++) {
	        if (footer[_i4]) footer[_i4] = footer[_i4].contentId ? view.getHeaderContent(footer[_i4].contentId).getValue() : footer[_i4].text;else footer[_i4] = "";
	      }

	      record.footer = footer;
	      f_count = Math.max(f_count, record.footer.length);
	    }

	    scheme.push(record);
	  }

	  if (!treeColumn && isTree && options.treeLines != treeLines && scheme[0]) scheme[0].isTree = true;

	  for (var _i5 = 0; _i5 < scheme.length; _i5++) {
	    var diff = h_count - scheme[_i5].header.length;

	    for (var d = 0; d < diff; d++) {
	      scheme[_i5].header.push("");
	    }

	    if (view._settings.footer) {
	      diff = f_count - scheme[_i5].footer.length;

	      for (var _d = 0; _d < diff; _d++) {
	        scheme[_i5].footer.push("");
	      }
	    }
	  }

	  return scheme;
	}
	function getExportData(view, options, scheme) {
	  var filterHTML = !!options.filterHTML;
	  //var htmlFilter = /<[^>]*>/gi;
	  var htmlFilter = /<[\/\!]*font[^<>]*?>/gi;
	  var data = [];
	  var header, headers;
	  var mode = options.export_mode;

	  if ((mode === "excel" || mode == "csv") && options.docHeader) {
	    data = [[(options.docHeader.text || options.docHeader).toString()], [""]];
	    if (mode === "excel" && options.docHeader.height) scheme.heights[0] = options.docHeader.height;
	  }

	  if (options.header !== false && scheme.length) {
	    for (var h = 0; h < scheme[0].header.length; h++) {
	      headers = [];

	      for (var i = 0; i < scheme.length; i++) {
	        header = "";

	        if (scheme[i].header[h]) {
	          header = scheme[i].header[h];
	          if (filterHTML) header = scheme[i].header[h] = header.replace(htmlFilter, "");
	        }

	        headers.push(header);
	      }

	      if (mode == "excel" && view._columns && options.heights !== false && (view._headers[h] !== $active.barHeight || options.heights == "all")) scheme.heights[data.length] = view._headers[h];
	      if (mode !== "pdf") data[data.length] = headers;
	    }
	  }

	  options.yCorrection = (options.yCorrection || 0) - data.length;
	  var treeline = options.flatTree || options.plainOutput ? "" : "-";
	  view.data.each(function (item) {
	    if (!options.filter || options.filter(item)) {
	      if (this.data._scheme_export) {
	        item = view.data._scheme_export(item);
	      }

	      var line = [];

	      for (var _i6 = 0; _i6 < scheme.length; _i6++) {
	        var column = scheme[_i6],
	            cell = null; //spreadsheet can output math

	        if (options.math && item["$" + column.id] && item["$" + column.id].charAt(0) == "=" && !item["$" + column.id].match(/^=(image|link|sparkline)\(/i)) cell = item["$" + column.id];

	        if (this._spans_pull) {
	          var span = this.getSpan(item.id, column.id);

	          if (span && span[4] && span[0] == item.id && span[1] == column.id) {
	            cell = span[4];
	            if (filterHTML && typeof cell === "string") cell = cell.replace(htmlFilter, "");
	          }
	        }

	        if (!cell) {
	          cell = column.template(item, view.type, item[column.id], column, _i6);
	          if (!cell && cell !== 0) cell = "";
	          if (column.isTree && treeline) cell = " " + Array(item.$level).join(treeline) + " " + cell;

	          if (filterHTML && typeof cell === "string") {
	            cell = cell.replace(htmlFilter, "");
	          } //remove end/start spaces(ex.hierarchy data)


	          if (typeof cell === "string" && mode === "csv") cell = cell.trim(); //for multiline data

	          if (typeof cell === "string" && (mode === "excel" || mode === "csv")) {
	            cell = cell.replace(/<br\s*\/?>/mg, "\n");
	          }
	        }

	        line.push(cell);
	      }

	      if (mode == "excel" && view._columns && options.heights !== false && (item.$height && item.$height !== $active.rowHeight || options.heights == "all")) scheme.heights[data.length] = item.$height || this.config.rowHeight;
	      data.push(line);
	    }
	  }, view);

	  if (options.footer !== false) {
	    var f_count = scheme[0].footer ? scheme[0].footer.length : 0;

	    for (var f = 0; f < f_count; f++) {
	      var footers = [];

	      for (var _i7 = 0; _i7 < scheme.length; _i7++) {
	        var footer = scheme[_i7].footer[f];
	        if (filterHTML) footer = scheme[_i7].footer[f] = footer.toString().replace(htmlFilter, "");
	        footers.push(footer);
	      }

	      if (mode == "excel" && view._columns && options.heights !== false && (view._footers[f] !== $active.barHeight || options.heights == "all")) scheme.heights[data.length] = view._footers[f];
	      if (mode !== "pdf") data.push(footers);
	    }
	  }

	  if (mode === "excel" && options.docFooter) {
	    data = data.concat([[], [(options.docFooter.text || options.docFooter).toString()]]);
	    if (options.docFooter.height) scheme.heights[data.length - 1] = options.docFooter.height;
	  }

	  return data;
	}

	var toPNG = function (id, options) {
	  var defer = Deferred.defer();
	  return require(env.cdn + "/extras/html2canvas-1.0.min.js").then(function () {
	    //backward compatibility
	    if (typeof options === "string") options = {
	      filename: options
	    };
	    options = options || {};
	    var view = $$(id);
	    if (view && view.$exportView) view = view.$exportView(options);
	    assert(view, errorMessage);
	    if (!view) return defer.reject(errorMessage);
	    var node = view ? view.$view : toNode(id);
	    var filename = (options.filename || "Data") + ".png";
	    window.html2canvas(node, {
	      background: "#fff",
	      logging: false
	    }).then(function (canvas) {
	      var callback = function (data) {
	        if (options.download !== false) download(data, filename);
	        canvas.remove();
	        defer.resolve(data);
	      };

	      if (canvas.msToBlob) callback(canvas.msToBlob());else canvas.toBlob(callback, "image/png");
	    });
	    return defer;
	  });
	};

	var toCSV = function (id, options) {
	  options = options || {};
	  var view = $$(id);
	  if (view && view.$exportView) view = view.$exportView(options);
	  assert(view, errorMessage);
	  if (!view) return Deferred.reject(errorMessage);
	  options.export_mode = "csv";
	  options.filterHTML = true;
	  var scheme = getExportScheme(view, options);
	  var result = getExportData(view, options, scheme);
	  var data = getCsvData(result, scheme);
	  var filename = (options.filename || "Data") + ".csv";
	  var blob = new Blob(["\uFEFF" + data], {
	    type: "text/csv"
	  });
	  if (options.download !== false) download(blob, filename);
	  return Deferred.resolve(blob);
	};

	function getCsvData(data) {
	  return csv$1.stringify(data);
	}

	var font = {};
	var toPDF = function (id, options) {
	  return require([env.cdn + "/extras/pdfjs.js", env.cdn + "/extras/html2canvas-1.0.min.js"]).then(function () {
	    options = options || {};
	    options.export_mode = "pdf";
	    options._export_font = font;
	    options.fontName = options.fontName || "pt-sans.regular";
	    options.display = options.display || "table";
	    id = isArray(id) ? id : [id];
	    var views = [];

	    for (var i = 0; i < id.length; i++) {
	      if (!id[i].id) id[i] = {
	        id: id[i]
	      };
	      var view = $$(id[i].id);
	      var viewOptions = exports.extend(id[i].options || {}, options);
	      if (view && view.$exportView) view = view.$exportView(viewOptions);

	      if (view) {
	        if (viewOptions.display !== "table") {
	          views.push({
	            node: view.$view,
	            viewOptions: viewOptions
	          });
	          if (options.autowidth) options.width = Math.max(options.width || 0, view.$view.$width);
	        }

	        if (viewOptions.display !== "image" && view.data && view.data.pull) {
	          var scheme = getExportScheme(view, viewOptions);
	          views.push({
	            scheme: scheme,
	            data: getExportData(view, viewOptions, scheme),
	            viewOptions: viewOptions
	          });
	          if (options.autowidth) getAutowidth(scheme, options);
	        }
	      }

	      assert(view, errorMessage);
	    }

	    if (views.length == 0) return Deferred.reject(errorMessage);

	    if (font[options.fontName]) {
	      options._export_font = font[options.fontName];
	      return getPdfData(views, options).then(function (pdf) {
	        return getBlob(pdf, options);
	      });
	    } else {
	      var defer = Deferred.defer();
	      /* global pdfjs */

	      pdfjs.load(env.cdn + "/extras/" + options.fontName + ".ttf", function (err, buf) {
	        if (err) throw err;
	        options._export_font = font[options.fontName] = new pdfjs.TTFFont(buf);
	        defer.resolve(getPdfData(views, options).then(function (pdf) {
	          return getBlob(pdf, options);
	        }));
	      });
	      return defer;
	    }
	  });
	};

	function getBlob(pdf, options) {
	  var filename = (options.filename || "Data") + ".pdf";
	  var blob = new Blob([pdf.toString()], {
	    type: "application/pdf"
	  });
	  if (options.download !== false) download(blob, filename);
	  return blob;
	}

	function getPdfData(views, options) {
	  var doc = addPDFDoc(options);
	  var promises = [];

	  for (var i = 0; i < views.length; i++) {
	    if (views[i].node) promises.push(getPDFImage(views[i].node));else promises.push(Deferred.resolve());
	  }

	  return Deferred.all(promises).then(function (images) {
	    for (var _i = 0; _i < promises.length; _i++) {
	      var viewOptions = views[_i].viewOptions;
	      if (viewOptions.textBefore) addText(doc, "before", viewOptions.textBefore);
	      if (images[_i]) doc.image(images[_i], {
	        align: "center"
	      });else addPDFTable(views[_i].scheme, views[_i].data, viewOptions, doc);
	      if (viewOptions.textAfter) addText(doc, "after", viewOptions.textAfter);
	      if (_i != views.length - 1) doc.pageBreak();
	    }

	    return addPDFHeader(doc, options);
	  });
	}

	function addText(doc, type, text) {
	  if (type == "after") doc.text().br();
	  if (typeof text == "string") text = {
	    text: text
	  };
	  doc.text(text.text, text.options || {});
	  if (type == "before") doc.text().br();
	}

	function getPDFImage(node) {
	  return window.html2canvas(node, {
	    background: "#fff",
	    logging: false
	  }).then(function (canvas) {
	    var defer = Deferred.defer();
	    var image = canvas.toDataURL("image/jpeg");
	    pdfjs.load(image, function (err, buffer) {
	      defer.resolve(new pdfjs.Image(buffer));
	    });
	    return defer;
	  });
	}

	function getAutowidth(scheme, options) {
	  var prop = options.orientation && options.orientation == "landscape" ? "height" : "width";
	  var width = 80; //paddings

	  for (var i = 0; i < scheme.length; i++) {
	    width += scheme[i].width;
	  }

	  options[prop] = Math.max(options[prop] || 0, width);
	}

	function addPDFDoc(options) {
	  var width = options.width || 595.296,
	      height = options.height || 841.896; // default A4 size

	  if (options.orientation && options.orientation === "landscape") height = [width, width = height][0];
	  return new pdfjs.Document({
	    padding: 40,
	    font: options._export_font,
	    threshold: 256,
	    width: width,
	    height: height
	  });
	}

	function addPDFTable(scheme, data, options, doc) {
	  options.header = isUndefined(options.header) || options.header === true ? {} : options.header;
	  options.footer = isUndefined(options.footer) || options.footer === true ? {} : options.footer;
	  options.table = options.table || {}; //render table

	  var h_count = options.header === false ? 0 : scheme[0].header.length;
	  var f_count = options.footer === false || !scheme[0].footer ? 0 : scheme[0].footer.length;
	  var colWidths = [];

	  for (var i = 0; i < scheme.length; i++) {
	    colWidths[i] = scheme[i].width;
	  }

	  var tableOps = exports.extend(options.table, {
	    borderWidth: 1,
	    height: 20,
	    lineHeight: 1.1,
	    borderColor: 0xEEEEEE,
	    backgroundColor: 0xFFFFFF,
	    color: 0x666666,
	    textAlign: "left",
	    paddingRight: 10,
	    paddingLeft: 10,
	    headerRows: h_count,
	    widths: colWidths.length ? colWidths : ["100%"]
	  });
	  var table = doc.table(tableOps); //render table header

	  if (h_count) {
	    var headerOps = exports.extend(options.header, {
	      borderRightColor: 0xB0CEE3,
	      borderBottomColor: 0xB0CEE3,
	      color: 0x4A4A4A,
	      backgroundColor: 0xD2E3EF,
	      height: 27,
	      lineHeight: 1.2
	    });

	    for (var _i2 = 0; _i2 < h_count; _i2++) {
	      var header = table.tr(headerOps);

	      for (var s = 0; s < scheme.length; s++) {
	        header.td(scheme[s].header[_i2].toString());
	      }
	    }
	  } //render table data


	  for (var r = 0; r < data.length; r++) {
	    var row = table.tr({});

	    for (var c = 0; c < data[r].length; c++) {
	      row.td(data[r][c]);
	    }
	  } //render table footer


	  if (f_count) {
	    var footerOps = exports.extend(options.footer, {
	      borderRightColor: 0xEEEEEE,
	      borderBottomColor: 0xEEEEEE,
	      backgroundColor: 0xFAFAFA,
	      color: 0x666666,
	      height: 27,
	      lineHeight: 1.2
	    });

	    for (var _i3 = 0; _i3 < f_count; _i3++) {
	      var footer = table.tr(footerOps);

	      for (var _s = 0; _s < scheme.length; _s++) {
	        footer.td(scheme[_s].footer[_i3].toString());
	      }
	    }
	  }
	}

	function addPDFHeader(doc, options) {
	  //doc footer
	  if (options.docFooter !== false) {
	    var ft = doc.footer();
	    ft.text({
	      color: 0x666666,
	      textAlign: "center"
	    }).append(i18n.dataExport.page || "Page").pageNumber().append("  " + (i18n.dataExport.of || "of") + "  ").pageCount();
	  }

	  var horder = {
	    text: 0,
	    image: 1
	  }; //doc header, configurable

	  if (options.docHeader) {
	    if (typeof options.docHeader == "string") options.docHeader = {
	      text: options.docHeader
	    };
	    exports.extend(options.docHeader, {
	      color: 0x666666,
	      textAlign: "right",
	      order: 0
	    });
	    horder.text = options.docHeader.order;
	  }

	  if (options.docHeaderImage) {
	    if (typeof options.docHeaderImage == "string") options.docHeaderImage = {
	      url: options.docHeaderImage
	    };
	    exports.extend(options.docHeaderImage, {
	      align: "right",
	      order: 1
	    });
	    horder.image = options.docHeaderImage.order;
	  }

	  if (options.docHeader && horder.image > horder.text) doc.header({
	    paddingBottom: 10
	  }).text(options.docHeader.text, options.docHeader);

	  if (options.docHeaderImage) {
	    var defer = Deferred.defer();
	    pdfjs.load(options.docHeaderImage.url, function (err, buffer) {
	      if (!err) {
	        var img = new pdfjs.Image(buffer);
	        doc.header({
	          paddingBottom: 10
	        }).image(img, options.docHeaderImage);
	        if (options.docHeader && horder.image < horder.text) doc.header({
	          paddingBottom: 10
	        }).text(options.docHeader.text, options.docHeader);
	      } //render pdf and show in browser


	      defer.resolve(doc.render());
	    });
	    return defer;
	  } else return Deferred.resolve(doc.render());
	}

	var toExcel = function (id, options) {
	  options = options || {};
	  options.export_mode = "excel";
	  id = isArray(id) ? id : [id];
	  var views = [];

	  for (var i = 0; i < id.length; i++) {
	    var view = $$(id[i]);
	    if (view && view.$exportView) view = view.$exportView(options);
	    if (view) views = views.concat(view);
	    assert(view, errorMessage); //spreadsheet and excelviewer require plain data output first

	    if (options.dataOnly) {
	      var scheme = getExportScheme(view, options);
	      views[i] = {
	        scheme: scheme,
	        exportData: getExportData(view, options, scheme),
	        spans: options.spans ? getSpans(view, options) : []
	      };
	    }
	  }

	  if (options.dataOnly) return views;
	  var defer = Deferred.defer();
	  return require(env.cdn + "/extras/xlsx.core.styles.min.js").then(function () {
	    if (!views.length) return defer.reject(errorMessage);
	    var wb = {
	      SheetNames: [],
	      Sheets: {},
	      Workbook: {
	        WBProps: {},
	        Names: []
	      }
	    };
	    var name = isArray(options.sheets) ? options.sheets : [options.name || "Data"];

	    for (var i = 0; i < views.length; i++) {
	      var scheme = views[i].scheme || getExportScheme(views[i], options);
	      var result = views[i].exportData || getExportData(views[i], options, scheme);
	      var spans = views[i].spans ? views[i].spans : options.spans ? getSpans(views[i], options) : [];
	      var ranges = views[i].ranges || [];
	      var styles = views[i].styles || [];
	      var data = getExcelData(result, scheme, spans, styles, options);
	      var sname = (name[i] || "Data" + i).replace(/[*?:[\]\\/]/g, "").replace(/&/g, "&amp;").substring(0, 31);
	      wb.SheetNames.push(sname);
	      wb.Sheets[sname] = data;
	      wb.Workbook.Names = wb.Workbook.Names.concat(ranges);
	    }
	    /* global XLSX */


	    var xls = XLSX.write(wb, {
	      bookType: "xlsx",
	      bookSST: false,
	      type: "binary"
	    });
	    var filename = (options.filename || name.join(",")) + ".xlsx";
	    var blob = new Blob([str2array(xls)], {
	      type: "application/xlsx"
	    });
	    if (options.download !== false) download(blob, filename);
	    defer.resolve(blob);
	    return defer;
	  });
	};

	function str2array(s) {
	  var buf = new ArrayBuffer(s.length);
	  var view = new Uint8Array(buf);

	  for (var i = 0; i != s.length; ++i) {
	    view[i] = s.charCodeAt(i) & 0xFF;
	  }

	  return buf;
	}

	var types = {
	  number: "n",
	  date: "n",
	  string: "s",
	  boolean: "b"
	};
	var table = "_table";

	function getExcelData(data, scheme, spans, styles, options) {
	  var ws = {};
	  var range = {
	    s: {
	      c: 10000000,
	      r: 10000000
	    },
	    e: {
	      c: 0,
	      r: 0
	    }
	  };

	  for (var R = 0; R != data.length; ++R) {
	    for (var C = 0; C != data[R].length; ++C) {
	      if (range.s.r > R) range.s.r = R;
	      if (range.s.c > C) range.s.c = C;
	      if (range.e.r < R) range.e.r = R;
	      if (range.e.c < C) range.e.c = C;
	      var cell = {
	        v: data[R][C]
	      };
	      if (cell.v === null) continue;
	      var cell_ref = XLSX.utils.encode_cell({
	        c: C,
	        r: R
	      });
	      var stringValue = cell.v.toString();
	      var isFormula = stringValue.charAt(0) === "="; // set type based on column's config
	      // skip headers and formula based cells

	      var header = (options.docHeader ? 2 : 0) + scheme[0].header.length;

	      if (R >= header && !isFormula) {
	        var column = scheme[C];
	        if (column.type) cell.t = types[column.type] || "";
	        if (column.format) cell.z = column.format;
	      } // set type based on cell's value


	      if (cell.v instanceof Date) {
	        cell.t = cell.t || "n";
	        cell.z = cell.z || XLSX.SSF[table][14];
	        cell.v = excelDate(cell.v);
	      } else if (!cell.t) {
	        if (typeof cell.v === "boolean") cell.t = "b";else if (typeof cell.v === "number" || parseFloat(cell.v) == cell.v) {
	          cell.v = cell.v * 1;
	          cell.t = "n";
	        } else {
	          // convert any other object to a string
	          cell.v = stringValue;

	          if (isFormula) {
	            cell.t = "n";
	            cell.f = cell.v;
	            delete cell.v;
	          } else cell.t = "s";
	        }
	      }

	      if (styles) {
	        var cellStyle = getStyles(R, C, styles);

	        if (cellStyle.format) {
	          cell.z = cellStyle.format;
	          delete cellStyle.format;
	        }

	        cell.s = cellStyle;
	      }

	      ws[cell_ref] = cell;
	    }
	  }

	  if (range.s.c < 10000000) ws["!ref"] = XLSX.utils.encode_range(range);
	  ws["!rows"] = getRowHeights(scheme.heights);
	  ws["!cols"] = getColumnsWidths(scheme);
	  if (spans.length) ws["!merges"] = spans;
	  return ws;
	}

	function getRowHeights(heights) {
	  for (var i in heights) {
	    heights[i] = {
	      hpx: heights[i],
	      hpt: heights[i] * 0.75
	    };
	  }

	  return heights;
	}

	function getStyles(r, c, styles) {
	  //row index, column index, styles array
	  if (styles[r] && styles[r][c]) return styles[r][c];
	  return "";
	}

	function getSpans(view, options) {
	  var isTable = view.getColumnConfig;
	  var pull = view._spans_pull;
	  var spans = [];

	  if (isTable) {
	    if (options.header !== false) spans = getHeaderSpans(view, options, "header", spans);

	    if (pull) {
	      var xc = options.xCorrection || 0;
	      var yc = options.yCorrection || 0;

	      for (var row in pull) {
	        //{ s:{c:1, r:0}, e:{c:3, r:0} }
	        var cols = pull[row];

	        for (var col in cols) {
	          var sc = view.getColumnIndex(col) - xc;
	          var sr = view.getIndexById(row) - yc;
	          if (sc < 0 || sr < 0) //hidden cols/rows
	            continue;
	          var ec = sc + cols[col][0] - 1;
	          var er = sr + (cols[col][1] - 1);
	          spans.push({
	            s: {
	              c: sc,
	              r: sr
	            },
	            e: {
	              c: ec,
	              r: er
	            }
	          });
	        }
	      }
	    }

	    if (options.footer !== false) spans = getHeaderSpans(view, options, "footer", spans);
	  }

	  return spans;
	}

	function getHeaderSpans(view, options, group, spans) {
	  var columns = view.config.columns;
	  var delta = (options.docHeader ? 2 : 0) + (group == "header" ? 0 : (options.header !== false ? view._headers.length : 0) + view.count());

	  for (var i = 0; i < columns.length; i++) {
	    var header = columns[i][group];

	    for (var h = 0; h < header.length; h++) {
	      if (header[h] && (header[h].colspan || header[h].rowspan)) {
	        spans.push({
	          s: {
	            c: i,
	            r: h + delta
	          },
	          e: {
	            c: i + (header[h].colspan || 1) - 1,
	            r: h + (header[h].rowspan || 1) - 1 + delta
	          }
	        });
	      }
	    }
	  }

	  return spans;
	}

	function excelDate(date) {
	  return Math.round(25569 + date / (24 * 60 * 60 * 1000));
	}

	function getColumnsWidths(scheme) {
	  var wscols = [];

	  for (var i = 0; i < scheme.length; i++) {
	    wscols.push({
	      wch: scheme[i].width
	    });
	  }

	  return wscols;
	}

	function callback(config, result) {
	  config.type.indexOf("confirm") != -1 && result === false ? config._promise.reject() : config._promise.resolve(result);
	  var usercall = config.callback;
	  if (usercall) usercall(result, config.details);
	  modalbox.hide(config.id);
	}

	function modal_key(e) {
	  var count = modalbox.order.length;

	  if (count > 0 && message.keyboard) {
	    e = e || event$1;
	    var code = e.which || event$1.keyCode;
	    var lastModalbox = modalbox.pull[modalbox.order[count - 1]];
	    if (code == 13 || code == 32) callback(lastModalbox, true);
	    if (code == 27) callback(lastModalbox, false);
	    if (e.preventDefault) e.preventDefault();
	    return !(e.cancelBubble = true);
	  }
	}

	event$1(document, "keydown", modal_key, {
	  capture: true
	});

	function modality(mode, container) {
	  var node = container || document.body;
	  var cover;

	  if (isUndefined(node.modality)) {
	    cover = create("DIV", {
	      class: "webix_modal_cover",
	      style: "position:" + (container ? "absolute" : "fixed") + ";"
	    });
	    cover.onkeydown = modal_key;

	    if (container) {
	      var position = window.getComputedStyle(container).position;
	      if (position != "fixed" && position != "absolute" && position != "sticky" && position != "relative") node.style.position = "relative";
	    }

	    node.appendChild(cover);
	    node.modality = 1;
	  } else mode ? node.modality++ : node.modality--; //trigger visibility only if necessary


	  if (mode && node.modality === 1 || node.modality === 0) {
	    if (cover) cover.style.display = "inline-block";else {
	      cover = node.querySelectorAll(".webix_modal_cover");

	      for (var i = 0; i < cover.length; i++) {
	        if (cover[i].parentNode == node) {
	          cover[i].style.display = node.modality == 1 ? "inline-block" : "none";
	          break;
	        }
	      }
	    }
	  }
	}

	function button(text, result, className) {
	  return "<div role='button' tabindex='0' aria-label='" + text + "' class='webix_popup_button" + (className ? " " + className : "") + "' result='" + result + "' ><div>" + text + "</div></div>";
	}

	function info(text) {
	  if (!t.area) {
	    t.area = document.createElement("DIV");
	    t.area.className = "webix_message_area";
	    t.area.style[t.position] = "5px";
	    document.body.appendChild(t.area);
	  }

	  t.area.setAttribute("role", "alert");
	  t.area.setAttribute("aria-atomic", true);
	  t.hide(text.id);
	  var message = document.createElement("DIV");
	  message.innerHTML = "<div>" + text.text + "</div>";
	  message.className = "webix_message webix_" + text.type;

	  message.onclick = function () {
	    t.hide(text.id);
	    text = null;
	  };
	  if (t.position == "bottom" && t.area.firstChild) t.area.insertBefore(message, t.area.firstChild);else t.area.appendChild(message);
	  if (text.expire > 0) t.timers[text.id] = window.setTimeout(function () {
	    t.hide(text.id);
	  }, text.expire); //styling for animation

	  message.style.height = message.offsetHeight - 2 + "px";
	  t.pull[text.id] = message;
	  message = null;
	  return text.id;
	}

	function _boxStructure(config, ok, cancel) {
	  var box = document.createElement("DIV");
	  var css = config.css ? " " + config.css : "";
	  box.className = "webix_modal_box webix_" + config.type + css;
	  box.setAttribute("webixbox", 1);
	  box.setAttribute("role", "alertdialog");
	  box.setAttribute("aria-label", config.title || "");
	  box.setAttribute("tabindex", "0");
	  var inner = "";
	  if (config.width) box.style.width = config.width + (rules.isNumber(config.width) ? "px" : "");
	  if (config.height) box.style.height = config.height + (rules.isNumber(config.height) ? "px" : "");
	  if (config.title) inner += "<div class=\"webix_popup_title\">" + config.title + "</div>";
	  inner += "<div class=\"webix_popup_text\"><span>" + (config.content ? "" : config.text) + "</span></div><div  class=\"webix_popup_controls\">";
	  if (cancel) inner += button(config.cancel || i18n.message.cancel, false);
	  if (ok) inner += button(config.ok || i18n.message.ok, true, "confirm");

	  if (config.buttons && !ok && !cancel) {
	    for (var i = 0; i < config.buttons.length; i++) {
	      inner += button(config.buttons[i], i);
	    }
	  }

	  inner += "</div>";
	  box.innerHTML = inner;

	  if (config.content) {
	    var node = config.content;
	    if (typeof node == "string") node = document.getElementById(node);
	    if (node.style.display == "none") node.style.display = "";
	    box.childNodes[config.title ? 1 : 0].appendChild(node);
	  }

	  box.onclick = function (e) {
	    e = e || event$1;
	    var source = e.target || e.srcElement;
	    if (!source.className) source = source.parentNode;

	    if (source.className.indexOf("webix_popup_button") != -1) {
	      var result = source.getAttribute("result");
	      result = result == "true" || (result == "false" ? false : result);
	      callback(config, result);
	    }

	    e.cancelBubble = true;
	  };

	  config._box = box;
	  return box;
	}

	modalbox.pull = {};
	modalbox.order = [];

	function _createBox(config, ok, cancel) {
	  var box = config.tagName ? config : _boxStructure(config, ok, cancel);
	  var containerWidth = config.container ? config.container.offsetWidth : window.innerWidth || document.documentElement.offsetWidth;
	  var containerHeight = config.container ? config.container.offsetHeight : window.innerHeight || document.documentElement.offsetHeight;
	  if (config.container) box.style.position = "absolute";
	  toNode((config.container || document.body).appendChild(box));
	  modality(true, config.container);
	  var x = config.left || Math.abs(Math.floor((containerWidth - box.offsetWidth) / 2));
	  var y = config.top || Math.abs(Math.floor((containerHeight - box.offsetHeight) / 2));
	  if (config.position == "top") box.style.top = "-3px";else box.style.top = y + "px";
	  box.style.left = x + "px"; //necessary for IE only

	  box.onkeydown = modal_key;
	  box.focus();
	  if (!config.id) config.id = _uid("modalbox");else if (modalbox.pull[config.id]) {
	    modalbox.hide(config.id);
	  }
	  modalbox.order.push(config.id);
	  modalbox.pull[config.id] = config;
	  config._promise = Deferred.defer();
	  return config._promise;
	}

	function alertPopup(config) {
	  return _createBox(config, true);
	}

	function confirmPopup(config) {
	  return _createBox(config, true, true);
	}

	function boxPopup(config) {
	  return _createBox(config);
	}

	function box_params(text, type, callback) {
	  if (_typeof(text) != "object") {
	    if (typeof type == "function") {
	      callback = type;
	      type = "";
	    }

	    text = {
	      text: text,
	      type: type,
	      callback: callback
	    };
	  }

	  return text;
	}

	function params(text, type, expire, id) {
	  if (_typeof(text) != "object") text = {
	    text: text,
	    type: type,
	    expire: expire,
	    id: id
	  };
	  text.id = text.id || _uid("message");
	  text.expire = text.expire || t.expire;
	  return text;
	}

	function alert() {
	  var text = box_params.apply(this, arguments);
	  text.type = text.type || "alert";
	  return alertPopup(text);
	}
	function confirm() {
	  var text = box_params.apply(this, arguments);
	  text.type = text.type || "confirm";
	  return confirmPopup(text);
	}
	function modalbox() {
	  var text = box_params.apply(this, arguments);
	  text.type = text.type || "alert";
	  return boxPopup(text);
	}

	modalbox.hide = function (id) {
	  if (id && modalbox.pull[id]) {
	    var node = modalbox.pull[id]._box;

	    if (node) {
	      node.parentNode.removeChild(node);
	      modalbox.order.splice(modalbox.order.indexOf(id), 1);
	      modality(false, modalbox.pull[id].container);
	      delete modalbox.pull[id];
	    }
	  }
	};

	modalbox.hideAll = function () {
	  for (var id in modalbox.pull) {
	    this.hide(id);
	  }
	};

	function message(text, type, expire, id) {
	  //eslint-disable-line
	  text = params.apply(this, arguments);
	  text.type = text.type || "info";
	  var subtype = text.type.split("-")[0];

	  switch (subtype) {
	    case "alert":
	      return alertPopup(text);

	    case "confirm":
	      return confirmPopup(text);

	    case "modalbox":
	      return boxPopup(text);

	    default:
	      return info(text);
	  }
	}
	var t = message;
	t.expire = 4000;
	t.keyboard = true;
	t.position = "top";
	t.pull = {};
	t.timers = {};

	t.hideAll = function () {
	  for (var key in t.pull) {
	    t.hide(key);
	  }
	};

	t.hide = function (id) {
	  var obj = t.pull[id];

	  if (obj && obj.parentNode) {
	    window.setTimeout(function () {
	      obj.parentNode.removeChild(obj);
	      obj = null;
	    }, 2000); //styling for animation

	    obj.style.height = 0;
	    obj.className += " hidden";
	    t.area.removeAttribute("role");
	    if (t.timers[id]) window.clearTimeout(t.timers[id]);
	    delete t.pull[id];
	  }
	}; //override circualr dependencies


	define("message", message);

	function editStop() {
	  callEvent("onEditEnd", []);
	}

	if (env.isIE8) {
	  // Not really a polyfill, silence the esModule flag
	  Object.defineProperty = function (obj, key, data) {
	    obj[key] = data.value;
	  };
	}

	var en = {
	  groupDelimiter: ",",
	  groupSize: 3,
	  decimalDelimiter: ".",
	  decimalSize: 2,
	  dateFormat: "%m/%d/%Y",
	  timeFormat: "%h:%i %A",
	  longDateFormat: "%d %F %Y",
	  fullDateFormat: "%m/%d/%Y %h:%i %A",
	  am: ["am", "AM"],
	  pm: ["pm", "PM"],
	  price: "${obj}",
	  priceSettings: {
	    groupDelimiter: ",",
	    groupSize: 3,
	    decimalDelimiter: ".",
	    decimalSize: 2
	  },
	  fileSize: ["b", "Kb", "Mb", "Gb", "Tb", "Pb", "Eb"],
	  calendar: {
	    monthFull: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
	    monthShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
	    dayFull: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
	    dayShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
	    hours: "Hours",
	    minutes: "Minutes",
	    done: "Done",
	    clear: "Clear",
	    today: "Today"
	  },
	  dataExport: {
	    page: "Page",
	    of: "of"
	  },
	  PDFviewer: {
	    of: "of",
	    automaticZoom: "Automatic Zoom",
	    actualSize: "Actual Size",
	    pageFit: "Page Fit",
	    pageWidth: "Page Width",
	    pageHeight: "Page Height",
	    enterPassword: "Enter password",
	    passwordError: "Wrong password"
	  },
	  aria: {
	    calendar: "Calendar",
	    increaseValue: "Increase value",
	    decreaseValue: "Decrease value",
	    navMonth: ["Previous month", "Next month"],
	    navYear: ["Previous year", "Next year"],
	    navDecade: ["Previous decade", "Next decade"],
	    dateFormat: "%d %F %Y",
	    monthFormat: "%F %Y",
	    yearFormat: "%Y",
	    hourFormat: "Hours: %h %A",
	    minuteFormat: "Minutes: %i",
	    removeItem: "Remove item",
	    pages: ["First page", "Previous page", "Next page", "Last page"],
	    page: "Page",
	    headermenu: "Header menu",
	    openGroup: "Open column group",
	    closeGroup: "Close column group",
	    closeTab: "Close tab",
	    showTabs: "Show more tabs",
	    resetTreeMap: "Reset tree map",
	    navTreeMap: "Level up",
	    nextTab: "Next tab",
	    prevTab: "Previous tab",
	    multitextSection: "Add section",
	    multitextextraSection: "Remove section",
	    showChart: "Show chart",
	    hideChart: "Hide chart",
	    resizeChart: "Resize chart"
	  },
	  richtext: {
	    underline: "Underline",
	    bold: "Bold",
	    italic: "Italic"
	  },
	  combo: {
	    select: "Select",
	    selectAll: "Select all",
	    unselectAll: "Unselect all"
	  },
	  message: {
	    ok: "OK",
	    cancel: "Cancel"
	  },
	  comments: {
	    send: "Send",
	    confirmMessage: "The comment will be removed. Are you sure?",
	    edit: "Edit",
	    remove: "Remove",
	    placeholder: "Type here..",
	    moreComments: "More comments"
	  }
	};

	var wDate = {
	  startOnMonday: false,
	  toFixed: function (num, ms) {
	    if (num < 10) num = "0" + num;
	    if (ms && num < 100) num = "0" + num;
	    return num;
	  },
	  weekStart: function (date) {
	    date = this.copy(date);
	    var shift = date.getDay();

	    if (this.startOnMonday) {
	      if (shift === 0) shift = 6;else shift--;
	    }

	    return this.datePart(this.add(date, -1 * shift, "day"));
	  },
	  monthStart: function (date) {
	    date = this.copy(date);
	    date.setDate(1);
	    return this.datePart(date);
	  },
	  yearStart: function (date) {
	    date = this.copy(date);
	    date.setMonth(0);
	    return this.monthStart(date);
	  },
	  dayStart: function (date) {
	    return this.datePart(date, true);
	  },
	  dateToStr: function (format, utc) {
	    if (typeof format == "function") return format;

	    if (env.strict) {
	      return function (date) {
	        var str = "";
	        var lastPos = 0;
	        format.replace(/%[a-zA-Z]/g, function (s, pos) {
	          str += format.slice(lastPos, pos);

	          var fn = function (date) {
	            if (s == "%d") return wDate.toFixed(date.getDate());
	            if (s == "%m") return wDate.toFixed(date.getMonth() + 1);
	            if (s == "%j") return date.getDate();
	            if (s == "%n") return date.getMonth() + 1;
	            if (s == "%y") return wDate.toFixed(date.getFullYear() % 100);
	            if (s == "%Y") return date.getFullYear();
	            if (s == "%D") return i18n.calendar.dayShort[date.getDay()];
	            if (s == "%l") return i18n.calendar.dayFull[date.getDay()];
	            if (s == "%M") return i18n.calendar.monthShort[date.getMonth()];
	            if (s == "%F") return i18n.calendar.monthFull[date.getMonth()];
	            if (s == "%h") return wDate.toFixed((date.getHours() + 11) % 12 + 1);
	            if (s == "%g") return (date.getHours() + 11) % 12 + 1;
	            if (s == "%G") return date.getHours();
	            if (s == "%H") return wDate.toFixed(date.getHours());
	            if (s == "%i") return wDate.toFixed(date.getMinutes());
	            if (s == "%a") return date.getHours() > 11 ? i18n.pm[0] : i18n.am[0];
	            if (s == "%A") return date.getHours() > 11 ? i18n.pm[1] : i18n.am[1];
	            if (s == "%s") return wDate.toFixed(date.getSeconds());
	            if (s == "%S") return wDate.toFixed(date.getMilliseconds(), true);
	            if (s == "%W") return wDate.toFixed(Date.getISOWeek(date));

	            if (s == "%c") {
	              var str = date.getFullYear();
	              str += "-" + wDate.toFixed(date.getMonth() + 1);
	              str += "-" + wDate.toFixed(date.getDate());
	              str += "T";
	              str += wDate.toFixed(date.getHours());
	              str += ":" + wDate.toFixed(date.getMinutes());
	              str += ":" + wDate.toFixed(date.getSeconds());
	              return str;
	            }

	            return s;
	          };

	          str += fn(date);
	          lastPos = pos + 2;
	        });
	        str += format.slice(lastPos, format.length);
	        return str;
	      };
	    }

	    format = format.replace(/%[a-zA-Z]/g, function (a) {
	      switch (a) {
	        case "%d":
	          return "\"+wDate.toFixed(date.getDate())+\"";

	        case "%m":
	          return "\"+wDate.toFixed((date.getMonth()+1))+\"";

	        case "%j":
	          return "\"+date.getDate()+\"";

	        case "%n":
	          return "\"+(date.getMonth()+1)+\"";

	        case "%y":
	          return "\"+wDate.toFixed(date.getFullYear()%100)+\"";

	        case "%Y":
	          return "\"+date.getFullYear()+\"";

	        case "%D":
	          return "\"+i18n.calendar.dayShort[date.getDay()]+\"";

	        case "%l":
	          return "\"+i18n.calendar.dayFull[date.getDay()]+\"";

	        case "%M":
	          return "\"+i18n.calendar.monthShort[date.getMonth()]+\"";

	        case "%F":
	          return "\"+i18n.calendar.monthFull[date.getMonth()]+\"";

	        case "%h":
	          return "\"+wDate.toFixed((date.getHours()+11)%12+1)+\"";

	        case "%g":
	          return "\"+((date.getHours()+11)%12+1)+\"";

	        case "%G":
	          return "\"+date.getHours()+\"";

	        case "%H":
	          return "\"+wDate.toFixed(date.getHours())+\"";

	        case "%i":
	          return "\"+wDate.toFixed(date.getMinutes())+\"";

	        case "%a":
	          return "\"+(date.getHours()>11?i18n.pm[0]:i18n.am[0])+\"";

	        case "%A":
	          return "\"+(date.getHours()>11?i18n.pm[1]:i18n.am[1])+\"";

	        case "%s":
	          return "\"+wDate.toFixed(date.getSeconds())+\"";

	        case "%S":
	          return "\"+wDate.toFixed(date.getMilliseconds(), true)+\"";

	        case "%W":
	          return "\"+wDate.toFixed(wDate.getISOWeek(date))+\"";

	        case "%c":
	          var str = "\"+date.getFullYear()+\"";
	          str += "-\"+wDate.toFixed((date.getMonth()+1))+\"";
	          str += "-\"+wDate.toFixed(date.getDate())+\"";
	          str += "T";
	          str += "\"+wDate.toFixed(date.getHours())+\"";
	          str += ":\"+wDate.toFixed(date.getMinutes())+\"";
	          str += ":\"+wDate.toFixed(date.getSeconds())+\"";
	          if (utc === true) str += "Z";
	          return str;

	        default:
	          return a;
	      }
	    });
	    if (utc === true) format = format.replace(/date\.get/g, "date.getUTC");
	    var temp = new Function("date", "i18n", "wDate", "if (!date) return ''; if (!date.getMonth) date=i18n.parseFormatDate(date);  return \"" + format + "\";");
	    return function (v) {
	      return temp(v, i18n, wDate);
	    };
	  },
	  strToDate: function (format, utc) {
	    if (typeof format == "function") return format;
	    var mask = format.match(/%[a-zA-Z]/g);
	    var splt = "var temp=date.split(/[^0-9a-zA-Z]+/g);";
	    var i, t, s;

	    if (!i18n.calendar.monthShort_hash) {
	      s = i18n.calendar.monthShort;
	      t = i18n.calendar.monthShort_hash = {};

	      for (i = 0; i < s.length; i++) {
	        t[s[i]] = i;
	      }

	      s = i18n.calendar.monthFull;
	      t = i18n.calendar.monthFull_hash = {};

	      for (i = 0; i < s.length; i++) {
	        t[s[i]] = i;
	      }
	    }

	    if (env.strict) {
	      return function (date) {
	        if (!date) return "";
	        if (_typeof(date) == "object") return date;
	        var temp = date.split(/[^0-9a-zA-Z]+/g);
	        var set = [0, 0, 1, 0, 0, 0, 0];

	        for (i = 0; i < mask.length; i++) {
	          var a = mask[i];
	          if (a == "%y") set[0] = temp[i] * 1 + (temp[i] > 30 ? 1900 : 2000);else if (a == "%Y") {
	            set[0] = (temp[i] || 0) * 1;
	            if (set[0] < 30) set[0] += 2000;
	          } else if (a == "%n" || a == "%m") set[1] = (temp[i] || 1) - 1;else if (a == "%M") set[1] = i18n.calendar.monthShort_hash[temp[i]] || 0;else if (a == "%F") set[1] = i18n.calendar.monthFull_hash[temp[i]] || 0;else if (a == "%j" || a == "%d") set[2] = temp[i] || 1;else if (a == "%g" || a == "%G" || a == "%h" || a == "%H") set[3] = temp[i] || 0;else if (a == "%a") set[3] = set[3] % 12 + ((temp[i] || "") == i18n.am[0] ? 0 : 12);else if (a == "%A") set[3] = set[3] % 12 + ((temp[i] || "") == i18n.am[1] ? 0 : 12);else if (a == "%i") set[4] = temp[i] || 0;else if (a == "%s") set[5] = temp[i] || 0;else if (a == "%S") set[6] = temp[i] || 0;else if (a == "%c") {
	            var reg = /(\d+)-(\d+)-(\d+)T(\d+):(\d+):(\d+)(\+.*|)/g;
	            var res = reg.exec(date);
	            set[0] = (res[1] || 0) * 1;
	            if (set[0] < 30) set[0] += 2000;
	            set[1] = (res[2] || 1) - 1;
	            set[2] = res[3] || 1;
	            set[3] = res[4] || 0;
	            set[4] = res[5] || 0;
	            set[5] = res[6] || 0;
	          }
	        }

	        if (utc) return new Date(Date.UTC(set[0], set[1], set[2], set[3], set[4], set[5], set[6]));
	        return new Date(set[0], set[1], set[2], set[3], set[4], set[5], set[6]);
	      };
	    }

	    for (i = 0; i < mask.length; i++) {
	      switch (mask[i]) {
	        case "%j":
	        case "%d":
	          splt += "set[2]=temp[" + i + "]||1;";
	          break;

	        case "%n":
	        case "%m":
	          splt += "set[1]=(temp[" + i + "]||1)-1;";
	          break;

	        case "%y":
	          splt += "set[0]=temp[" + i + "]*1+(temp[" + i + "]>30?1900:2000);";
	          break;

	        case "%g":
	        case "%G":
	        case "%h":
	        case "%H":
	          splt += "set[3]=temp[" + i + "]||0;";
	          break;

	        case "%i":
	          splt += "set[4]=temp[" + i + "]||0;";
	          break;

	        case "%Y":
	          splt += "set[0]=(temp[" + i + "]||0)*1; if (set[0]<30) set[0]+=2000;";
	          break;

	        case "%a":
	          splt += "set[3]=set[3]%12+(temp[" + i + "]==i18n.am[0]?0:12);";
	          break;

	        case "%A":
	          splt += "set[3]=set[3]%12+(temp[" + i + "]==i18n.am[1]?0:12);";
	          break;

	        case "%s":
	          splt += "set[5]=temp[" + i + "]||0;";
	          break;

	        case "%S":
	          splt += "set[6]=temp[" + i + "]||0;";
	          break;

	        case "%M":
	          splt += "set[1]=i18n.calendar.monthShort_hash[temp[" + i + "]]||0;";
	          break;

	        case "%F":
	          splt += "set[1]=i18n.calendar.monthFull_hash[temp[" + i + "]]||0;";
	          break;

	        case "%c":
	          splt += "var res = date.split('T');";
	          splt += "if(res[0]){ var d = res[0].split('-');";
	          splt += "set[0]= (d[0]||0)*1; if (set[0]<30) set[0]+=2000;";
	          splt += "set[1]= (d[1]||1)-1;";
	          splt += "set[2]= d[2]||1;}";
	          splt += "if(res[1]){ var t = res[1].split(':');";
	          splt += "set[3]= t[0]||0;";
	          splt += "set[4]= t[1]||0;";
	          splt += "set[5]= parseInt(t[2])||0;}";
	          break;

	        default:
	          break;
	      }
	    }

	    var code = "set[0],set[1],set[2],set[3],set[4],set[5], set[6]";
	    if (utc) code = " Date.UTC(" + code + ")";
	    var temp = new Function("date", "i18n", "if (!date) return ''; if (typeof date == 'object') return date; var set=[0,0,1,0,0,0,0]; " + splt + " return new Date(" + code + ");");
	    return function (v) {
	      return temp(v, i18n);
	    };
	  },
	  getISOWeek: function (ndate) {
	    if (!ndate) return false;
	    var nday = ndate.getDay();

	    if (nday === 0) {
	      nday = 7;
	    }

	    var first_thursday = new Date(ndate.valueOf());
	    first_thursday.setDate(ndate.getDate() + (4 - nday));
	    var year_number = first_thursday.getFullYear(); // year of the first Thursday

	    var ordinal_date = Math.floor((first_thursday.getTime() - new Date(year_number, 0, 1).getTime()) / 86400000); //ordinal date of the first Thursday - 1 (so not really ordinal date)

	    var weekNumber = 1 + Math.floor(ordinal_date / 7);
	    return weekNumber;
	  },
	  getUTCISOWeek: function (ndate) {
	    return this.getISOWeek(ndate);
	  },
	  _correctDate: function (d, d0, inc, checkFunc) {
	    if (!inc) return;
	    var incorrect = checkFunc(d, d0);

	    if (incorrect) {
	      var i = inc > 0 ? 1 : -1;

	      while (incorrect) {
	        d.setHours(d.getHours() + i);
	        incorrect = checkFunc(d, d0);
	        i += inc > 0 ? 1 : -1;
	      }
	    }
	  },
	  add: function (date, inc, mode, copy) {
	    if (copy) date = this.copy(date);
	    var d = wDate.copy(date);

	    switch (mode) {
	      case "day":
	        date.setDate(date.getDate() + inc);

	        this._correctDate(date, d, inc, function (d, d0) {
	          return wDate.datePart(d0, true).valueOf() == wDate.datePart(d, true).valueOf();
	        });

	        break;

	      case "week":
	        date.setDate(date.getDate() + 7 * inc);

	        this._correctDate(date, d, 7 * inc, function (d, d0) {
	          return wDate.datePart(d0, true).valueOf() == wDate.datePart(d, true).valueOf();
	        });

	        break;

	      case "month":
	        date.setMonth(date.getMonth() + inc);

	        this._correctDate(date, d, inc, function (d, d0) {
	          return d0.getMonth() == d.getMonth() && d0.getYear() == d.getYear();
	        });

	        break;

	      case "year":
	        date.setYear(date.getFullYear() + inc);

	        this._correctDate(date, d, inc, function (d, d0) {
	          return d0.getFullYear() == d.getFullYear();
	        });

	        break;

	      case "hour":
	        date.setHours(date.getHours() + inc);

	        this._correctDate(date, d, inc, function (d, d0) {
	          return d0.getHours() == d.getHours() && Date.datePart(d0, true) == Date.datePart(d, true);
	        });

	        break;

	      case "minute":
	        date.setMinutes(date.getMinutes() + inc);
	        break;

	      default:
	        wDate.add[mode](date, inc, mode);
	        break;
	    }

	    return date;
	  },
	  datePart: function (date, copy) {
	    if (copy) date = this.copy(date); // workaround for non-existent hours

	    var d = this.copy(date);
	    d.setHours(0);

	    if (d.getDate() != date.getDate()) {
	      date.setHours(1);
	    } else {
	      date.setHours(0);
	    }

	    date.setMinutes(0);
	    date.setSeconds(0);
	    date.setMilliseconds(0);
	    return date;
	  },
	  timePart: function (date, copy) {
	    if (copy) date = this.copy(date);
	    return (date.valueOf() / 1000 - date.getTimezoneOffset() * 60) % 86400;
	  },
	  copy: function (date) {
	    return new Date(date.valueOf());
	  },
	  equal: function (a, b) {
	    if (!a || !b) return false;
	    return a.valueOf() === b.valueOf();
	  },
	  isHoliday: function (day) {
	    day = day.getDay();
	    if (day === 0 || day == 6) return "webix_cal_event";
	  }
	};

	var Number$1 = {
	  getConfig: function (value) {
	    var config = {
	      decimalSize: 0,
	      groupSize: 999,
	      prefix: "",
	      sufix: ""
	    };
	    var parts = value.split(/[0-9].*[0-9]/g);
	    if (parts[0].length) config.prefix = parts[0];
	    if (parts[1].length) config.sufix = parts[1];

	    if (config.prefix || config.sufix) {
	      value = value.substr(config.prefix.length, value.length - config.prefix.length - config.sufix.length);
	    }

	    var num = value.indexOf("1");

	    if (num > 0) {
	      config.prefix = value.substr(0, num);
	      value = value.substr(num);
	    }

	    var dot = value.indexOf("0");

	    if (dot > 0) {
	      config.decimalSize = value.length - dot;
	      config.decimalDelimiter = value[dot - 1];
	      value = value.substr(0, dot - 1);
	    }

	    var sep = value.match(/[^0-9]/);

	    if (sep) {
	      config.groupSize = value.length - sep.index - 1;
	      config.groupDelimiter = value[sep.index];
	    }

	    return config;
	  },
	  parse: function (value, config) {
	    if (!value || typeof value !== "string") return value;
	    if (config.prefix) value = value.toLowerCase().replace(config.prefix.toLowerCase() || "", "");
	    if (config.sufix) value = value.toLowerCase().replace(config.sufix.toLowerCase() || "", "");
	    var decimal = "";

	    if (config.decimalDelimiter) {
	      var ind = value.indexOf(config.decimalDelimiter);

	      if (ind > -1) {
	        decimal = value.substr(ind + 1).replace(/[^0-9]/g, "");
	        decimal = decimal.substr(0, Math.min(decimal.length, config.decimalSize));
	        value = value.substr(0, ind);
	      }
	    }

	    var sign = value[0] === "-" ? -1 : 1;
	    value = value.replace(/[^0-9]/g, "");
	    if (!value) value = "0";
	    if (decimal) value += "." + decimal;
	    return parseFloat(value) * sign;
	  },
	  format: function (value, config) {
	    if (value === "" || typeof value === "undefined") return value;
	    config = config || i18n;
	    value = parseFloat(value);
	    var sign = value < 0 ? "-" : "";
	    value = Math.abs(value);
	    if (!config.decimalOptional) value = value.toFixed(config.decimalSize);
	    var str = value.toString();
	    str = str.split(".");
	    var int_value = "";

	    if (config.groupSize) {
	      var step = config.groupSize;
	      var i = str[0].length;

	      do {
	        i -= step;
	        var chunk = i > 0 ? str[0].substr(i, step) : str[0].substr(0, step + i);
	        int_value = chunk + (int_value ? config.groupDelimiter + int_value : "");
	      } while (i > 0);
	    } else int_value = str[0];

	    if (config.decimalSize) str = sign + int_value + (str[1] ? config.decimalDelimiter + str[1] : "");else str = sign + int_value;

	    if (config.prefix || config.sufix) {
	      return config.prefix + str + config.sufix;
	    } else return str;
	  },
	  numToStr: function (config) {
	    return function (value) {
	      return Number$1.format(value, config);
	    };
	  }
	};

	function extend(base, source) {
	  for (var method in source) {
	    if (_typeof(source[method]) == "object" && !isArray(source[method])) {
	      if (!base[method]) {
	        base[method] = {};
	      }

	      extend(base[method], source[method]);
	    } else base[method] = source[method];
	  }
	}

	var helpers = ["fullDateFormat", "timeFormat", "dateFormat", "longDateFormat", "parseFormat", "parseTimeFormat"];

	i18n.setLocale = function (locale) {
	  if (typeof locale == "string") locale = i18n.locales[locale];

	  if (locale) {
	    locale.priceSettings = copy(locale.priceSettings || locale);
	    extend(i18n, locale);
	  }

	  for (var i = 0; i < helpers.length; i++) {
	    var key = helpers[i];
	    var utc = i18n[key + "UTC"];
	    i18n[key + "Str"] = wDate.dateToStr(i18n[key], utc);
	    i18n[key + "Date"] = wDate.strToDate(i18n[key], utc);
	  }

	  var _price_format = template(i18n.price);

	  var _price_settings = i18n.priceSettings || i18n;

	  i18n.intFormat = Number$1.numToStr({
	    groupSize: i18n.groupSize,
	    groupDelimiter: i18n.groupDelimiter,
	    decimalSize: 0
	  });

	  i18n.priceFormat = function (value) {
	    return _price_format(Number$1.format(value, _price_settings));
	  };

	  i18n.numberFormat = Number$1.format;
	};

	i18n.locales = {
	  "en-US": en
	};
	i18n.setLocale("en-US");

	/*
		Behavior:History - change multiview state on 'back' button

	 */

	var history = {
	  track: function (id, url) {
	    this._init_state(id, url);

	    if (this._aHandler) $$(this._aViewId).detachEvent(this._aHandler);

	    if (id) {
	      this._aViewId = id;
	      var view = $$(id);

	      var handler = function () {
	        if (history._ignored) return;
	        if (view.getValue) history.push(id, view.getValue());
	      };

	      if (view.getActiveId) this._aHandler = view.attachEvent("onViewChange", handler);else this._aHandler = view.attachEvent("onChange", handler);
	    }
	  },
	  _set_state: function (view, state$$1) {
	    history._ignored = 1;
	    view = $$(view);
	    if (view.callEvent("onBeforeHistoryNav", [state$$1])) if (view.setValue) view.setValue(state$$1);
	    history._ignored = 0;
	  },
	  push: function (view, url, value) {
	    view = $$(view);
	    var new_url = "";
	    if (url) new_url = "#!/" + url;

	    if (isUndefined(value)) {
	      if (view.getValue) value = view.getValue();else value = url;
	    }

	    window.history.pushState({
	      webix: true,
	      id: view._settings.id,
	      value: value
	    }, "", new_url);
	  },
	  _init_state: function (view, url) {
	    event$1(window, "popstate", function (ev) {
	      if (ev.state && ev.state.webix) {
	        history._set_state(ev.state.id, ev.state.value);
	      }
	    });
	    var hash = window.location.hash;
	    state.noanimate = true;
	    if (hash && hash.indexOf("#!/") === 0) history._set_state(view, hash.replace("#!/", ""));else if (url) {
	      history.push(view, url);

	      history._set_state(view, url);
	    }
	    state.noanimate = false;

	    this._init_state = function () {};
	  }
	};

	var markup = {
	  namespace: "x",
	  attribute: "data-",
	  dataTag: "li",
	  _dash: /-([a-z])/g,
	  _after_dash: function (match) {
	    return match[1].toUpperCase();
	  },
	  _parse_int: {
	    width: true,
	    height: true,
	    gravity: true,
	    margin: true,
	    padding: true,
	    paddingX: true,
	    paddingY: true,
	    minWidth: true,
	    maxWidth: true,
	    minHeight: true,
	    maxHeight: true,
	    headerRowHeight: true
	  },
	  _parse_bool: {
	    disabled: true,
	    hidden: true
	  },
	  _view_has_method: function (view, name) {
	    return hasMethod(view, name);
	  },
	  init: function (node, target, scope) {
	    node = node || document.body;
	    var els = [];

	    var temp = this._get_core_els(node);

	    var html = temp.html;
	    var ui$$1 = null; //make copy to prevent node removing effects

	    for (var i = temp.length - 1; i >= 0; i--) {
	      els[i] = temp[i];
	    }

	    for (var _i = 0; _i < els.length; _i++) {
	      var config; //collect configuration

	      config = this._sub_markup(els[_i], html);
	      config.$scope = scope;
	      ui$$1 = this._initComponent(config, els[_i], html, target);
	    }

	    return ui$$1;
	  },
	  parse: function (source, mode) {
	    //convert from string to object
	    if (typeof source == "string") source = DataDriver[mode || "xml"].toObject(source, source);

	    var els = this._get_core_els(source, mode);

	    return this._sub_markup(els[0], els.html);
	  },
	  _initComponent: function (config, node, html, target) {
	    if (!target) {
	      config.container = node.parentNode;
	      remove(node);
	    } else config.container = target;

	    if (this._view_has_method(config.view, "setPosition")) delete config.container; //init ui

	    return ui(config);
	  },
	  _get_core_els: function (node) {
	    this._full_prefix = this.namespace ? this.namespace + ":" : "";
	    this._full_prefix_top = this._full_prefix + "ui"; //xhtml mode

	    var els = node.getElementsByTagName(this._full_prefix_top);
	    if (!els.length && node.documentElement && node.documentElement.tagName == this._full_prefix_top) els = [node.documentElement]; //loading from xml file with valid namespace

	    if (!els.length && this.namespace) {
	      els = node.getElementsByTagName("ui");
	      if (!els.length && node.documentElement && node.documentElement.tagName == "ui") els = [node.documentElement];
	    }

	    if (!els.length) {
	      //html mode
	      els = this._get_html_tops(node);
	      els.html = true;
	    }

	    return els;
	  },
	  //html conversion
	  _get_html_tops: function (node) {
	    if (node.getAttribute && node.getAttribute(this.attribute + "view")) return [node];
	    var els = node.querySelectorAll("[" + this.attribute + "view]");
	    var tags = [];

	    for (var i = 0; i < els.length; i++) {
	      if (!els[i].parentNode.getAttribute(this.attribute + "view")) tags.push(els[i]);
	    }

	    return tags;
	  },
	  _sub_markup: function (el, html, json) {
	    var htmltable = false; //ignore top x:ui for xhtml and xml 

	    if (!json) {
	      var name = this._get_name(el, html);

	      if (name == "ui") {
	        var childs = el.childNodes;

	        for (var i = 0; i < childs.length; i++) {
	          if (childs[i].nodeType == 1) {
	            return this._sub_markup(childs[i], html);
	          }
	        }
	      }

	      json = {
	        view: name
	      };

	      if (html && el.tagName.toLowerCase() == "table") {
	        json.data = el;
	        json.datatype = "htmltable";
	        htmltable = true;
	      }
	    }

	    var is_layout = json.view == "cols" || json.view == "rows" || this._view_has_method(json.view, "addView");

	    var subs = [];
	    var has_tags = 0;
	    var allow_sub_tags = !(html || el.style); //only for xml documents

	    var first = el.firstChild;

	    while (first) {
	      //tag node
	      if (first.nodeType == 1) {
	        var _name = this._get_name(first, html);

	        if (_name == "data") {
	          has_tags = 1;
	          var data = first;
	          first = first.nextSibling;
	          json.data = this._handle_data(data, html);
	          continue;
	        } else if (_name == "config") {
	          this._get_config_html(first, json, html);

	          var confignode = first;
	          first = first.nextSibling;
	          remove(confignode);
	          continue;
	        } else if (_name == "column") {
	          has_tags = 1;

	          var column = this._tag_to_json(first, html);

	          column.header = column.header || column.value;
	          column.width = column.width * 1 || "";
	          json.columns = json.columns || [];
	          json.columns.push(column);
	        } else if (_name || is_layout && html) {
	          var obj = this._sub_markup(first, html, {
	            view: _name
	          });

	          if (obj.view == "head") json.head = obj.rows ? obj.rows[0] : obj.template;else if (obj.view == "body") {
	            if (this._view_has_method(json.view, "addView")) {
	              //multiview, accordion
	              //subtag or plain value
	              //in case of multiple sub tags, only first will be used
	              // #dirty
	              subs.push({
	                body: obj.rows ? obj.rows[0] : obj.value,
	                header: obj.header || ""
	              });
	            } else {
	              //window, fieldset
	              //one sub tag - use it
	              //multiple sub tags - create sub layout
	              //or use plain text value
	              json.body = obj.rows ? obj.rows.length == 1 ? obj.rows[0] : {
	                rows: obj.rows
	              } : obj.value;
	            }
	          } else subs.push(obj);
	        } else if (allow_sub_tags) {
	          has_tags = 1;
	          var tagName = first.tagName;
	          if (html) tagName = tagName.toLowerCase().replace(this._dash, this._after_dash);
	          json[tagName] = DataDriver.xml.tagToObject(first);
	        }
	      }

	      first = first.nextSibling;
	    }

	    this._attrs_to_json(el, json, html);

	    if (subs.length) {
	      if (json.stack) {
	        json[json.stack] = subs;
	      } else if (json.subui) {
	        json[json.subui] = subs[0];
	      } else if (this._view_has_method(json.view, "setValues")) {
	        json["elements"] = subs;
	      } else if (json.view == "rows") {
	        json.view = "layout";
	        json.rows = subs;
	      } else if (json.view == "cols") {
	        json.view = "layout";
	        json.cols = subs;
	      } else if (this._view_has_method(json.view, "setValue")) {
	        json["cells"] = subs;
	      } else if (this._view_has_method(json.view, "getBody")) {
	        json.body = subs.length == 1 ? subs[0] : {
	          rows: subs
	        };
	      } else {
	        json["rows"] = subs;
	      }
	    } else if (!htmltable && !has_tags) {
	      if (html && !json.template && (!json.view || json.view == "template")) {
	        json.view = "template";
	        json.content = el;
	      } else {
	        var content = this._content(el, html);

	        if (content) {
	          var target = "template";
	          if (this._view_has_method(json.view, "setValue")) target = "value";
	          json[target] = json[target] || content;
	        }
	      }
	    }

	    return json;
	  },
	  _empty: function (str) {
	    var clean = str.replace(/\s+/gm, "");
	    return clean.length > 0 ? false : true;
	  },
	  _markup_names: {
	    body: 1,
	    head: 1,
	    data: 1,
	    rows: 1,
	    cols: 1,
	    cells: 1,
	    elements: 1,
	    ui: 1,
	    column: 1,
	    config: 1
	  },
	  _get_config_html: function (tag, json, html) {
	    var master = this._attrs_to_json(tag, {});

	    if (master.name) {
	      json[master.name] = master;
	      delete master.name;
	    } else if (master.stack) json[master.stack] = [];else json = master;

	    var childs = tag.childNodes;

	    for (var i = 0; i < childs.length; i++) {
	      var sub = null;
	      if (childs[i].nodeType == 1 && childs[i].tagName.toLowerCase() == "config" && childs[i].attributes.length) sub = this._get_config_html(childs[i], master, html);else if (childs[i].nodeType == 1 && childs[i].tagName.toLowerCase() == "data") {
	        sub = this._handle_data(childs[i], html);
	        if (!master.stack) master.data = sub;
	      } else sub = childs[i].innerHTML;
	      if (master.stack && sub) json[master.stack].push(sub);
	    }

	    return json;
	  },
	  _get_name: function (tag, html) {
	    //value of view attribute or config tag
	    if (html) return tag.getAttribute(this.attribute + "view") || (tag.tagName.toLowerCase() == "config" ? "config" : null);
	    var name = tag.tagName.toLowerCase();

	    if (this.namespace) {
	      if (name.indexOf(this._full_prefix) === 0 || tag.scopeName == this.namespace) return name.replace(this._full_prefix, "");
	    } else {
	      if (ui[name] || this._markup_names[name]) return name;
	    }

	    return 0;
	  },
	  _handle_data: function (el, html) {
	    var data = [];
	    var records = el.getElementsByTagName(markup.dataTag);

	    for (var i = 0; i < records.length; i++) {
	      var rec = records[i];

	      if (rec.parentNode.parentNode.tagName != markup.dataTag) {
	        var json = this._tag_to_json(rec, html); //reuse css class 


	        if (rec.className) json.$css = rec.className;
	        data.push(json);
	      }
	    }

	    remove(el);
	    return data;
	  },
	  _content: function (el) {
	    if (el.style) return el.innerHTML;
	    if (el.firstChild) return el.firstChild.wholeText || el.firstChild.data || "";
	    return "";
	  },
	  _tag_to_json: function (el, html) {
	    if (!html) return DataDriver.xml.tagToObject(el);

	    var json = this._attrs_to_json(el, {}, html);

	    if (!json.value && el.childNodes.length) json.value = this._content(el, html);
	    return json;
	  },
	  _attrs_to_json: function (el, json, html) {
	    var attrs = el.attributes;

	    for (var i = 0; i < attrs.length; i++) {
	      var name = attrs[i].name;

	      if (html) {
	        if (name.indexOf(this.attribute) !== 0) continue;
	        name = name.replace(this.attribute, "").replace(this._dash, this._after_dash);
	      }

	      var value = attrs[i].value;
	      if (value.indexOf("json://") != -1) value = JSON.parse(value.replace("json://", ""));
	      if (this._parse_int[name]) value = parseInt(value, 10);else if (this._parse_bool[name]) value = value && value !== "false" && value != "0";
	      json[name] = value;
	    }

	    return json;
	  }
	};

	env.printPPI = 96;
	env.printMargin = 0.75 * env.printPPI;
	var margin = env.printMargin;
	var papers = {
	  "a4": "A4",
	  "a3": "A3",
	  "letter": "letter"
	};
	var fits = {
	  page: true,
	  data: true
	};
	var modes = {
	  portrait: true,
	  landscape: true
	};
	var sizes = {
	  //inches, real size is value*ppi
	  "A3": {
	    width: 11.7,
	    height: 16.5
	  },
	  "A4": {
	    width: 8.27,
	    height: 11.7
	  },
	  "letter": {
	    width: 8.5,
	    height: 11
	  }
	};

	var print = function (id, options) {
	  var view = $$(id);
	  if (view && view.$printView) view = view.$printView();
	  assert(view, "non-existing view for printing");
	  if (!view) return;
	  if (view.callEvent) view.callEvent("onBeforePrint", [options]);
	  options = _checkOptions(options);

	  _beforePrint(options); //try widget's custom logic first, sometimes it may deny 


	  if (!view.$customPrint || view.$customPrint(options) === true) _print(view, options);

	  _afterPrint(options);
	};
	/*processing print options*/


	function _checkOptions(options) {
	  options = options || {};
	  options.paper = papers[(options.paper || "").toLowerCase()] || "A4";
	  options.mode = modes[options.mode] ? options.mode : "portrait";
	  options.fit = fits[options.fit] ? options.fit : "page";
	  options.scroll = options.scroll || false;
	  options.size = sizes[options.paper];
	  options.margin = options.margin || options.margin === 0 ? options.margin : {};
	  margin = isNaN(options.margin * 1) ? margin : options.margin;
	  options.margin = {
	    top: options.margin.top || options.margin.top === 0 ? options.margin.top : margin,
	    bottom: options.margin.bottom || options.margin.bottom === 0 ? options.margin.bottom : margin,
	    right: options.margin.right || options.margin.right === 0 ? options.margin.right : margin,
	    left: options.margin.left || options.margin.left === 0 ? options.margin.left : margin
	  };
	  return options;
	}
	/*preparing printing environment*/


	function _beforePrint(options) {
	  addCss(document.body, "webix_print");
	  if (options.docHeader) _getHeaderFooter("Header", options);
	  if (options.docFooter) _getHeaderFooter("Footer", options);
	  /* static print styles are located at 'css/print.less'*/

	  var cssString = "@media print { " + "@page{ size:" + options.paper + " " + options.mode + ";" + "margin-top:" + options.margin.top + "px;margin-bottom:" + options.margin.bottom + "px;margin-right:" + options.margin.right + "px;margin-left:" + options.margin.left + "px;}" + "}";
	  addStyle(cssString, "print");
	}
	/*cleaning environment*/


	function _afterPrint(options) {
	  removeCss(document.body, "webix_print");
	  removeStyle("print");
	  if (options.docHeader) remove(options.docHeader);
	  if (options.docFooter) remove(options.docFooter);
	}
	/*common print actions */


	function _print(view, options) {
	  var doc = view.$view.cloneNode(true); //copy data from all canvases

	  var canvases = view.$view.getElementsByTagName("canvas");
	  if (canvases.length) for (var i = canvases.length - 1; i >= 0; i--) {
	    var destCtx = doc.getElementsByTagName("canvas")[i].getContext("2d");
	    destCtx.drawImage(canvases[i], 0, 0);
	  }
	  insertBefore(doc, options.docFooter, document.body);
	  addCss(doc, "webix_ui_print");
	  if (!options.scroll && (view._dataobj && view.data && view.data.pull || view.getBody)) addCss(doc, "webix_print_noscroll");
	  window.print();
	  remove(doc);
	}
	/*custom header nad footer*/


	function _getHeaderFooter(group, options) {
	  var header = create("div", {
	    "class": "webix_view webix_print_" + group.toLowerCase(),
	    "style": "height:0px;visibility:hidden;"
	  }, options["doc" + group]);
	  if (group === "Header") insertBefore(header, document.body.firstChild);else document.body.appendChild(header);
	  options["doc" + group] = header;
	}

	var patterns = {
	  phone: {
	    mask: "+# (###) ###-####",
	    allow: /[0-9]/g
	  },
	  card: {
	    mask: "#### #### #### ####",
	    allow: /[0-9]/g
	  },
	  date: {
	    mask: "####-##-## ##:##",
	    allow: /[0-9]/g
	  }
	};

	var fullscreen = {
	  set: function (view, config) {
	    config = config || {};
	    if (this._view) this.exit();
	    if ($$(view)) view = $$(view);else {
	      if (typeof view == "string") view = document.getElementById(view);
	      if (view instanceof Element) view = {
	        $view: view,
	        $html: true
	      };
	      assert(view, "Incorrect view for fullscreen mode");
	    }
	    this._view = view;
	    var viewConfig = view.config;

	    if (view.setPosition) {
	      this._pos = {
	        left: view.config.left,
	        top: view.config.top
	      };
	      view.setPosition(0, 0);
	      viewConfig.fullscreen = true;
	      view.resize();
	    } else {
	      this._fullscreen = ui({
	        view: "window",
	        head: this._getHeadConfig(config),
	        fullscreen: true,
	        //better resize logic
	        body: {
	          rows: []
	        }
	      });
	      if (viewConfig) this._sizes = {
	        width: viewConfig.width,
	        minWidth: viewConfig.minWidth,
	        maxWidth: viewConfig.maxWidth,
	        height: viewConfig.height,
	        minHeight: viewConfig.minHeight,
	        maxHeight: viewConfig.maxHeight
	      };

	      if (view.getParentView && view.getParentView()) {
	        this._parent = view.getParentView();
	        if (this._parent.index) this._pos = {
	          index: this._parent.index(view),
	          active: this._parent.getActiveId ? this._parent.getActiveId() == viewConfig.id : false
	        };
	      } else {
	        this._parent = view.$view.parentNode;
	        this._pos = create("div"); //save old position

	        this._parent.replaceChild(this._pos, view.$view);
	      }

	      this._fullscreen.getBody().addView(view.$html ? {
	        view: "template",
	        content: view.$view,
	        css: "webix_fullscreen_html"
	      } : view);

	      this._fullscreen.show();

	      this._setSizes(view);
	    }
	  },
	  exit: function () {
	    if (this._view) {
	      var viewConfig = this._view.config;

	      if (this._view.setPosition) {
	        viewConfig.fullscreen = false;

	        this._view.setPosition(this._pos.left, this._pos.top);

	        this._view.resize();
	      } else {
	        if (this._parent instanceof Element) {
	          this._view._parent_cell = null;
	          if (this._view._set_inner) this._view._set_inner(this._view.config);

	          this._parent.replaceChild(this._view.$view, this._pos);
	        } else {
	          if (!isUndefined(this._pos)) {
	            this._parent.addView(this._view, this._pos.index);

	            if (this._pos.active) this._view.show(false, false);
	          } else {
	            this._view._parent_cell = this._parent;

	            this._parent._replace(this._view);
	          }
	        }

	        this._setSizes(this._view, this._sizes); //prevent view destruction (with layout)


	        if (!this._view.$html) this._fullscreen.getBody()._cells = []; //we can't set head false with replace, so we need to close win

	        this._fullscreen.close();
	      }

	      this._clearValues();
	    }
	  },
	  _clearValues: function () {
	    delete this._parent;
	    delete this._view;
	    delete this._sizes;
	    delete this._pos;
	  },
	  _setSizes: function (view, sizes) {
	    if (!view.$html) {
	      sizes = sizes || {
	        height: 0,
	        minHeight: 0,
	        maxHeight: 0,
	        width: 0,
	        minWidth: 0,
	        maxWidth: 0
	      };
	      view.define(sizes);
	      view.resize();
	    }
	  },
	  _getHeadConfig: function (config) {
	    var _this = this;

	    if (config.head === false || _typeof(config.head) == "object") return config.head;else {
	      return {
	        cols: [{
	          template: config.head || "",
	          type: "header",
	          borderless: true
	        }, {
	          view: "icon",
	          icon: "wxi-close",
	          click: function () {
	            _this.exit();
	          }
	        }]
	      };
	    }
	  }
	};

	/*
		Common helpers
	*/
	var html$1 = {
	  denySelect: denySelect,
	  allowSelect: allowSelect,
	  index: index,
	  createCss: createCss,
	  addStyle: addStyle,
	  removeStyle: removeStyle,
	  create: create,
	  getValue: getValue,
	  remove: remove,
	  insertBefore: insertBefore,
	  locate: locate,
	  offset: offset,
	  posRelative: posRelative,
	  pos: pos,
	  preventEvent: preventEvent,
	  stopEvent: stopEvent,
	  triggerEvent: triggerEvent,
	  addCss: addCss,
	  removeCss: removeCss,
	  getTextSize: getTextSize,
	  download: download,
	  _getClassName: _getClassName,
	  setSelectionRange: setSelectionRange,
	  getSelectionRange: getSelectionRange,
	  addMeta: addMeta
	};

	var timers = {};
	function jsonp(url, params, callback, master) {
	  var defer = Deferred.defer();
	  var id = "webix_jsonp_" + uid();
	  var script = document.createElement("script");
	  script.id = id;
	  script.type = "text/javascript";
	  var head = document.getElementsByTagName("head")[0];

	  if (typeof params == "function") {
	    master = callback;
	    callback = params;
	    params = {};
	  }

	  if (!params) params = {};
	  params.jsonp = id;

	  window[id] = function () {
	    if (callback) callback.apply(master || window, arguments);
	    defer.resolve(arguments[0]);
	    window.clearTimeout(timers[id]);
	    delete timers[id];
	    script.parentNode.removeChild(script);
	    callback = head = master = script = null;
	    delete window[id];
	  }; //timeout timer


	  timers[id] = window.setTimeout(function () {
	    defer.reject();
	    delete window[id];
	  }, jsonp.timer);
	  var vals = [];

	  for (var key in params) {
	    vals.push(key + "=" + encodeURIComponent(params[key]));
	  }

	  url += (url.indexOf("?") == -1 ? "?" : "&") + vals.join("&");
	  script.src = url;
	  head.appendChild(script);
	  return defer;
	}
	jsonp.timer = 3000;

	var error_key = "__webix_remote_error";

	function RemoteContext(url, config) {
	  this._proxy = {};
	  this._queue = [];
	  this._url = url;
	  this._key = "";
	  if (config) this._process(config);else this._ready = ajax(url).then(function (data) {
	    return data.text();
	  }).then(bind(function (text) {
	    text = text.split("/*api*/")[1];

	    this._process(JSON.parse(text));

	    return this._proxy;
	  }, this));
	}

	RemoteContext.prototype = {
	  _process: function (config) {
	    if (config.$key) this._key = config.$key;
	    if (config.$vars) for (var key in config.$vars) {
	      this._proxy[key] = config.$vars[key];
	    }

	    this._parse(config, this._proxy, "");
	  },
	  _parse: function (api, obj, prefix) {
	    for (var key in api) {
	      if (key === "$key" || key === "$vars") continue;
	      var val = api[key];

	      if (_typeof(val) == "object") {
	        var sub = obj[key] = {};

	        this._parse(val, sub, prefix + key + ".");
	      } else obj[key] = this._proxy_call(this, prefix + key);
	    }
	  },
	  _call: function (name, args) {
	    var def = this._deffer(this, name, args);

	    this._queue.push(def);

	    this._start_queue();

	    return def;
	  },
	  _start_queue: function () {
	    if (!this._timer) this._timer = setTimeout(bind(this._run_queue, this), 1);
	  },
	  _run_queue: function () {
	    var data = [],
	        defs = this._queue;

	    for (var i = 0; i < this._queue.length; i++) {
	      var def = this._queue[i];

	      if (def.$sync) {
	        defs.splice(i, 1);
	        i--;
	      } else data.push({
	        name: def.$name,
	        args: def.$args
	      });
	    }

	    if (defs.length) {
	      var request = ajax();

	      var pack = this._pack(data);

	      callEvent("onBeforeRemoteCall", [request, pack, {}]);
	      var promise = request.post(this._url, pack).then(function (response) {
	        var data = response.json();
	        var results = data.data;

	        for (var i = 0; i < results.length; i++) {
	          var res = results[i];
	          var error = results[i] && results[i][error_key];

	          if (error) {
	            callEvent("onRemoteError", [error]);
	            defs[i].reject(error);
	          } else {
	            defs[i].resolve(res);
	          }
	        }
	      }, function (res) {
	        for (var i = 0; i < defs.length; i++) {
	          defs[i].reject(res);
	        }

	        throw res;
	      });
	      callEvent("onAfterRemoteCall", [promise]);
	    }

	    this._queue = [];
	    this._timer = null;
	  },
	  _sync: function () {
	    var value = null;
	    this.$sync = true;
	    var data = [{
	      name: this.$name,
	      args: this.$args
	    }];

	    try {
	      var request = ajax();

	      var pack = this.$context._pack(data);

	      callEvent("onBeforeRemoteCall", [request, pack, {
	        sync: true
	      }]);
	      var xhr = request.sync().post(this.$context._url, pack);
	      callEvent("onAfterRemoteCall", [null]);
	      value = JSON.parse(xhr.responseText).data[0];
	      if (value[error_key]) value = null;
	    } catch (e) {} //eslint-disable-line


	    return value;
	  },
	  _deffer: function (master, name, args) {
	    var pr = Deferred.defer();
	    pr.sync = master._sync;
	    pr.$name = name;
	    pr.$args = args;
	    pr.$context = this;
	    return pr;
	  },
	  _proxy_call: function (master, name) {
	    return function () {
	      return master._call(name, [].slice.call(arguments));
	    };
	  },
	  _getProxy: function () {
	    return this._ready || this._proxy;
	  },
	  _pack: function (obj) {
	    return {
	      key: this._key,
	      payload: obj
	    };
	  }
	};

	function getApi(url, config) {
	  var ctx = new RemoteContext(url, config);

	  var proxy = ctx._getProxy();

	  for (var key in proxy) {
	    remote[key] = proxy[key];
	  }

	  return proxy;
	}

	var remote = function (url, config) {
	  if (_typeof(url) === "object") {
	    var scripts = document.getElementsByTagName("script");
	    config = url;
	    url = scripts[scripts.length - 1].src;
	    return getApi(url, config);
	  } else return getApi(url, config);
	};

	/*submits values*/

	function send(url, values, method, target) {
	  var form = create("FORM", {
	    "target": target || "_self",
	    "action": url,
	    "method": method || "POST"
	  }, "");

	  for (var k in values) {
	    var field = create("INPUT", {
	      "type": "hidden",
	      "name": k,
	      "value": values[k]
	    }, "");
	    form.appendChild(field);
	  }

	  form.style.display = "none";
	  document.body.appendChild(form);
	  form.submit();
	  document.body.removeChild(form);
	}

	function animate$1(obj, parent, config) {
	  var pobj = $$(parent);

	  if (pobj) {
	    var aniset = config || {
	      type: "slide",
	      direction: "left"
	    };

	    var d = pobj._viewobj.cloneNode(true);

	    var view = ui(obj, parent);

	    view._viewobj.parentNode.appendChild(d);

	    var line = animate.formLine(view._viewobj, d, aniset);

	    aniset.callback = function () {
	      animate.breakLine(line);
	    };

	    animate(line, aniset);
	    return view;
	  }
	}
	function animateView(view, stateHandler, config) {
	  view = $$(view);

	  if (view) {
	    config = config || {
	      type: "slide",
	      direction: "left"
	    };

	    var getHTML = function (view) {
	      var el = view._viewobj;
	      var css = el.className;
	      var content = el.innerHTML;
	      return "<div class='" + css + "' style='width:" + el.offsetWidth + "px;height:" + el.offsetHeight + "px;'>" + content + "</div>";
	    }; // get 'display' state of child nodes


	    var display = [];

	    for (var i = 0; i < view._viewobj.childNodes.length; i++) {
	      var node = view._viewobj.childNodes[i];
	      var value = node.currentStyle ? node.currentStyle.display : getComputedStyle(node, null).display;
	      display.push(value || "");
	    } // get current html content


	    var currentState = getHTML(view); // apply new state

	    if (typeof stateHandler == "function") {
	      stateHandler.call(this);
	    } // get new html content


	    var newState = getHTML(view); // insert elements into the view

	    var tempParent = view._viewobj.insertBefore(create("DIV", {
	      "class": "webix_view_animate",
	      "style": "width:" + view._viewobj.offsetWidth + "px;height:" + view._viewobj.offsetHeight + "px;"
	    }, newState + currentState), view._viewobj.firstChild); // hide child nodes


	    for (var _i = 1; _i < view._viewobj.childNodes.length; _i++) {
	      view._viewobj.childNodes[_i].style.display = "none";
	    } // animate inserted elements


	    var line = animate.formLine(tempParent.childNodes[0], tempParent.childNodes[1], config);

	    config.callback = function () {
	      if (tempParent) {
	        view._viewobj.removeChild(tempParent);

	        tempParent = null; // restore 'display' state of child nodes

	        for (var i = 0; i < view._viewobj.childNodes.length; i++) {
	          view._viewobj.childNodes[i].style.display = display[i];
	        }
	      }
	    };

	    animate(line, config);
	    return view;
	  }
	}

	function orientation() {
	  var new_orientation = !!(window.orientation % 180);
	  if (state.orientation === new_orientation) return;
	  state.orientation = new_orientation;
	  callEvent("onRotate", [new_orientation]);
	}

	if (env.touch) {
	  state.orientation = !!((isUndefined(window.orientation) ? 90 : window.orientation) % 180);
	  event$1(window, "onorientationchange" in window ? "orientationchange" : "resize", orientation);
	}

	function fullScreen() {
	  if (!env.touch) return;
	  addMeta("apple-mobile-web-app-capable", "yes");
	  addMeta("viewport", "initial-scale=1, maximum-scale=1, user-scalable=no, shrink-to-fit=no");

	  var fix = function () {
	    var x = window.innerWidth;
	    var y = window.innerHeight;

	    if (y) {
	      document.body.style.height = y + "px";
	      document.body.style.width = x + "px";
	    }

	    state._freeze_resize = false;
	    resize();
	  };

	  var onrotate = function () {
	    state._freeze_resize = true;
	    delay(fix, null, [], 500);
	  };

	  attachEvent("onRotate", onrotate);
	  orientation();
	  delay(onrotate);
	}

	var datafilter = {
	  textWaitDelay: 500,
	  "summColumn": {
	    getValue: function (node) {
	      return node.firstChild.innerHTML;
	    },
	    setValue: function () {},
	    refresh: function (master, node, value) {
	      var result = 0;
	      master.mapCells(null, value.columnId, null, 1, function (value) {
	        value = value * 1;
	        if (!isNaN(value)) result += value;
	      }, true);
	      if (value.format) result = value.format(result);
	      if (value.template) result = value.template({
	        value: result
	      });
	      node.firstChild.innerHTML = result;
	    },
	    trackCells: true,
	    render: function (master, config) {
	      if (config.template) config.template = template(config.template);
	      return "";
	    }
	  },
	  "masterCheckbox": {
	    getValue: function () {},
	    setValue: function () {},
	    getHelper: function (node, config) {
	      return {
	        check: function () {
	          config.checked = false;
	          node.onclick();
	        },
	        uncheck: function () {
	          config.checked = true;
	          node.onclick();
	        },
	        isChecked: function () {
	          return config.checked;
	        }
	      };
	    },
	    refresh: function (master, node, config) {
	      node.onclick = function () {
	        this.getElementsByTagName("input")[0].checked = config.checked = !config.checked;
	        var column = master.getColumnConfig(config.columnId);
	        var checked = config.checked ? column.checkValue : column.uncheckValue;
	        master.data.each(function (obj) {
	          obj[config.columnId] = checked;
	          master.callEvent("onCheck", [obj.id, config.columnId, checked]);
	          this.callEvent("onStoreUpdated", [obj.id, obj, "save"]);
	        });
	        master.refresh();
	      };
	    },
	    render: function (master, config) {
	      return "<input type='checkbox' " + (config.checked ? "checked='1'" : "") + ">";
	    }
	  },
	  "textFilter": {
	    getInputNode: function (node) {
	      return node.firstChild ? node.firstChild.firstChild : {
	        value: null
	      };
	    },
	    getValue: function (node) {
	      return this.getInputNode(node).value;
	    },
	    setValue: function (node, value) {
	      this.getInputNode(node).value = value;
	    },
	    refresh: function (master, node, value) {
	      node.component = master._settings.id;
	      master.registerFilter(node, value, this);
	      node._comp_id = master._settings.id;
	      if (value.value && this.getValue(node) != value.value) this.setValue(node, value.value);
	      node.onclick = preventEvent;

	      _event(node, "keydown", this._on_key_down);
	    },
	    render: function (master, config) {
	      if (this.init) this.init(config);
	      config.css = "webix_ss_filter";
	      return "<input " + (config.placeholder ? "placeholder=\"" + config.placeholder + "\" " : "") + "type='text'>";
	    },
	    _on_key_down: function (e) {
	      var id = this._comp_id; //tabbing through filters must not trigger filtering
	      //we can improve this functionality by preserving initial filter value
	      //and comparing new one with it

	      if ((e.which || e.keyCode) == 9) return;
	      if (this._filter_timer) window.clearTimeout(this._filter_timer);
	      this._filter_timer = window.setTimeout(function () {
	        var ui$$1 = $$(id); //ensure that ui is not destroyed yet

	        if (ui$$1) ui$$1.filterByAll();
	      }, datafilter.textWaitDelay);
	    }
	  },
	  "selectFilter": {
	    getInputNode: function (node) {
	      return node.firstChild ? node.firstChild.firstChild : {
	        value: null
	      };
	    },
	    getValue: function (node) {
	      return this.getInputNode(node).value;
	    },
	    setValue: function (node, value) {
	      this.getInputNode(node).value = value;
	    },
	    refresh: function (master, node, value) {
	      //value - config from header { contet: }
	      value.compare = value.compare || function (a, b) {
	        return a == b;
	      };

	      node.component = master._settings.id;
	      master.registerFilter(node, value, this);

	      var data = datafilter._get_data(master, value);

	      if (value.emptyOption !== false) data.unshift({
	        id: "",
	        value: value.emptyOption || ""
	      }); //slow in IE
	      //http://jsperf.com/select-options-vs-innerhtml

	      var select = document.createElement("select");

	      for (var i = 0; i < data.length; i++) {
	        var option = document.createElement("option");
	        option.value = data[i].id;
	        option.text = data[i].value;
	        select.add(option);
	      }

	      node.firstChild.innerHTML = "";
	      node.firstChild.appendChild(select);
	      if (value.value) this.setValue(node, value.value);
	      node.onclick = preventEvent;
	      select._comp_id = master._settings.id;

	      _event(select, "change", this._on_change);
	    },
	    render: function (master, config) {
	      if (this.init) this.init(config);
	      config.css = "webix_ss_filter";
	      return "";
	    },
	    _on_change: function () {
	      $$(this._comp_id).filterByAll();
	    }
	  },
	  _get_data: function (master, value) {
	    var data;
	    var options = value.options;

	    if (options) {
	      data = master._collectValues.call(options, "id", "value");
	    } else data = master.collectValues(value.columnId, value.collect);

	    return data;
	  }
	};
	datafilter.serverFilter = exports.extend({
	  $server: true,
	  _on_key_down: function (e) {
	    var id = this._comp_id,
	        code = e.which || e.keyCode; //ignore tab and navigation keys

	    if (code == 9 || code >= 33 && code <= 40) return;
	    if (this._filter_timer) window.clearTimeout(this._filter_timer);
	    this._filter_timer = window.setTimeout(function () {
	      $$(id).filterByAll();
	    }, datafilter.textWaitDelay);
	  }
	}, datafilter.textFilter);
	datafilter.serverSelectFilter = exports.extend({
	  $server: true,
	  _on_change: function () {
	    var id = this._comp_id;
	    $$(id).filterByAll();
	  }
	}, datafilter.selectFilter);
	datafilter.numberFilter = exports.extend({
	  init: function (config) {
	    config.prepare = function (value) {
	      var _this = this;

	      var equality = value.indexOf("=") != -1;
	      var intvalue = this.format(value);
	      if (intvalue === "") return "";
	      var compare;

	      if (value.indexOf(">") != -1) {
	        compare = this._greater;
	      } else if (value.indexOf("<") != -1) {
	        compare = this._lesser;
	      }

	      if (compare && equality) {
	        config.compare = function (a, b) {
	          return _this._equal(a, b) || compare(a, b);
	        };
	      } else {
	        config.compare = compare || this._equal;
	      }

	      return intvalue;
	    };
	  },
	  format: function (value) {
	    return value.replace(/[^\-.0-9]/g, "");
	  },
	  _greater: function (a, b) {
	    return a * 1 > b;
	  },
	  _lesser: function (a, b) {
	    return a !== "" && a * 1 < b;
	  },
	  _equal: function (a, b) {
	    return a !== "" && a * 1 == b;
	  }
	}, datafilter.textFilter);
	datafilter.dateFilter = exports.extend({
	  format: function (value) {
	    if (value === "") return "";
	    var date = new Date();

	    if (value.indexOf("today") != -1) {
	      date = wDate.dayStart(date);
	    } else if (value.indexOf("now") == -1) {
	      var parts = value.match(/[0-9]+/g);
	      if (!parts || !parts.length) return "";

	      if (parts.length < 3) {
	        parts.reverse();
	        date = new Date(parts[0], (parts[1] || 1) - 1, 1);
	      } else date = i18n.dateFormatDate(value.replace(/^[>< =]+/, ""));
	    }

	    return date.valueOf();
	  }
	}, datafilter.numberFilter);

	env.scrollSize = env.touch || env.$customScroll ? 0 : 17;
	ready(function () {
	  var size = _detectScrollSize();

	  env.scrollSize = env.touch ? 0 : size;
	});

	function _detectScrollSize() {
	  var div = create("div");
	  div.className = "webix_skin_mark";
	  div.style.cssText = "position:absolute;left:-1000px;width:100px;padding:0px;margin:0px;min-height:100px;overflow-y:scroll;";
	  document.body.appendChild(div);
	  var width = div.offsetWidth - div.clientWidth;
	  var name = {
	    200: "flat",
	    210: "compact",
	    230: "contrast",
	    240: "material",
	    250: "mini"
	  }[Math.floor(div.offsetHeight / 10) * 10];
	  document.body.removeChild(div);
	  if (name) set$1(name);
	  if (env.$customScroll) return 0;
	  return width;
	}

	var fixed = false;

	function _fixHeight() {
	  if (fixed) return;
	  addStyle("html, body{ height:100%; }");
	  document.body.className += " webix_full_screen";
	  Touch.limit(false);
	  fixed = true;
	}

	define("_fixHeight", _fixHeight);

	ui.animate = animate$1;
	ui.animateView = animateView;
	ui.freeze = freeze;
	ui.resize = resize;
	ui.each = each;
	ui.hasMethod = hasMethod;
	ui.datafilter = datafilter;
	ui.fullScreen = fullScreen;

	/*German (Germany) locale*/
	var de = {
	  groupDelimiter: ".",
	  groupSize: 3,
	  decimalDelimiter: ",",
	  decimalSize: 2,
	  dateFormat: "%d.%n.%Y",
	  timeFormat: "%H:%i",
	  longDateFormat: "%j. %F %Y",
	  fullDateFormat: "%j. %F %Y %H:%i",
	  am: null,
	  pm: null,
	  price: "{obj} €",
	  priceSettings: {
	    groupDelimiter: ".",
	    groupSize: 3,
	    decimalDelimiter: ",",
	    decimalSize: 2
	  },
	  calendar: {
	    monthFull: ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"],
	    monthShort: ["Jan", "Feb", "Mrz", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"],
	    dayFull: ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"],
	    dayShort: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"],
	    hours: "Stunden",
	    minutes: "Minuten",
	    done: "Fertig",
	    clear: "Entfernen",
	    today: "Heute"
	  },
	  dataExport: {
	    page: "Seite",
	    of: "aus"
	  },
	  PDFviewer: {
	    of: "aus",
	    automaticZoom: "Automatisch Zoom",
	    actualSize: "Aktuelles Ausmaß",
	    pageFit: "Seite Ausmaß",
	    pageWidth: "Seite Breite",
	    pageHeight: "Seite Höhe",
	    enterPassword: "Passwort eingeben",
	    passwordError: "Falsches passwort"
	  },
	  aria: {
	    calendar: "Kalender",
	    increaseValue: "Wert erhöhen",
	    decreaseValue: "Wert verringern",
	    navMonth: ["Vorheriger Monat", "Nächsten Monat"],
	    navYear: ["Vorheriges Jahr", "Nächstes Jahr"],
	    navDecade: ["Zurück Jahrzehnt", "Als nächstes Jahrzehnt"],
	    dateFormat: "%j. %F %Y",
	    monthFormat: "%F %Y",
	    yearFormat: "%Y",
	    hourFormat: "Stunden: %H",
	    minuteFormat: "Minuten: %i",
	    removeItem: "Element entfernen",
	    pages: ["Erste Seite", "Vorherige Seite", "Folgeseite", "Letzte Seite"],
	    page: "Seite",
	    headermenu: "Titelmenü",
	    openGroup: "Öffnen Spaltengruppe ",
	    closeGroup: "Schließen Spaltengruppe ",
	    closeTab: "Tab schließen",
	    showTabs: "Weitere Tabs",
	    resetTreeMap: "Zurück zur ursprünglichen Ansicht",
	    navTreeMap: "Aufleveln",
	    nextTab: "Weiter tab",
	    prevTab: "Zurück tab",
	    multitextSection: "Element hinzufügen",
	    multitextextraSection: "Element entfernen",
	    showChart: "Chart anzeigen",
	    hideChart: "Chart verstecken",
	    resizeChart: "Chart Größe ändern"
	  },
	  richtext: {
	    underline: "Unterstreichen",
	    bold: "Fettgedruckt",
	    italic: "Kursiv"
	  },
	  combo: {
	    select: "Auswählen",
	    selectAll: "Alles auswählen",
	    unselectAll: "Alles widerrufen"
	  },
	  message: {
	    ok: "OK",
	    cancel: "Abbrechen"
	  },
	  comments: {
	    send: "Absenden",
	    confirmMessage: "Der Kommentar wird entfernt. Sind Sie sicher?",
	    edit: "Redigieren",
	    remove: "Löschen",
	    placeholder: "Geben Sie hier ein..",
	    moreComments: "Mehr Kommentare"
	  }
	};

	/*Spanish (Spain, International Sort) locale*/
	var es = {
	  groupDelimiter: ".",
	  groupSize: 3,
	  decimalDelimiter: ",",
	  decimalSize: 2,
	  dateFormat: "%d/%n/%Y",
	  timeFormat: "%G:%i",
	  longDateFormat: "%d %F %Y",
	  fullDateFormat: "%d %F %Y %G:%i",
	  am: null,
	  pm: null,
	  price: "{obj} €",
	  priceSettings: {
	    groupDelimiter: ".",
	    groupSize: 3,
	    decimalDelimiter: ",",
	    decimalSize: 2
	  },
	  calendar: {
	    monthFull: ["enero", "febrero", "marzo", "abril", "mayo", "junio", "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre"],
	    monthShort: ["ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sep", "oct", "nov", "dic"],
	    dayFull: ["domingo", "lunes", "martes", "miércoles", "jueves", "viernes", "sábado"],
	    dayShort: ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
	    hours: "Horas",
	    minutes: "Minutos",
	    done: "Listo",
	    clear: "Reinicio",
	    today: "Hoy"
	  },
	  dataExport: {
	    page: "Página",
	    of: "de"
	  },
	  PDFviewer: {
	    of: "de",
	    automaticZoom: "Zoom automático",
	    actualSize: "Tamaño real",
	    pageFit: "Tamaño de página",
	    pageWidth: "Ancho de página",
	    pageHeight: "Altura de la página",
	    enterPassword: "Introduzca la contraseña",
	    passwordError: "Contraseña incorrecta"
	  },
	  aria: {
	    calendar: "Сalendario",
	    increaseValue: "Aumentar el valor",
	    decreaseValue: "Disminuye el valor",
	    navMonth: ["Mes anterior", "Próximo mes"],
	    navYear: ["Año anterior", "Próximo año"],
	    navDecade: ["Década anterior", "Próxima década"],
	    dateFormat: "%d %F %Y",
	    monthFormat: "%F %Y",
	    yearFormat: "%Y",
	    hourFormat: "Horas: %G",
	    minuteFormat: "Minutos: %i",
	    removeItem: "Retire el elemento",
	    pages: ["Primera página", "Pagina anterior", "Siguiente página", "Última página"],
	    page: "Página",
	    headermenu: "Menú de títulos",
	    openGroup: "Grupo de columnas abiertas",
	    closeGroup: "Primer grupo de columnas",
	    closeTab: "Cerrar tab",
	    showTabs: "Mostrar más tabs",
	    resetTreeMap: "Volver a la vista original",
	    navTreeMap: "Elevar a mismo nivel",
	    nextTab: "Siguiente tab",
	    prevTab: "Tab anterior",
	    multitextSection: "Añadir elemento",
	    multitextextraSection: "Retire el elemento",
	    showChart: "Espectáculo chart",
	    hideChart: "Esconder chart",
	    resizeChart: "Cambiar el tamaño el chart"
	  },
	  richtext: {
	    underline: "Subrayar",
	    bold: "Negrita",
	    italic: "Itálico"
	  },
	  combo: {
	    select: "Seleccionar",
	    selectAll: "Seleccionar todo",
	    unselectAll: "Deselecciona todo"
	  },
	  message: {
	    ok: "OK",
	    cancel: "Cancelar"
	  },
	  comments: {
	    send: "Enviar",
	    confirmMessage: "El comentario será eliminado. Estás seguro?",
	    edit: "Corregir",
	    remove: "Suprimir",
	    placeholder: "Escriba aquí..",
	    moreComments: "Más comentarios"
	  }
	};

	var fr = {
	  groupDelimiter: " ",
	  groupSize: 3,
	  decimalDelimiter: ",",
	  decimalSize: 2,
	  dateFormat: "%d/%m/%Y",
	  timeFormat: "%H:%i",
	  longDateFormat: "%d %F %Y",
	  fullDateFormat: "%d.%m.%Y %H:%i",
	  price: "{obj} €",
	  priceSettings: null,
	  //use number defaults
	  calendar: {
	    monthFull: ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"],
	    monthShort: ["Jan", "Fév", "Mar", "Avr", "Mai", "Juin", "Juil", "Aôu", "Sep", "Oct", "Nov", "Déc"],
	    dayFull: ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"],
	    dayShort: ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"],
	    hours: "Heures",
	    minutes: "Minutes",
	    done: "Fini",
	    clear: "Effacer",
	    today: "Aujourd'hui"
	  },
	  dataExport: {
	    page: "Page",
	    of: "sur"
	  },
	  PDFviewer: {
	    of: "sur",
	    automaticZoom: "Zoom automatique",
	    actualSize: "Taille actuelle",
	    pageFit: "Taille de la page",
	    pageWidth: "Largeur de la page",
	    pageHeight: "Hauteur de page",
	    enterPassword: "Entrez le mot de passe",
	    passwordError: "Mauvais mot de passe"
	  },
	  aria: {
	    calendar: "Сalendrier",
	    increaseValue: "Augmenter la valeur",
	    decreaseValue: "Diminution de la valeur",
	    navMonth: ["Le mois précédent", "Le mois prochain"],
	    navYear: ["Année précédente", "L'année prochaine"],
	    navDecade: ["Décennie précédente", "Suivant décennie"],
	    dateFormat: "%d %F %Y",
	    monthFormat: "%F %Y",
	    yearFormat: "%Y",
	    hourFormat: "Heures: %H",
	    minuteFormat: "Minutes: %i",
	    removeItem: "Retirer l'élément",
	    pages: ["Première page", "Page précédente", "Page suivante", "Dernière page"],
	    page: "Page",
	    headermenu: "Menu de titre",
	    openGroup: "Ouvrir groupe de colonnes ",
	    closeGroup: "Fermer groupe de colonnes",
	    closeTab: "Fermer tab",
	    showTabs: "Montrer plus tabs",
	    resetTreeMap: "Revenir à la vue originale",
	    navTreeMap: "Niveau supérieur",
	    nextTab: "Prochain tab",
	    prevTab: "Précédent tab",
	    multitextSection: "Ajouter l'élément",
	    multitextextraSection: "Retirer l'élément",
	    showChart: "Montrer chart",
	    hideChart: "Cacher chart",
	    resizeChart: "Redimensionner chart"
	  },
	  richtext: {
	    underline: "Souligner",
	    bold: "Gras",
	    italic: "Italique"
	  },
	  combo: {
	    select: "Sélectionner",
	    selectAll: "Tout sélectionner",
	    unselectAll: "Tout déselectionner"
	  },
	  message: {
	    ok: "OK",
	    cancel: "Annuler"
	  },
	  comments: {
	    send: "Envoyer",
	    confirmMessage: "Le commentaire sera supprimé. Êtes-vous sûr?",
	    edit: "Modifier",
	    remove: "Effacer",
	    placeholder: "Écrivez ici..",
	    moreComments: "Plus de commentaires"
	  }
	};

	/*Italian (Italy) locale*/
	var it = {
	  groupDelimiter: ".",
	  groupSize: 3,
	  decimalDelimiter: ",",
	  decimalSize: 2,
	  dateFormat: "%d/%m/%Y",
	  timeFormat: "%H:%i",
	  longDateFormat: "%j %F %Y",
	  fullDateFormat: "%j %F %Y %H:%i",
	  am: null,
	  pm: null,
	  price: "€ {obj}",
	  priceSettings: {
	    groupDelimiter: ".",
	    groupSize: 3,
	    decimalDelimiter: ",",
	    decimalSize: 2
	  },
	  calendar: {
	    monthFull: ["gennaio", "febbraio", "marzo", "aprile", "maggio", "giugno", "luglio", "agosto", "settembre", "ottobre", "novembre", "dicembre"],
	    monthShort: ["gen", "feb", "mar", "apr", "mag", "giu", "lug", "ago", "set", "ott", "nov", "dic"],
	    dayFull: ["domenica", "lunedì", "martedì", "mercoledì", "giovedì", "venerdì", "sabato"],
	    dayShort: ["dom", "lun", "mar", "mer", "gio", "ven", "sab"],
	    hours: "Orario",
	    minutes: "Minuti",
	    done: "Pronto",
	    clear: "Pulisci",
	    today: "Oggi"
	  },
	  dataExport: {
	    page: "Pagina",
	    of: "di"
	  },
	  PDFviewer: {
	    of: "di",
	    automaticZoom: "Zoom automatico",
	    actualSize: "Dimensione reale",
	    pageFit: "Dimensioni della pagina",
	    pageWidth: "Larghezza della pagina",
	    pageHeight: "Altezza della pagina",
	    enterPassword: "Inserisci la password",
	    passwordError: "Password errata"
	  },
	  aria: {
	    calendar: "Calendario",
	    increaseValue: "Aumenta il valore",
	    decreaseValue: "Riduci il valore",
	    navMonth: ["Il mese scorso", "Il prossimo mese"],
	    navYear: ["L'anno scorso", "L'anno prossimo"],
	    navDecade: ["Decennio precedente", "Prossimo decennio"],
	    dateFormat: "%j %F %Y",
	    monthFormat: "%F %Y",
	    yearFormat: "%Y",
	    hourFormat: "Orario: %H",
	    minuteFormat: "Minuti: %i",
	    removeItem: "Rimuovere l'elemento",
	    pages: ["Prima pagina", "Pagina precedente", "Pagina successiva", "Ultima pagina"],
	    page: "Pagina",
	    headermenu: "Menu del titolo",
	    openGroup: "Aperto gruppo di colonne",
	    closeGroup: "Chiudi gruppo di colonne",
	    closeTab: "Chiudi tab",
	    showTabs: "Mostra più tabs",
	    resetTreeMap: "Tornare alla vista originale",
	    navTreeMap: "Livello superiore",
	    nextTab: "Tab successivo",
	    prevTab: "Tab precedente",
	    multitextSection: "Aggiungi elemento",
	    multitextextraSection: "Rimuovere l'elemento",
	    showChart: "Mostrare grafico",
	    hideChart: "Nascondere grafico",
	    resizeChart: "Ridimensionare grafico"
	  },
	  richtext: {
	    underline: "Sottolineare",
	    bold: "Grassetto",
	    italic: "Corsivo"
	  },
	  combo: {
	    select: "Selezionare",
	    selectAll: "Seleziona tutto",
	    unselectAll: "Deseleziona tutto"
	  },
	  message: {
	    ok: "OK",
	    cancel: "Annullare"
	  },
	  comments: {
	    send: "Inviare",
	    confirmMessage: "Il commento verrà rimosso. Sei sicuro?",
	    edit: "Correggere",
	    remove: "Elimina",
	    placeholder: "Digitare qui..",
	    moreComments: "Altri commenti"
	  }
	};

	var ja = {
	  groupDelimiter: ",",
	  groupSize: 3,
	  decimalDelimiter: ".",
	  decimalSize: 2,
	  dateFormat: "%Y.%m.%d",
	  timeFormat: "%H:%i",
	  longDateFormat: "%Y年%m月%d日",
	  fullDateFormat: "%Y.%m.%d %H:%i",
	  price: "¥{obj}",
	  priceSettings: {
	    groupSize: 3,
	    groupDelimiter: ",",
	    decimalDelimiter: "",
	    decimalSize: 0
	  },
	  calendar: {
	    monthFull: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
	    monthShort: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
	    dayFull: ["日曜日", "月曜日", "火曜日", "水曜日", "木曜日", "金曜日", "土曜日"],
	    dayShort: ["日", "月", "火", "水", "木", "金", "土"],
	    hours: "営業時間",
	    minutes: "分",
	    done: "レディー",
	    clear: "削除する",
	    today: "今日"
	  },
	  dataExport: {
	    page: "ページ",
	    of: "から"
	  },
	  PDFviewer: {
	    of: "から",
	    automaticZoom: "自動ズーム",
	    actualSize: "実サイズ",
	    pageFit: "ページサイズ",
	    pageWidth: "ページ幅",
	    pageHeight: "ページの高さ",
	    enterPassword: "パスワードを入力する",
	    passwordError: "間違ったパスワード"
	  },
	  aria: {
	    calendar: "カレンダー",
	    increaseValue: "増加値",
	    decreaseValue: "数字を小さく",
	    navMonth: ["前の月", "来月"],
	    navYear: ["前年", "来年"],
	    navDecade: ["前の十年", "次の10年"],
	    dateFormat: "%Y年%m月%d日",
	    monthFormat: "%Y年%m月",
	    yearFormat: "%Y年",
	    hourFormat: "営業時間: %H",
	    minuteFormat: "分: %i",
	    removeItem: "要素を削除します",
	    pages: ["一ページ目", "前のページ", "次のページ", "最後のページ"],
	    page: "ページ",
	    headermenu: "ヘッダメニュー",
	    openGroup: "オープン列グループ",
	    closeGroup: "閉じる列グループ",
	    closeTab: "タブを閉じます",
	    showTabs: "複数のタブを表示します",
	    resetTreeMap: "元の表示に戻ります",
	    navTreeMap: "レベルパック",
	    nextTab: "次のタブ",
	    prevTab: "前のタブ",
	    multitextSection: "要素を追加します。",
	    multitextextraSection: "要素を削除します",
	    showChart: "靴チャート",
	    hideChart: "隠すチャート",
	    resizeChart: "グラフのサイズを変更"
	  },
	  richtext: {
	    underline: "アンダーライン",
	    bold: "大胆な",
	    italic: "イタリック"
	  },
	  combo: {
	    select: "選択する",
	    selectAll: "すべて選択",
	    unselectAll: "すべての選択を解除する"
	  },
	  message: {
	    ok: "OK",
	    cancel: "取り消す"
	  },
	  comments: {
	    send: "送信",
	    confirmMessage: "コメントは削除されます. 本気ですか？",
	    edit: "編集",
	    remove: "削除",
	    placeholder: "ここに入力..",
	    moreComments: "その他のコメント"
	  }
	};

	var pt = {
	  groupDelimiter: ".",
	  groupSize: 3,
	  decimalDelimiter: ",",
	  decimalSize: 2,
	  dateFormat: "%d/%m/%Y",
	  timeFormat: "%G:%i",
	  longDateFormat: "%d de %F de %Y",
	  fullDateFormat: "%d de %F de %Y %G:%i",
	  am: null,
	  pm: null,
	  price: "R$ {obj}",
	  priceSettings: {
	    groupDelimiter: ".",
	    groupSize: 3,
	    decimalDelimiter: ",",
	    decimalSize: 2
	  },
	  fileSize: ["b", "Kb", "Mb", "Gb", "Tb", "Pb", "Eb"],
	  calendar: {
	    monthFull: ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"],
	    monthShort: ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"],
	    dayFull: ["Domingo", "Segunda-Feira", "Terça-Feira", "Quarta-Feira", "Quinta-Feira", "Sexta-Feira", "Sábado"],
	    dayShort: ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"],
	    hours: "Horas",
	    minutes: "Minutos",
	    done: "Feito",
	    clear: "Limpar",
	    today: "Hoje"
	  },
	  dataExport: {
	    page: "Página",
	    of: "de"
	  },
	  PDFviewer: {
	    of: "de",
	    automaticZoom: "Zoom automático",
	    actualSize: "Tamanho atual",
	    pageFit: "Tamanho da página",
	    pageWidth: "Largura da página",
	    pageHeight: "Altura da página",
	    enterPassword: "Digite a senha",
	    passwordError: "Senha incorreta"
	  },
	  aria: {
	    calendar: "Calendário",
	    increaseValue: "Aumentar o valor",
	    decreaseValue: "Diminuir o valor",
	    navMonth: ["Mês anterior", "Próximo mês"],
	    navYear: ["Ano anterior", "Próximo ano"],
	    navDecade: ["Década anterior", "Próxima década"],
	    dateFormat: "%d de %F de %Y",
	    monthFormat: "%F de %Y",
	    yearFormat: "%Y",
	    hourFormat: "Horas: %G",
	    minuteFormat: "Minutos: %i",
	    removeItem: "Remover elemento",
	    pages: ["Primeira página", "Página anterior", "Próxima página", "Última página"],
	    page: "Página",
	    headermenu: "Menu de títulos",
	    openGroup: "Grupo coluna aberta",
	    closeGroup: "Fechar grupo de colunas",
	    closeTab: "Fechar tab",
	    showTabs: "Mostre mais tabs",
	    resetTreeMap: "Мoltar à vista original",
	    navTreeMap: "Upar",
	    nextTab: "Próximo tab",
	    prevTab: "Anterior tab",
	    multitextSection: "Adicionar elemento",
	    multitextextraSection: "Remover elemento",
	    showChart: "Exposição chart",
	    hideChart: "Esconder chart",
	    resizeChart: "Redimensionar chart"
	  },
	  richtext: {
	    underline: "Sublinhado",
	    bold: "Negrito",
	    italic: "itálico"
	  },
	  combo: {
	    select: "Selecionar",
	    selectAll: "Selecionar tudo",
	    unselectAll: "Desmarque todos"
	  },
	  message: {
	    ok: "OK",
	    cancel: "Cancelar"
	  },
	  comments: {
	    send: "Enviar",
	    confirmMessage: "Comentário será removido. Você tem certeza?",
	    edit: "Editar",
	    remove: "Excluir",
	    placeholder: "Digite aqui..",
	    moreComments: "Mais comentários"
	  }
	};

	/*Chinese (Simplified, PRC) locale*/
	var zh = {
	  groupDelimiter: ",",
	  groupSize: 3,
	  decimalDelimiter: ".",
	  decimalSize: 2,
	  dateFormat: "%Y/%m/%j",
	  timeFormat: "%G:%i",
	  longDateFormat: "%Y'年'%m'月'%j'日'",
	  fullDateFormat: "%Y'年'%m'月'%j'日' %G:%i",
	  am: ["上午", "上午"],
	  pm: ["下午", "下午"],
	  price: "¥{obj}",
	  priceSettings: {
	    groupDelimiter: ",",
	    groupSize: 3,
	    decimalDelimiter: ".",
	    decimalSize: 2
	  },
	  calendar: {
	    monthFull: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
	    monthShort: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
	    dayFull: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"],
	    dayShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
	    hours: "小时",
	    minutes: "分钟",
	    done: "完成",
	    clear: "清除",
	    today: "今天"
	  },
	  dataExport: {
	    page: "页",
	    of: "从"
	  },
	  PDFviewer: {
	    of: "从",
	    automaticZoom: "自动设置页面大小",
	    actualSize: "实际尺寸",
	    pageFit: "页面大小",
	    pageWidth: "页面宽度",
	    pageHeight: "页面高度",
	    enterPassword: "输入密码",
	    passwordError: "密码错误"
	  },
	  aria: {
	    calendar: "日历",
	    increaseValue: "增加值",
	    decreaseValue: "减少值",
	    navMonth: ["上个月", "下个月"],
	    navYear: ["上年", "明年"],
	    navDecade: ["过去十年", "下个十年"],
	    dateFormat: "%Y'年'%m'月'%j'日'",
	    monthFormat: "%Y'年'%m'月",
	    yearFormat: "%Y'年",
	    hourFormat: "小时: %G",
	    minuteFormat: "分钟: %i",
	    removeItem: "删除元素",
	    pages: ["第一页", "上一页", "下一页", "最后一页"],
	    page: "页",
	    headermenu: "标题菜单",
	    openGroup: "打开栏目组",
	    closeGroup: "关闭栏目组",
	    closeTab: "关闭标签",
	    showTabs: "显示更多选项卡",
	    resetTreeMap: "回到原来的视图",
	    navTreeMap: "升级",
	    nextTab: "下一个标签",
	    prevTab: "前一个标签",
	    multitextSection: "加元",
	    multitextextraSection: "删除元素",
	    showChart: "显示图表",
	    hideChart: "隐藏图表",
	    resizeChart: "调整图"
	  },
	  richtext: {
	    underline: "强调",
	    bold: "粗體",
	    italic: "斜体"
	  },
	  combo: {
	    select: "选择",
	    selectAll: "全选",
	    unselectAll: "全部取消选择"
	  },
	  message: {
	    ok: "好",
	    cancel: "取消"
	  },
	  comments: {
	    send: "发送",
	    confirmMessage: "评论将被删除. 你确定吗?",
	    edit: "编辑",
	    remove: "去掉",
	    placeholder: "在此输入..",
	    moreComments: "更多评论"
	  }
	};

	var ru = {
	  groupDelimiter: " ",
	  groupSize: 3,
	  decimalDelimiter: ",",
	  decimalSize: 2,
	  dateFormat: "%d.%m.%Y",
	  timeFormat: "%H:%i",
	  longDateFormat: "%d %F %Y",
	  fullDateFormat: "%d.%m.%Y %H:%i",
	  price: "{obj} руб.",
	  priceSettings: null,
	  //use number defaults
	  calendar: {
	    monthFull: ["Январь", "Февраль", "Март", "Апрель", "Maй", "Июнь", "Июль", "Август", "Сентябрь", "Oктябрь", "Ноябрь", "Декабрь"],
	    monthShort: ["Янв", "Фев", "Maр", "Aпр", "Maй", "Июн", "Июл", "Aвг", "Сен", "Окт", "Ноя", "Дек"],
	    dayFull: ["Воскресенье", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"],
	    dayShort: ["Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"],
	    hours: "Часы",
	    minutes: "Минуты",
	    done: "Гoтовo",
	    clear: "Очистить",
	    today: "Сегодня"
	  },
	  dataExport: {
	    page: "Страница",
	    of: "из"
	  },
	  PDFviewer: {
	    of: "из",
	    automaticZoom: "Автоматический зум",
	    actualSize: "Настоящий размер",
	    pageFit: "Размер страницы",
	    pageWidth: "Ширина страницы",
	    pageHeight: "Высота страницы",
	    enterPassword: "Введите пароль",
	    passwordError: "Неверный пароль"
	  },
	  aria: {
	    calendar: "Календарь",
	    increaseValue: "Увеличить значение",
	    decreaseValue: "Уменьшить значение",
	    navMonth: ["Предыдущий месяц", "Следующий месяц"],
	    navYear: ["Предыдущий год", "Следующий год"],
	    navDecade: ["Предыдущие десять лет", "Следующие десять лет"],
	    dateFormat: "%d %F %Y",
	    monthFormat: "%F %Y",
	    yearFormat: "%Y",
	    hourFormat: "Часы: %H",
	    minuteFormat: "Минуты: %i",
	    removeItem: "Удалить элемент",
	    pages: ["Первая страница", "Предыдущая страница", "Следующая страница", "Последняя страница"],
	    page: "Страница",
	    headermenu: "Меню шапки таблицы",
	    openGroup: "Развернуть группу столбцов",
	    closeGroup: "Свернуть группу столбцов",
	    closeTab: "Закрыть вкладку",
	    showTabs: "Показать больше вкладок",
	    resetTreeMap: "Вернуться к первоначальному представлению",
	    navTreeMap: "Перейти на уровень выше",
	    nextTab: "Следующая вкладка",
	    prevTab: "Предыдущая вкладка",
	    multitextSection: "Добавить элемент",
	    multitextextraSection: "Удалить элемент",
	    showChart: "Показать чарт",
	    hideChart: "Спрятать чарт",
	    resizeChart: "Изменить размер чарта"
	  },
	  richtext: {
	    underline: "Подчеркивание",
	    bold: "Жирный",
	    italic: "Курсив"
	  },
	  combo: {
	    select: "Выбрать",
	    selectAll: "Выбрать все",
	    unselectAll: "Сброс выбора"
	  },
	  message: {
	    ok: "OK",
	    cancel: "Отмена"
	  },
	  comments: {
	    send: "Отправить",
	    confirmMessage: "Комментарий будет удален. Вы уверены?",
	    edit: "Редактировать",
	    remove: "Удалить",
	    placeholder: "Введите текст..",
	    moreComments: "Больше комментариев"
	  }
	};

	var be = {
	  groupDelimiter: " ",
	  groupSize: 3,
	  decimalDelimiter: ",",
	  decimalSize: 2,
	  dateFormat: "%d.%m.%Y",
	  timeFormat: "%H:%i",
	  longDateFormat: "%d %F %Y",
	  fullDateFormat: "%d.%m.%Y %H:%i",
	  price: "{obj} руб.",
	  priceSettings: {
	    groupSize: 3,
	    groupDelimiter: " ",
	    decimalDelimiter: "",
	    decimalSize: 0
	  },
	  calendar: {
	    monthFull: ["Студзень", "Люты", "Сакавік", "Красавік", "Травень", "Чэрвень", "Ліпень", "Жнівень", "Верасень", "Кастрычнік", "Лістапад", "Снежань"],
	    monthShort: ["Студз", "Лют", "Сак", "Крас", "Трав", "Чэр", "Ліп", "Жнів", "Вер", "Каст", "Ліст", "Снеж"],
	    dayFull: ["Нядзеля", "Панядзелак", "Аўторак", "Серада", "Чацвер", "Пятніца", "Субота"],
	    dayShort: ["Нд", "Пн", "Аўт", "Ср", "Чцв", "Пт", "Сб"],
	    hours: "Гадзіны",
	    minutes: "Хвіліны",
	    done: "Гатова",
	    today: "Cёння",
	    clear: "Ачысціць"
	  },
	  dataExport: {
	    page: "Старонка",
	    of: "з"
	  },
	  PDFviewer: {
	    of: "з",
	    automaticZoom: "Аўтаматычны зум",
	    actualSize: "Сапраўдны памер",
	    pageFit: "Памер старонкі",
	    pageWidth: "Шырыня старонкі",
	    pageHeight: "Вышыня старонкі",
	    enterPassword: "Увядзіце пароль",
	    passwordError: "Няправільны пароль"
	  },
	  aria: {
	    calendar: "Каляндар",
	    increaseValue: "Павялічыць значэнне",
	    decreaseValue: "Паменшыць значэнне",
	    navMonth: ["Папярэдні месяц", "Наступны месяц"],
	    navYear: ["Папярэдні год", "Наступны год"],
	    navDecade: ["Папярэднія дзесяць год", "Наступныя дзесяць год"],
	    dateFormat: "%d %F %Y",
	    monthFormat: "%F %Y",
	    yearFormat: "%Y",
	    hourFormat: "Hours: %h",
	    minuteFormat: "Гадзіны: %i",
	    removeItem: "Прыбраць элемент",
	    pages: ["Першая старонка", "Папярэдняя старонка", "Наступная старонка", "Апошняя старонка"],
	    page: "Старонка",
	    headermenu: "Меню загалоўка",
	    openGroup: "Адкрыць групу слупкоў",
	    closeGroup: "Закрыць групу слупкоў",
	    closeTab: "Закрыць укладку",
	    showTabs: "Паказаць больш укладак",
	    resetTreeMap: "Вярнуцца да першапачатковага выгляду",
	    navTreeMap: "Падняцца на ўзровень вышэй",
	    nextTab: "Наступная ўкладка",
	    prevTab: "Папярэдняя ўкладка",
	    multitextSection: "Дадаць элемент",
	    multitextextraSection: "Прыбраць элемент",
	    showChart: "Паказаць графік",
	    hideChart: "Схаваць графік",
	    resizeChart: "Змяніць памер графіка"
	  },
	  richtext: {
	    underline: "Падкрэсліванне",
	    bold: "Паўтлусты",
	    italic: "Курсіў"
	  },
	  combo: {
	    select: "Абраць",
	    selectAll: "Абраць усё",
	    unselectAll: "Ачысціць ўсе"
	  },
	  message: {
	    ok: "ОК",
	    cancel: "Адмена"
	  },
	  comments: {
	    send: "Даслаць",
	    confirmMessage: "Каментарый будзе выдалены. Вы ўпэўнены?",
	    edit: "Рэдагаваць",
	    remove: "Выдаліць",
	    placeholder: "Пішыце тут..",
	    moreComments: "Больш каментарыяў"
	  }
	};

	// en-EN locale is bundled by default
	i18n.locales["de-DE"] = de; // spain
	i18n.locales["es-ES"] = es; // france
	i18n.locales["fr-FR"] = fr; // italy
	i18n.locales["it-IT"] = it; // japan
	i18n.locales["ja-JP"] = ja; // portuguese
	i18n.locales["pt-BR"] = pt; // chinese
	i18n.locales["zh-CN"] = zh; // russian
	i18n.locales["ru-RU"] = ru; // belarus
	i18n.locales["be-BY"] = be;

	var api$4 = {
	  name: "baselayout",
	  restore: function (state$$1, factory, configOnly) {
	    var out = this._restore(copy(state$$1), factory);

	    if (configOnly) return out;else ui(out.cols || out.rows, this);
	  },
	  _restore: function (state$$1, factory) {
	    if (state$$1.$layout) {
	      var sub = state$$1.cols || state$$1.rows;

	      for (var i = 0; i < sub.length; i++) {
	        sub[i] = this._restore(sub[i], factory);
	      }
	    } else {
	      return factory.call(this, state$$1);
	    }

	    return state$$1;
	  },
	  serialize: function (serializer) {
	    var out = [];
	    var childs = this.getChildViews();

	    for (var i = 0; i < childs.length; i++) {
	      var sub = childs[i];

	      if (sub.movePortlet) {
	        var child = sub.getChildViews();
	        out.push(serializer.call(this, child[child.length - 1]));
	      } else if (sub.serialize) {
	        // some kind of layout
	        out.push(sub.serialize(serializer, true));
	      } else {
	        // leaf view
	        out.push(serializer.call(this, sub));
	      }
	    }

	    var obj = {
	      $layout: true,
	      type: this.config.type
	    };
	    if (this.config.rows) obj.rows = out;else obj.cols = out;
	    return obj;
	  },
	  $init: function (config) {
	    this.$ready.push(this._parse_cells);
	    this._dataobj = this._contentobj;
	    this._layout_sizes = [];
	    this._responsive = [];
	    this._padding = {
	      top: 0,
	      left: 0,
	      right: 0,
	      bottom: 0
	    };

	    if (config.$topView) {
	      config.borderless = true;
	      config._inner = {
	        top: true,
	        left: true,
	        bottom: true,
	        right: true
	      };
	    }

	    if (config.isolate) exports.extend(this, IdSpace);
	  },
	  rows_setter: function (value) {
	    this._vertical_orientation = 1;
	    this._collection = value;
	    return true;
	  },
	  cols_setter: function (value) {
	    this._vertical_orientation = 0;
	    this.$view.style.whiteSpace = "nowrap";
	    this._collection = value;
	    return true;
	  },
	  _remove: function (view) {
	    var index$$1 = PowerArray.find.call(this._cells, view);
	    if (this._beforeRemoveView) this._beforeRemoveView(index$$1);
	    PowerArray.removeAt.call(this._cells, index$$1);
	    this.resizeChildren(true);
	  },
	  _replace: function (new_view, target_id) {
	    if (isUndefined(target_id)) {
	      for (var i = 0; i < this._cells.length; i++) {
	        this._cells[i].destructor();
	      }

	      this._collection = new_view;

	      this._parse_cells();
	    } else {
	      var source;

	      if (typeof target_id == "number") {
	        if (target_id < 0 || target_id > this._cells.length) target_id = this._cells.length;
	        PowerArray.insertAt.call(this._cells, new_view, target_id);
	        if (!new_view._settings.hidden) this._insertBeforeView(new_view, this._cells[target_id]);
	      } else {
	        source = $$(target_id);
	        target_id = PowerArray.find.call(this._cells, source);
	        assert(target_id != -1, "Attempt to replace the non-existing view");
	        if (!new_view._settings.hidden) this._insertBeforeView(new_view, source);
	        source.destructor();
	        this._cells[target_id] = new_view;
	      }

	      if (!this._vertical_orientation) this._fix_vertical_layout(new_view);
	    }

	    this.resizeChildren(true);
	    var form = this.elements ? this : this.getFormView();
	    if (form) form._recollect_elements();
	    callEvent("onReconstruct", [this]);
	  },
	  _fix_vertical_layout: function (cell) {
	    cell._viewobj.style.display = "inline-block";
	    cell._viewobj.style.verticalAlign = "top";
	  },
	  addView: function (view, index$$1) {
	    if (isUndefined(index$$1)) index$$1 = this._cells.length;
	    var top = this.$$ ? this : this.getTopParentView();
	    state._parent_cell = this;
	    var newui = top && top.ui ? top.ui(view, this, index$$1) : ui(view, this, index$$1);
	    state._parent_cell = null;
	    return newui._settings.id;
	  },
	  removeView: function (id) {
	    var view;
	    if (_typeof(id) != "object") view = $$(id) || (this.$$ ? this.$$(id) : null);else view = id;
	    var target = PowerArray.find.call(this._cells, view);

	    if (target >= 0) {
	      if (this._beforeRemoveView) this._beforeRemoveView(target, view);
	      var form = this.elements ? this : this.getFormView();

	      this._cells.splice(target, 1);

	      if (form) ui.each(view, function (sub) {
	        if (sub.name) delete form.getCleanValues()[sub.config.name];
	      }, form, true);
	      view.destructor();
	      this.resizeChildren(true);
	      if (form) form._recollect_elements();
	    } else assert(false, "Attemp to remove not existing view: " + id);

	    callEvent("onReconstruct", [this]);
	  },
	  reconstruct: function () {
	    this._hiddencells = 0;

	    this._replace(this._collection);
	  },
	  _hide: function (obj, settings, silent) {
	    if (obj._settings.hidden) return;
	    obj._settings.hidden = true;
	    remove(obj._viewobj);
	    this._hiddencells++;
	    if (!silent && !state._ui_creation) this.resizeChildren(true);
	  },
	  _signal_hidden_cells: function (view) {
	    if (view.callEvent) view.callEvent("onViewShow", []);
	  },
	  resizeChildren: function () {
	    if (state._freeze_resize) return;

	    if (this._layout_sizes) {
	      var parent = this.getParentView();

	      if (parent) {
	        if (parent.resizeChildren) return parent.resizeChildren();else return parent.resize();
	      }

	      var sizes = this.$getSize(0, 0);
	      var x, y, nx, ny;
	      nx = x = this._layout_sizes[0] || 0;
	      ny = y = this._layout_sizes[1] || 0; //for auto-fill content, use adjust strategy

	      if ((sizes[1] >= 100000 || sizes[3] >= 100000) && this._viewobj.parentNode) {
	        //in hidden container adjust doesn't work, so fallback to last known size
	        //also, ensure that min-size is not violated
	        nx = x = Math.max(sizes[0], this._settings.width || this._viewobj.parentNode.offsetWidth || x || 0);
	        ny = y = Math.max(sizes[2], this._settings.height || this._viewobj.parentNode.offsetHeight || y || 0);
	      }

	      if (!parent) {
	        //minWidth
	        if (sizes[0] > x) nx = sizes[0]; //minHeight

	        if (sizes[2] > y) ny = sizes[2];
	        var fullscreen = this._viewobj.parentNode == document.body && !this.setPosition; //maxWidth rule

	        if (!fullscreen && x > sizes[1]) nx = sizes[1]; //maxHeight rule

	        if (!fullscreen && y > sizes[3]) ny = sizes[3];
	        this.$setSize(nx, ny);
	      } else this._set_child_size(x, y);

	      if (state._responsive_exception) {
	        state._responsive_exception = false;
	        this.resizeChildren();
	      }

	      callEvent("onResize", []);
	    }
	  },
	  getChildViews: function () {
	    return this._cells;
	  },
	  index: function (obj) {
	    if (obj._settings) obj = obj._settings.id;

	    for (var i = 0; i < this._cells.length; i++) {
	      if (this._cells[i]._settings.id == obj) return i;
	    }

	    return -1;
	  },
	  _insertBeforeView: function (view, before) {
	    if (before) {
	      if (before._settings.hidden || view === before) {
	        //index of sibling cell, next to which new item will appear
	        var index$$1 = this.index(before) + 1; //locate nearest visible cell

	        while (this._cells[index$$1] && this._cells[index$$1]._settings.hidden) {
	          index$$1++;
	        }

	        before = this._cells[index$$1] ? this._cells[index$$1]._viewobj : null;
	      } else {
	        before = before._viewobj;
	      }
	    }

	    insertBefore(view._viewobj, before, this._dataobj || this._viewobj);
	  },
	  _show: function (obj, settings, silent) {
	    if (!obj._settings.hidden) return;

	    this._insertBeforeView(obj, obj);

	    obj._settings.hidden = false;
	    this._hiddencells--;

	    if (!silent) {
	      this.resizeChildren(true);
	      if (obj.refresh) obj.refresh();
	    }

	    if (obj.callEvent) {
	      obj.callEvent("onViewShow", []);
	      ui.each(obj, this._signal_hidden_cells);
	    }
	  },
	  showBatch: function (name, mode) {
	    var preserve = typeof mode != "undefined";
	    mode = mode !== false;

	    if (!preserve) {
	      if (this._settings.visibleBatch == name) return;
	      this._settings.visibleBatch = name;
	    } else this._settings.visibleBatch = "";

	    var show = [];

	    for (var i = 0; i < this._cells.length; i++) {
	      if (!this._cells[i]._settings.batch && !this._cells[i]._settings.hidden) show.push(this._cells[i]);else if (this._cells[i]._settings.batch == name) {
	        if (mode) show.push(this._cells[i]);else this._hide(this._cells[i], null, true);
	      } else if (!preserve) this._hide(this._cells[i], null, true);
	    }

	    for (var _i = 0; _i < show.length; _i++) {
	      this._show(show[_i], null, true);

	      show[_i]._render_hidden_views();
	    }

	    this.resizeChildren(true);
	  },
	  _parse_cells: function (collection) {
	    this._cells = [];
	    assert(collection, this.name + " was incorrectly defined. <br><br> You have missed rows|cols|cells|elements collection");

	    for (var i = 0; i < collection.length; i++) {
	      state._parent_cell = this;
	      if (!collection[i]._inner) collection[i].borderless = true;
	      this._cells[i] = ui._view(collection[i], this);
	      if (!this._vertical_orientation) this._fix_vertical_layout(this._cells[i]);

	      if (this._settings.visibleBatch && this._settings.visibleBatch != this._cells[i]._settings.batch && this._cells[i]._settings.batch) {
	        this._cells[i]._settings.hidden = true;
	        this._hiddencells++;
	      }

	      if (!this._cells[i]._settings.hidden) {
	        (this._dataobj || this._contentobj).appendChild(this._cells[i]._viewobj);

	        if (this._cells[i].$nospace) this._hiddencells++;
	      }
	    }

	    if (this._parse_cells_ext_end) this._parse_cells_ext_end(collection);
	  },
	  _bubble_size: function (prop, size, vertical) {
	    if (this._vertical_orientation != vertical) for (var i = 0; i < this._cells.length; i++) {
	      this._cells[i]._settings[prop] = size;
	      if (this._cells[i]._bubble_size) this._cells[i]._bubble_size(prop, size, vertical);
	    }
	  },
	  $getSize: function (dx, dy) {
	    debug_size_box_start(this, true);
	    var minWidth = 0;
	    var maxWidth = 100000;
	    var maxHeight = 100000;
	    var minHeight = 0;
	    if (this._vertical_orientation) maxHeight = 0;else maxWidth = 0;
	    var fixed = 0;
	    var fixed_count = 0;
	    var gravity = 0;
	    this._sizes = [];

	    for (var i = 0; i < this._cells.length; i++) {
	      //ignore hidden cells
	      if (this._cells[i]._settings.hidden) continue;

	      var sizes = this._sizes[i] = this._cells[i].$getSize(0, 0);

	      if (this._cells[i].$nospace) {
	        fixed_count++;
	        continue;
	      }

	      if (this._vertical_orientation) {
	        //take max minSize value
	        if (sizes[0] > minWidth) minWidth = sizes[0]; //take min maxSize value

	        if (sizes[1] < maxWidth) maxWidth = sizes[1];
	        minHeight += sizes[2];
	        maxHeight += sizes[3];

	        if (sizes[2] == sizes[3] && sizes[2] != -1) {
	          fixed += sizes[2];
	          fixed_count++;
	        } else gravity += sizes[4];
	      } else {
	        //take max minSize value
	        if (sizes[2] > minHeight) minHeight = sizes[2]; //take min maxSize value

	        if (sizes[3] < maxHeight) maxHeight = sizes[3];
	        minWidth += sizes[0];
	        maxWidth += sizes[1];

	        if (sizes[0] == sizes[1] && sizes[0] != -1) {
	          fixed += sizes[0];
	          fixed_count++;
	        } else gravity += sizes[4];
	      }
	    }

	    if (minHeight > maxHeight) maxHeight = minHeight;
	    if (minWidth > maxWidth) maxWidth = minWidth;
	    this._master_size = [fixed, this._cells.length - fixed_count, gravity];
	    this._desired_size = [minWidth + dx, minHeight + dy]; //get layout sizes

	    var self_size = baseview.api.$getSize.call(this, 0, 0); //use child settings if layout's one was not defined

	    if (self_size[1] >= 100000) self_size[1] = 0;
	    if (self_size[3] >= 100000) self_size[3] = 0;
	    self_size[0] = (self_size[0] || minWidth) + dx;
	    self_size[1] = Math.max(self_size[0], (self_size[1] || maxWidth) + dx);
	    self_size[2] = (self_size[2] || minHeight) + dy;
	    self_size[3] = Math.max(self_size[2], (self_size[3] || maxHeight) + dy);
	    debug_size_box_end(this, self_size);
	    if (!this._vertical_orientation && this._settings.responsive) self_size[0] = 0;
	    return self_size;
	  },
	  $setSize: function (x, y) {
	    this._layout_sizes = [x, y];
	    debug_size_box_start(this);
	    baseview.api.$setSize.call(this, x, y);

	    this._set_child_size(x, y);

	    debug_size_box_end(this, [x, y]);
	  },
	  _set_child_size_a: function (sizes, min, max) {
	    min = sizes[min];
	    max = sizes[max];
	    var height = min;

	    if (min != max) {
	      var ps = this._set_size_delta * sizes[4] / this._set_size_gravity;

	      if (ps < min) {
	        height = min;
	        this._set_size_gravity -= sizes[4];
	        this._set_size_delta -= height;
	      } else if (ps > max) {
	        height = max;
	        this._set_size_gravity -= sizes[4];
	        this._set_size_delta -= height;
	      } else {
	        return -1;
	      }
	    }

	    return height;
	  },
	  _responsive_hide: function (cell, mode) {
	    var target = $$(mode);

	    if (target === "hide" || !target) {
	      cell.hide();
	      cell._responsive_marker = "hide";
	    } else {
	      //for SideBar in Webix 1.9
	      if (!target) target = ui({
	        view: "popup",
	        body: [{}]
	      });
	      cell._responsive_width = cell._settings.width;
	      cell._responsive_height = cell._settings.height;
	      cell._responsive_marker = target._settings.id;
	      cell._settings.width = 0;
	      if (!cell._settings.height) cell._settings.autoheight = true;
	      ui(cell, target, this._responsive.length);
	    }

	    this._responsive.push(cell);
	  },
	  _responsive_show: function (cell) {
	    var target = cell._responsive_marker;
	    cell._responsive_marker = 0;

	    if (target === "hide" || !target) {
	      cell.show();
	    } else {
	      cell._settings.width = cell._responsive_width;
	      cell._settings.height = cell._responsive_height;
	      delete cell._settings.autoheight;
	      var index$$1 = 0;

	      while (this._cells[index$$1] && this._cells[index$$1]._settings.responsiveCell === false) {
	        index$$1++;
	      }

	      ui(cell, this, index$$1);
	    }

	    this._responsive.pop();
	  },
	  _responsive_cells: function (x) {
	    state._responsive_tinkery = true;

	    if (x + this._padding.left + this._padding.right + this._margin * (this._cells.length - 1) < this._desired_size[0]) {
	      var max = this._cells.length - 1;

	      for (var i = 0; i < max; i++) {
	        var cell = this._cells[i];

	        if (!cell._responsive_marker) {
	          if (cell._settings.responsiveCell !== false) {
	            this._responsive_hide(cell, this._settings.responsive);

	            callEvent("onResponsiveHide", [cell._settings.id]);
	            state._responsive_exception = true;
	            break;
	          } else {
	            max = this._cells.length;
	          }
	        }
	      }
	    } else if (this._responsive.length) {
	      var _cell = this._responsive[this._responsive.length - 1];
	      var dx = _cell._responsive_marker == "hide" ? 0 : _cell._responsive_width;

	      var px = _cell.$getSize(dx, 0);

	      if (px[0] + this._desired_size[0] + this._margin + 20 <= x) {
	        this._responsive_show(_cell);

	        callEvent("onResponsiveShow", [_cell._settings.id]);
	        state._responsive_exception = true;
	      }
	    }

	    state._responsive_tinkery = false;
	  },
	  _set_child_size: function (x, y) {
	    state._child_sizing_active = (state._child_sizing_active || 0) + 1;
	    if (!this._vertical_orientation && this._settings.responsive) this._responsive_cells(x, y);
	    this._set_size_delta = (this._vertical_orientation ? y : x) - this._master_size[0];
	    this._set_size_gravity = this._master_size[2];
	    var width = x;
	    var height = y;
	    var auto = [];

	    for (var i = 0; i < this._cells.length; i++) {
	      //ignore hidden cells
	      if (this._cells[i]._settings.hidden || !this._sizes[i]) continue;
	      var sizes = this._sizes[i];

	      if (this._vertical_orientation) {
	        height = this._set_child_size_a(sizes, 2, 3);

	        if (height < 0) {
	          auto.push(i);
	          continue;
	        }
	      } else {
	        width = this._set_child_size_a(sizes, 0, 1);

	        if (width < 0) {
	          auto.push(i);
	          continue;
	        }
	      }

	      this._cells[i].$setSize(width, height);
	    }

	    for (var _i2 = 0; _i2 < auto.length; _i2++) {
	      var index$$1 = auto[_i2];
	      var _sizes = this._sizes[index$$1];
	      var dx = Math.round(this._set_size_delta * _sizes[4] / this._set_size_gravity);
	      this._set_size_delta -= dx;
	      this._set_size_gravity -= _sizes[4];
	      if (this._vertical_orientation) height = dx;else {
	        width = dx;
	      }

	      this._cells[index$$1].$setSize(width, height);
	    }

	    state._child_sizing_active -= 1;
	  },
	  _next: function (obj, mode) {
	    var index$$1 = this.index(obj);
	    if (index$$1 == -1) return null;
	    return this._cells[index$$1 + mode];
	  },
	  _first: function () {
	    return this._cells[0];
	  }
	};
	var view$4 = exports.protoUI(api$4, EventSystem, baseview.view);
	var baselayout = {
	  api: api$4,
	  view: view$4
	};

	var api$5 = {
	  name: "spacer",
	  defaults: {
	    borderless: true
	  },
	  $init: function () {
	    this._viewobj.className += " webix_spacer";
	  }
	};
	var view$5 = exports.protoUI(api$5, base.view);

	var api$6 = {
	  name: "template",
	  $init: function (config) {
	    var subtype = this._template_types[config.type];

	    if (subtype) {
	      if (subtype.css && config.css) this._viewobj.className += " " + subtype.css;
	      exports.extend(config, subtype); //will reset borders for "section"

	      if (config.borderless) {
	        delete config._inner;

	        this._set_inner(config);
	      }
	    }

	    if (this._dataobj == this._viewobj) {
	      this._dataobj = create("DIV");
	      this._dataobj.className = " webix_template";

	      this._viewobj.appendChild(this._dataobj);
	    } else this._dataobj.className += " webix_template";

	    this.attachEvent("onAfterRender", this._correct_width_scroll);
	  },
	  setValues: function (obj, update) {
	    this.data = update ? exports.extend(this.data, obj, true) : obj;
	    this.render();
	  },
	  getValues: function () {
	    return this.data;
	  },
	  $skin: function () {
	    this._template_types.header.height = $active.barHeight - $active.borderWidth * 2;
	    this._template_types.section.height = $active.barHeight;
	  },
	  _template_types: {
	    "header": {
	      css: "webix_header"
	    },
	    "section": {
	      css: "webix_section",
	      borderless: true
	    },
	    "clean": {
	      css: "webix_clean",
	      borderless: true
	    }
	  },
	  onClick_setter: function (value) {
	    this.on_click = exports.extend(this.on_click || {}, value, true);
	    if (!this._onClick) exports.extend(this, MouseEvents);
	    return value;
	  },
	  defaults: {
	    template: template.empty
	  },
	  _render_me: function () {
	    this._not_render_me = false;

	    this._probably_render_me();

	    this.resize();
	  },
	  _probably_render_me: function () {
	    if (!this._not_render_me) {
	      this._not_render_me = true;
	      this.render();
	    }
	  },
	  src_setter: function (value) {
	    this._not_render_me = true;
	    if (!this.callEvent("onBeforeLoad", [])) return "";
	    ajax(value, bind(function (text) {
	      this._settings.template = template(text);

	      this._render_me();

	      this.callEvent("onAfterLoad", []);
	    }, this));
	    return value;
	  },
	  _correct_width_scroll: function () {
	    //we need to force auto height calculation after content change
	    //dropping the last_size flag will ensure that inner logic of $setSize will be processed
	    if (this._settings.autoheight) {
	      this._last_size = null;
	      this.resize();
	    }

	    if (this._settings.scroll && this._settings.scroll.indexOf("x") != -1) this._dataobj.style.width = this._dataobj.scrollWidth + "px";
	  },
	  content_setter: function (config) {
	    if (config) {
	      this._not_render_me = true;

	      this.render = function () {};

	      this._dataobj.appendChild(toNode(config));

	      delay(this._correct_width_scroll, this);
	    }
	  },
	  refresh: function () {
	    this.render();
	  },
	  setHTML: function (html) {
	    this._settings.template = function () {
	      return html;
	    };

	    this.refresh();
	  },
	  setContent: function (content) {
	    this._dataobj.innerHTML = "";
	    this.content_setter(content);
	  },
	  $setSize: function (x, y) {
	    if (base.api.$setSize.call(this, x, y)) {
	      this._probably_render_me();

	      if (this._settings.autoheight) {
	        var top = this.getTopParentView();
	        clearTimeout(top._template_resize_timer);
	        top._template_resize_timer = delay(this.resize, this);
	      }

	      return true;
	    }
	  },
	  $getSize: function (x, y) {
	    if (this._settings.autoheight && (!this._settings.type || this._settings.type == "clean")) this._settings.height = this._get_auto_height();
	    return base.api.$getSize.call(this, x, y);
	  },
	  _get_auto_height: function () {
	    var size;
	    var padding = $active.layoutPadding.space;

	    this._probably_render_me();

	    if (!this.isVisible()) {
	      //try getting height of a hidden template
	      size = getTextSize(this._toHTML(this.data) || this._dataobj.innerHTML, //check for config.content 
	      "webix_template", (this.$width || (this.getParentView() ? this.getParentView().$width : 0)) - padding).height;
	    } else {
	      this._dataobj.style.height = "auto";
	      size = this._dataobj.scrollHeight;
	      this._dataobj.style.height = "";
	    }

	    return size;
	  },
	  _one_time_scroll: true //scroll will appear only if set directly in config

	};
	var view$6 = exports.protoUI(api$6, Scrollable, AutoTooltip, AtomDataLoader, AtomRender, EventSystem, base.view);
	var template$1 = {
	  api: api$6,
	  view: view$6
	};

	/*scrollable view with another view insize*/

	var api$7 = {
	  name: "scrollview",
	  defaults: {
	    scroll: "y",
	    scrollSpeed: "0ms"
	  },
	  $init: function () {
	    this._viewobj.className += " webix_scrollview";
	  },
	  body_setter: function (config) {
	    config.borderless = true;
	    state._parent_cell = this;
	    this._body_cell = ui._view(config);

	    this._dataobj.appendChild(this._body_cell._viewobj);
	  },
	  getChildViews: function () {
	    return [this._body_cell];
	  },
	  getBody: function () {
	    return this._body_cell;
	  },
	  resizeChildren: function () {
	    if (!this._body_cell) return;
	    this._desired_size = this._body_cell.$getSize(0, 0);

	    this._resizeChildren();

	    callEvent("onResize", []);
	  },
	  _resizeChildren: function () {
	    var cx = Math.max(this._content_width, this._desired_size[0]);
	    var cy = Math.max(this._content_height, this._desired_size[2]);

	    this._body_cell.$setSize(cx, cy);

	    this._dataobj.style.width = this._body_cell._content_width + "px";
	    this._dataobj.style.height = this._body_cell._content_height + "px";

	    if (env.touch) {
	      var scroll = this.getScrollState();
	      var top = this._body_cell._content_height - this._content_height;
	      if (top < scroll.y) this.scrollTo(null, top);
	    }

	    if (state._responsive_exception) {
	      state._responsive_exception = false;
	      this._desired_size = this._body_cell.$getSize(0, 0);

	      this._resizeChildren();
	    }
	  },
	  $getSize: function (dx, dy) {
	    var desired_size = this._desired_size = this._body_cell.$getSize(0, 0);

	    var self_sizes = base.api.$getSize.call(this, dx, dy);
	    var scroll_size = this._native_scroll || env.scrollSize;

	    if (this._settings.scroll == "x") {
	      self_sizes[2] = Math.max(self_sizes[2], desired_size[2]) + scroll_size;
	      self_sizes[3] = Math.min(self_sizes[3], desired_size[3]) + scroll_size;
	    } else if (this._settings.scroll == "y") {
	      self_sizes[0] = Math.max(self_sizes[0], desired_size[0]) + scroll_size;
	      self_sizes[1] = Math.min(self_sizes[1], desired_size[1]) + scroll_size;
	    }

	    return self_sizes;
	  },
	  $setSize: function (x, y) {
	    var temp = env.scrollSize;
	    env.scrollSize = this._native_scroll || temp;
	    if (base.api.$setSize.call(this, x, y) || state._force_resize) this._resizeChildren();
	    env.scrollSize = temp;
	  },
	  scroll_setter: function (value) {
	    var custom = env.$customScroll;

	    if (typeof value == "string" && value.indexOf("native-") === 0) {
	      this._native_scroll = 17;
	      value = value.replace("native-");
	      env.$customScroll = false;
	    }

	    value = Scrollable.scroll_setter.call(this, value);
	    env.$customScroll = custom;
	    return value;
	  },
	  _replace: function (new_view) {
	    this._body_cell.destructor();

	    this._body_cell = new_view;

	    this._bodyobj.appendChild(this._body_cell._viewobj);

	    this.resize();
	  },
	  showView: function (id) {
	    var topPos = $$(id).$view.offsetTop - $$(id).$view.parentNode.offsetTop;
	    this.scrollTo(0, topPos);
	  }
	};
	var view$7 = exports.protoUI(api$7, Scrollable, EventSystem, base.view);

	var api$8 = {
	  name: "iframe",
	  $init: function () {
	    this._dataobj = this._contentobj;
	    this._contentobj.innerHTML = "<iframe style='width:100%; height:100%' frameborder='0' onload='var t = $$(this.parentNode.getAttribute(\"view_id\")); if (t) t.callEvent(\"onAfterLoad\",[]);' src='about:blank'></iframe>";
	  },
	  load: function (value) {
	    this.src_setter(value);
	  },
	  src_setter: function (value) {
	    if (!this.callEvent("onBeforeLoad", [])) return "";
	    this.getIframe().src = value;
	    return value;
	  },
	  getIframe: function () {
	    return this._contentobj.getElementsByTagName("iframe")[0];
	  },
	  getWindow: function () {
	    return this.getIframe().contentWindow;
	  }
	};
	var view$8 = exports.protoUI(api$8, base.view, EventSystem);

	var api$9 = {
	  name: "layout",
	  $init: function () {
	    this._hiddencells = 0;
	  },
	  defaults: {
	    type: "line"
	  },
	  _parse_cells: function () {
	    if (this._parse_cells_ext) collection = this._parse_cells_ext(collection);

	    if (!this._parse_once) {
	      this._viewobj.className += " webix_layout_" + (this._settings.type || "");
	      this._parse_once = 1;
	    }

	    if (this._settings.margin !== undefined) this._margin = this._settings.margin;
	    if (this._settings.padding !== undefined && _typeof(this._settings.padding) !== "object") this._padding.left = this._padding.right = this._padding.top = this._padding.bottom = this._settings.padding;
	    if (this._settings.paddingX !== undefined) this._padding.left = this._padding.right = this._settings.paddingX;
	    if (this._settings.paddingY !== undefined) this._padding.top = this._padding.bottom = this._settings.paddingY;
	    if (_typeof(this._settings.padding) === "object") exports.extend(this._padding, this._settings.padding, true);
	    if (this._padding.left || this._padding.right || this._padding.top || this._padding.bottom) this._padding.defined = true; //if layout has paddings we need to set the visible border 

	    if (this._hasBorders() && !this._settings.borderless) {
	      this._contentobj.style.borderWidth = "1px"; //if layout has border - normal bordering rules are applied

	      this._render_borders = true;
	    }

	    var collection = this._collection;
	    if (this._settings.borderless) this._settings._inner = {
	      top: true,
	      left: true,
	      right: true,
	      bottom: true
	    };

	    this._beforeResetBorders(collection);

	    baselayout.api._parse_cells.call(this, collection);

	    this._afterResetBorders(collection);
	  },
	  $getSize: function (dx, dy) {
	    dx = dx || 0;
	    dy = dy || 0;
	    var correction = this._margin * (this._cells.length - this._hiddencells - 1);

	    if (this._render_borders || this._hasBorders()) {
	      var _borders = this._settings._inner;

	      if (_borders) {
	        dx += (_borders.left ? 0 : 1) + (_borders.right ? 0 : 1);
	        dy += (_borders.top ? 0 : 1) + (_borders.bottom ? 0 : 1);
	      }
	    }

	    if (!this._settings.height) dy += this._padding.top + this._padding.bottom + (this._vertical_orientation ? correction : 0);
	    if (!this._settings.width) dx += this._padding.left + this._padding.right + (this._vertical_orientation ? 0 : correction);
	    return baselayout.api.$getSize.call(this, dx, dy);
	  },
	  $setSize: function (x, y) {
	    this._layout_sizes = [x, y];
	    debug_size_box_start(this);
	    if (this._hasBorders() || this._render_borders) base.api.$setSize.call(this, x, y);else baseview.api.$setSize.call(this, x, y); //form with scroll

	    y = this._content_height;
	    x = this._content_width;
	    var config = this._settings;

	    if (config.scroll) {
	      y = Math.max(y, this._desired_size[1]);
	      x = Math.max(x, this._desired_size[0]);
	    }

	    this._set_child_size(x, y);

	    debug_size_box_end(this, [x, y]);
	  },
	  _set_child_size: function (x, y) {
	    var correction = this._margin * (this._cells.length - this._hiddencells - 1);
	    y -= this._padding.top + this._padding.bottom;
	    x -= this._padding.left + this._padding.right;
	    if (this._vertical_orientation) y -= correction;else x -= correction;
	    return baselayout.api._set_child_size.call(this, x, y);
	  },
	  resizeChildren: function (structure_changed) {
	    if (structure_changed) {
	      this._last_size = null; //forces children resize

	      var config = [];

	      for (var i = 0; i < this._cells.length; i++) {
	        var cell = this._cells[i];
	        config[i] = cell._settings;
	        var n = cell._layout_sizes && !cell._render_borders || cell._settings.borderless ? "0px" : "1px";
	        cell._viewobj.style.borderTopWidth = cell._viewobj.style.borderBottomWidth = cell._viewobj.style.borderLeftWidth = cell._viewobj.style.borderRightWidth = n;
	      }

	      this._beforeResetBorders(config);

	      for (var _i = 0; _i < config.length; _i++) {
	        if (config[_i].borderless && this._cells[_i]._set_inner) this._cells[_i]._set_inner(config[_i]);
	      }

	      this._afterResetBorders(this._cells);
	    }

	    if (state._responsive_tinkery) return;
	    baselayout.api.resizeChildren.call(this);
	  },
	  _hasBorders: function () {
	    return this._padding.defined && this._margin > 0 && !this._cleanlayout;
	  },
	  _beforeResetBorders: function (collection) {
	    if (this._hasBorders() && (!this._settings.borderless || this._settings.type == "space")) {
	      for (var i = 0; i < collection.length; i++) {
	        if (!collection[i]._inner || !collection[i].borderless) collection[i]._inner = {
	          top: false,
	          left: false,
	          right: false,
	          bottom: false
	        };
	      }
	    } else {
	      for (var _i2 = 0; _i2 < collection.length; _i2++) {
	        collection[_i2]._inner = clone(this._settings._inner);
	      }

	      var mode = false;
	      if (this._cleanlayout) mode = true;
	      var maxlength = collection.length;

	      if (this._vertical_orientation) {
	        for (var _i3 = 1; _i3 < maxlength - 1; _i3++) {
	          collection[_i3]._inner.top = collection[_i3]._inner.bottom = mode;
	        }

	        if (maxlength > 1) {
	          if (this._settings.type != "head") collection[0]._inner.bottom = mode;

	          while (collection[maxlength - 1].hidden && maxlength > 1) {
	            maxlength--;
	          }

	          if (maxlength > 0) collection[maxlength - 1]._inner.top = mode;
	        }
	      } else {
	        for (var _i4 = 1; _i4 < maxlength - 1; _i4++) {
	          collection[_i4]._inner.left = collection[_i4]._inner.right = mode;
	        }

	        if (maxlength > 1) {
	          if (this._settings.type != "head") collection[0]._inner.right = mode;
	          collection[maxlength - 1]._inner.left = mode;

	          while (maxlength > 1 && collection[maxlength - 1].hidden) {
	            maxlength--;
	          }

	          if (maxlength > 0) collection[maxlength - 1]._inner.left = mode;
	        }
	      }
	    }
	  },
	  _fix_container_borders: function (style, inner) {
	    if (inner.top) style.borderTopWidth = "0px";
	    if (inner.left) style.borderLeftWidth = "0px";
	    if (inner.right) style.borderRightWidth = "0px";
	    if (inner.bottom) style.borderBottomWidth = "0px";
	  },
	  _afterResetBorders: function (collection) {
	    var start = 0;

	    for (var i = 0; i < collection.length; i++) {
	      var cell = this._cells[i];
	      var s_inner = cell._settings._inner;

	      if (cell._settings.hidden && this._cells[i + 1]) {
	        var s_next = this._cells[i + 1]._settings._inner;
	        if (!s_inner.top) s_next.top = false;
	        if (!s_inner.left) s_next.left = false;
	        if (i == start) start++;
	      }

	      this._fix_container_borders(cell._viewobj.style, cell._settings._inner);
	    }

	    var style = this._vertical_orientation ? "marginLeft" : "marginTop";
	    var contrstyle = this._vertical_orientation ? "marginTop" : "marginLeft";
	    var padding = this._vertical_orientation ? this._padding.left : this._padding.top;
	    var contrpadding = this._vertical_orientation ? this._padding.top : this._padding.left; //add top offset to all

	    for (var _i5 = 0; _i5 < collection.length; _i5++) {
	      this._cells[_i5]._viewobj.style[style] = padding + "px";
	    } //add left offset to first cell


	    if (this._cells.length) this._cells[start]._viewobj.style[contrstyle] = contrpadding + "px"; //add offset between cells

	    for (var index = start + 1; index < collection.length; index++) {
	      this._cells[index]._viewobj.style[contrstyle] = this._margin + "px";
	    }
	  },
	  type_setter: function (value) {
	    this._margin = typeof this._margin_set[value] !== "undefined" ? this._margin_set[value] : this._margin_set["line"];
	    this._padding.left = this._padding.right = this._padding.top = this._padding.bottom = typeof this._margin_set[value] !== "undefined" ? this._padding_set[value] : this._padding_set["line"];
	    this._cleanlayout = value == "material" || value == "clean";
	    if (value == "material") this._settings.borderless = true;
	    return value;
	  },
	  $skin: function () {
	    this._margin_set = $active.layoutMargin;
	    this._padding_set = $active.layoutPadding;
	  }
	};
	var view$9 = exports.protoUI(api$9, baselayout.view);
	var layout = {
	  api: api$9,
	  view: view$9
	}; //not necessary anymore
	//preserving for backward compatibility

	view$9.call(-1);

	var api$a = {
	  name: "accordion",
	  defaults: {
	    panelClass: "accordionitem",
	    multi: false,
	    collapsed: false
	  },
	  $init: function () {
	    this._viewobj.setAttribute("role", "tablist");

	    this._viewobj.setAttribute("aria-multiselectable", "true");
	  },
	  _replace: function (newview) {
	    layout.api._replace.apply(this, arguments);

	    if (newview.collapsed_setter && newview.refresh) {
	      newview.refresh();
	    }
	  },
	  _parse_cells: function () {
	    var panel = this._settings.panelClass;
	    var cells = this._collection;

	    for (var i = 0; i < cells.length; i++) {
	      if ((cells[i].body || cells[i].header) && !cells[i].view && !cells[i].align) cells[i].view = panel;
	      if (isUndefined(cells[i].collapsed)) cells[i].collapsed = this._settings.collapsed;
	    }

	    this._skin_render_collapse = true;

	    layout.api._parse_cells.call(this);

	    this._skin_render_collapse = false;

	    for (var _i = 0; _i < this._cells.length; _i++) {
	      if (this._cells[_i].name == panel) this._cells[_i].refresh();
	      this._cells[_i]._accLastChild = false;
	    }

	    var found = false;

	    for (var _i2 = this._cells.length - 1; _i2 >= 0 && !found; _i2--) {
	      if (!this._cells[_i2]._settings.hidden) {
	        this._cells[_i2]._accLastChild = true;
	        found = true;
	      }
	    }
	  },
	  _afterOpen: function (view) {
	    if (this._settings.multi === false && this._skin_render_collapse !== true) {
	      for (var i = 0; i < this._cells.length; i++) {
	        if (view != this._cells[i] && !this._cells[i]._settings.collapsed && this._cells[i].collapse) this._cells[i].collapse();
	      }
	    }

	    if (view.callEvent) {
	      view.callEvent("onViewShow", []);
	      each(view, this._signal_hidden_cells);
	    }
	  },
	  _canCollapse: function (view) {
	    if (this._settings.multi === true || this._skin_render_collapse) return true; //can collapse only if you have other item to open

	    for (var i = 0; i < this._cells.length; i++) {
	      if (view != this._cells[i] && !this._cells[i]._settings.collapsed && this._cells[i].isVisible() && !this._cells[i].$nospace) return true;
	    }

	    return false;
	  },
	  $skin: function () {
	    var defaults = this.defaults;
	    if ($active.accordionType) defaults.type = $active.accordionType;
	  }
	};
	var view$a = exports.protoUI(api$a, layout.view);
	var base$1 = {
	  api: api$a,
	  view: view$a
	};

	var api$b = {
	  name: "headerlayout",
	  defaults: {
	    type: "accordion",
	    multi: "mixed",
	    collapsed: false
	  }
	};
	var view$b = exports.protoUI(api$b, base$1.view);

	var api$c = {
	  name: "accordionitem",
	  $init: function (config) {
	    this._viewobj.innerHTML = "<div tabindex='0' " +
	    /*@attr*/
	    "webix_ai_id" + "='" + config.id + "'  class='webix_accordionitem_header'><div class='webix_accordionitem_button' ></div><div class='webix_accordionitem_label' ></div></div><div class='webix_accordionitem_body'></div>";
	    this._contentobj = this._viewobj;
	    this._headobj = this._contentobj.childNodes[0];
	    if (!config.header) this._headobj.style.display = "none";
	    this._headlabel = this._contentobj.childNodes[0].childNodes[1];
	    this._headbutton = this._contentobj.childNodes[0].childNodes[0];
	    this._bodyobj = this._contentobj.childNodes[1];
	    this._viewobj.className += " webix_accordionitem";
	    this._head_cell = this._body_cell = null;
	    this._cells = true;

	    this._bodyobj.setAttribute("role", "tabpanel");

	    this._headobj.setAttribute("role", "tab");

	    this.attachEvent("onKeyPress", this._onKeyPress);
	  },
	  _remove: function () {
	    this.body_setter();
	  },
	  _replace: function (new_view) {
	    this._body_cell.destructor();

	    this._body_cell = new_view;

	    this._bodyobj.appendChild(this._body_cell._viewobj);

	    this.resize();
	  },
	  _id:
	  /*@attr*/
	  "webix_ai_id",
	  getChildViews: function () {
	    return [this._body_cell];
	  },
	  body_setter: function (value) {
	    if (_typeof(value) != "object") value = {
	      template: value
	    };
	    value._inner = {
	      top: true,
	      left: true,
	      right: true,
	      bottom: true
	    };
	    state._parent_cell = this;
	    this._body_cell = ui._view(value);

	    this._bodyobj.appendChild(this._body_cell._viewobj);

	    return value;
	  },
	  header_setter: function (value) {
	    if (value) value = template(value);
	    return value;
	  },
	  headerAlt_setter: function (value) {
	    if (value) value = template(value);
	    return value;
	  },
	  $getSize: function (dx, dy) {
	    var size = this._body_cell.$getSize(0, 0); //apply external border to inner content sizes


	    var _borders = this._settings._inner;

	    if (_borders) {
	      dx += (_borders.left ? 0 : 1) + (_borders.right ? 0 : 1);
	      dy += (_borders.top ? 0 : 1) + (_borders.bottom ? 0 : 1);
	    }

	    var header = 0;
	    var self_size = baseview.api.$getSize.call(this, 0, 0); //use child settings if layout's one was not defined

	    self_size[0] = (self_size[0] || size[0]) + dx;
	    if (self_size[1] >= 100000) self_size[1] = size[1];
	    self_size[1] += dx;
	    self_size[2] = (self_size[2] || size[2]) + dy;
	    var fixedHeight = self_size[3] < 100000;
	    if (!fixedHeight) self_size[3] = size[3];
	    self_size[3] += dy;

	    if (this.getParentView()._vertical_orientation) {
	      if (this._settings.collapsed) {
	        self_size[2] = self_size[3] = this._getHeaderSize() + dy;
	      } else if (this._settings.header) header = this._settings.headerHeight;
	    } else {
	      if (this._settings.collapsed) self_size[0] = self_size[1] = this._getHeaderSize() + dx;
	      if (this._settings.header) header = this._settings.headerHeight;
	    } //include header in total height calculation


	    if (!fixedHeight) {
	      self_size[2] += header;
	      self_size[3] += header;
	    }

	    debug_size_box(this, self_size, true);
	    return self_size;
	  },
	  on_click: {
	    webix_accordionitem_header: function (e) {
	      this._toggle(e);

	      return false;
	    },
	    webix_accordionitem_header_v: function (e) {
	      this._toggle(e);

	      return false;
	    }
	  },
	  _toggle: function () {
	    this.define("collapsed", !this._settings.collapsed);
	  },
	  collapsed_setter: function (value) {
	    if (this._settings.header === false) return; //use last layout element if parent is not known yet

	    var parent = this.getParentView();

	    if (parent) {
	      if (!value) this._expand();else {
	        if (parent._canCollapse(this)) this._collapse();else {
	          var success = 0;
	          if (parent._cells.length > 1) for (var i = 0; i < parent._cells.length; i++) {
	            var sibl = parent._cells[i];

	            if (this != sibl && sibl.isVisible() && sibl.expand) {
	              sibl.expand();

	              this._collapse();

	              success = 1;
	              break;
	            }
	          }
	          if (!success) return;
	        }
	      }
	      this._settings.collapsed = value;
	      if (!value) parent._afterOpen(this);
	      this.refresh();
	      if (!state._ui_creation) this.resize();
	      parent.callEvent("onAfter" + (value ? "Collapse" : "Expand"), [this._settings.id]);
	      this._settings.$noresize = value;
	    }

	    return value;
	  },
	  collapse: function () {
	    this.define("collapsed", true);

	    UIManager._moveChildFocus(this);
	  },
	  expand: function () {
	    this.define("collapsed", false);
	  },
	  _show: function () {
	    this.show();
	  },
	  _hide: function () {
	    this.hide();
	  },
	  _expand: function () {
	    this._bodyobj.style.display = "";
	    removeCss(this.$view, "collapsed");
	    removeCss(this._headobj, "collapsed");

	    this._headobj.setAttribute("aria-expanded", "true");
	  },
	  _collapse: function () {
	    if (this._settings.headerAlt) this._headlabel.innerHTML = this._settings.headerAlt();
	    this._bodyobj.style.display = "none";
	    addCss(this.$view, "collapsed");
	    addCss(this._headobj, "collapsed");

	    this._headobj.setAttribute("aria-expanded", "false");
	  },
	  refresh: function () {
	    var template$$1 = this._settings[this._settings.collapsed ? "headerAlt" : "header"] || this._settings.header;

	    if (template$$1) {
	      this._headlabel.innerHTML = template$$1();

	      this._headbutton.setAttribute("aria-label", template$$1());
	    }

	    var css = this.getParentView()._vertical_orientation ? "vertical" : "horizontal";

	    if (this._viewobj.className.indexOf(" " + css) < 0) {
	      addCss(this._viewobj, css);
	    } //fix collapsed columns in IE8


	    if (!env.transform) {
	      addCss(this._viewobj, "webix_ie", true);
	    }
	  },
	  _getHeaderSize: function () {
	    return this._settings.collapsed ? this._settings.headerAltHeight : this._settings.headerHeight;
	  },
	  $setSize: function (x, y) {
	    if (base.api.$setSize.call(this, x, y) || this._getHeaderSize() != this._last_set_header_size) {
	      x = this._content_width;
	      y = this._content_height;

	      var headerSize = this._last_set_header_size = this._getHeaderSize(); //-(this._settings._inner.top?0:1);


	      if (this._settings.header) {
	        this._headobj.style.height = headerSize + "px";
	        this._headobj.style.width = "auto";
	        this._headobj.style[env.transform] = "";
	        this._headobj.style.borderBottomWidth = (this._settings.collapsed ? 0 : 1) + "px";

	        if (this.getParentView()._vertical_orientation || !this._settings.collapsed) {
	          y -= this._getHeaderSize();
	        } else if (this._settings.collapsed) {
	          //-2 - borders
	          if (animate.isSupported()) {
	            this._headobj.style.width = y + "px";
	            this._headobj.style.height = x + 3 + "px";
	            var d = Math.floor(y / 2 - x / 2) + (x - this._settings.headerAltHeight) / 2;
	            this._headobj.style[env.transform] = "rotate(90deg) translate(" + d + "px, " + (d + 1) + "px)";
	          } else {
	            //IE8 fix
	            this._headobj.style.width = x + "px";
	            this._headobj.style.height = y + 3 + "px";
	          }
	        }
	      }

	      if (!this._settings.collapsed) {
	        this._body_cell.$setSize(x, y);

	        this._last_size_y = y;
	      }
	    } else if (!this._settings.collapsed) {
	      var body = this._body_cell;
	      if (this._last_size_y) body.$setSize(this._content_width, this._last_size_y);
	    }
	  },
	  $skin: function () {
	    var defaults = this.defaults;
	    defaults.headerAltHeight = defaults.headerHeight = $active.barHeight - $active.borderWidth * 2;
	  },
	  defaults: {
	    header: false,
	    headerAlt: false,
	    body: ""
	  }
	};
	var view$c = exports.protoUI(api$c, MouseEvents, EventSystem, base.view);

	var api$d = {
	  name: "resizearea",
	  defaults: {
	    dir: "x"
	  },
	  $init: function (config) {
	    var dir = config.dir || "x";
	    var node = toNode(config.container);
	    var size = dir == "x" ? "width" : "height";
	    var margin = config.margin ? config.margin + "px" : 0;
	    this._key_property = dir == "x" ? "left" : "top";
	    this._viewobj = create("DIV", {
	      "class": "webix_resize_area webix_dir_" + dir
	    }); //[[COMPAT]] FF12 can produce 2 move events

	    _event(this._viewobj, env.mouse.down, stopEvent);

	    if (margin) {
	      if (dir == "x") margin = margin + " 0 " + margin;else margin = "0 " + margin + " 0 " + margin;
	    }

	    this._dragobj = create("DIV", {
	      "class": "webix_resize_handle_" + dir,
	      "style": margin ? "padding:" + margin : ""
	    }, "<div class='webix_handle_content'></div>");
	    this._originobj = create("DIV", {
	      "class": "webix_resize_origin_" + dir
	    });

	    if (config[size]) {
	      this._originobj.style[size] = config[size] + (config.border ? 1 : 0) + "px";
	      this._dragobj.style[size] = config[size] + "px";
	    }

	    if (config.cursor) this._dragobj.style.cursor = this._originobj.style.cursor = this._viewobj.style.cursor = config.cursor;
	    this._moveev = event$1(node, env.mouse.move, this._onmove, {
	      bind: this
	    });
	    this._upev = event$1(document.body, env.mouse.up, this._onup, {
	      bind: this
	    });
	    this._dragobj.style[this._key_property] = this._originobj.style[this._key_property] = config.start + "px";
	    node.appendChild(this._viewobj);
	    node.appendChild(this._dragobj);
	    node.appendChild(this._originobj);
	  },
	  _onup: function () {
	    this.callEvent("onResizeEnd", [this._last_result]);
	    eventRemove(this._moveev);
	    eventRemove(this._upev);
	    remove(this._viewobj);
	    remove(this._dragobj);
	    remove(this._originobj);
	    this._viewobj = this._dragobj = this._originobj = null;
	  },
	  _onmove: function (e) {
	    var eventPos = pos(e);
	    this._last_result = (this._settings.dir == "x" ? eventPos.x : eventPos.y) + this._settings.start - this._settings.eventPos;
	    this._dragobj.style[this._key_property] = this._last_result + "px";
	    this.callEvent("onResize", [this._last_result]);
	  }
	};
	var view$d = exports.protoUI(api$d, EventSystem, Settings);

	var api$e = {
	  name: "resizer",
	  defaults: {
	    width: 7,
	    height: 7
	  },
	  $init: function (config) {
	    assert(this.getParentView(), "Resizer can't be initialized outside a layout");
	    this._viewobj.className += " webix_resizer";

	    var space = this.getParentView()._margin;

	    _event(this._viewobj, env.mouse.down, this._rsDown, {
	      bind: this
	    });

	    event$1(document.body, env.mouse.up, this._rsUp, {
	      bind: this
	    });

	    var dir = this._getResizeDir();

	    this._rs_started = false;
	    this._resizer_dir = dir;
	    this._resizer_dim = dir == "x" ? "width" : "height";
	    if (dir == "x") config.height = 0;else config.width = 0;

	    if (space > 0) {
	      this._viewobj.className += " webix_resizer_v" + dir;
	      this._viewobj.style.marginRight = "-" + space + "px";
	      if (dir == "x") config.width = space;else config.height = space;
	      this.$nospace = true;
	    } else this._viewobj.className += " webix_resizer_" + dir;

	    this._viewobj.innerHTML = "<div class='webix_resizer_content'></div>";
	    if (dir == "y" && space > 0) this._viewobj.style.marginBottom = "-" + (config.height || this.defaults.height) + "px";

	    this._viewobj.setAttribute(
	    /*@attr*/
	    "webix_disable_drag", "true");

	    this._viewobj.setAttribute("tabindex", "-1");

	    this._viewobj.setAttribute("aria-grabbed", "false");
	  },
	  _rsDown: function (e) {
	    var cells = this._getResizerCells(); //some sibling can block resize


	    if (cells && !this._settings.disabled) {
	      e = e || event$1;
	      this._rs_started = true;
	      this._rs_process = pos(e);
	      this._rsLimit = [];

	      this._viewobj.setAttribute("aria-grabbed", "true");

	      for (var i = 0; i < 2; i++) {
	        cells[i].$view.setAttribute("aria-dropeffect", "move");
	      }

	      this._viewobj.setAttribute("aria-dropeffect", "move");

	      this._rsStart(e, cells[0]);
	    }
	  },
	  _rsUp: function () {
	    this._rs_started = false;
	    this._rs_process = false;
	  },
	  _rsStart: function (e, cell) {
	    var dir, cellOffset, pos$$1, posParent, start;
	    e = e || event$1;
	    dir = this._resizer_dir;
	    /*layout position:relative to place absolutely positioned elements in it*/

	    this.getParentView()._viewobj.style.position = "relative";
	    pos$$1 = offset(this._viewobj);
	    posParent = offset(this.getParentView()._viewobj);
	    start = pos$$1[dir] - posParent[dir];
	    cellOffset = offset(cell.$view)[dir] - offset(this.getParentView().$view)[dir];
	    this._rs_progress = [dir, cell, start, cellOffset];
	    /*resizer stick (resizerea ext)*/

	    this._resizeStick = new ui.resizearea({
	      container: this.getParentView()._viewobj,
	      dir: dir,
	      eventPos: this._rs_process[dir],
	      start: start - 1,
	      height: this.$height,
	      width: this.$width,
	      border: 1,
	      margin: this.getParentView()._padding[dir === "x" ? "left" : "top"]
	    });
	    /*stops resizing on stick mouseup*/

	    this._resizeStick.attachEvent("onResizeEnd", bind(this._rsEnd, this));
	    /*needed to stop stick moving when the limit for dimension is reached*/


	    this._resizeStick.attachEvent("onResize", bind(this._rsResizeHandler, this));

	    addCss(document.body, "webix_noselect", 1);
	  },
	  _getResizeDir: function () {
	    return this.getParentView()._vertical_orientation ? "y" : "x";
	  },
	  _rsResizeHandler: function () {
	    var cells, config, cDiff, diff, dir, i, limits, limitSizes, sizes, totalSize;

	    if (this._rs_progress) {
	      cells = this._getResizerCells();
	      dir = this._rs_progress[0];
	      /*vector distance between resizer and stick*/

	      diff = this._resizeStick._last_result - this._rs_progress[2];
	      /*new sizes for the resized cells, taking into account the stick position*/

	      sizes = this._rsGetDiffCellSizes(cells, dir, diff);
	      /*sum of cells dimensions*/

	      totalSize = cells[0]["$" + this._resizer_dim] + cells[1]["$" + this._resizer_dim];
	      /*max and min limits if they're set*/

	      limits = dir == "y" ? ["minHeight", "maxHeight"] : ["minWidth", "maxWidth"];

	      for (i = 0; i < 2; i++) {
	        config = cells[i]._settings;
	        cDiff = i ? -diff : diff;
	        /*if cDiff is positive, the size of i cell is increased*/

	        /*if size is bigger than max limit or size is smaller than min limit*/

	        var min = config[limits[0]];
	        var max = config[limits[1]];

	        if (cDiff > 0 && max && max <= sizes[i] || cDiff < 0 && (min || 3) >= sizes[i]) {
	          this._rsLimit[i] = cDiff > 0 ? max : min || 3;
	          /*new sizes, taking into account max and min limits*/

	          limitSizes = this._rsGetLimitCellSizes(cells, dir);
	          /*stick position*/

	          this._resizeStick._dragobj.style[dir == "y" ? "top" : "left"] = this._rs_progress[3] + limitSizes[0] + "px";
	          return;
	        } else if (sizes[i] < 3) {
	          /*cells size can not be less than 1*/
	          this._resizeStick._dragobj.style[dir == "y" ? "top" : "left"] = this._rs_progress[3] + i * totalSize + 1 + "px";
	        } else {
	          this._rsLimit[i] = null;
	        }
	      }
	    }
	  },
	  _getResizerCells: function () {
	    var cells, i, res;
	    cells = this.getParentView()._cells;

	    for (i = 0; i < cells.length; i++) {
	      if (cells[i] == this) {
	        res = [this._getRsCell(cells, i, 1, -1), this._getRsCell(cells, i, 1, 1)];
	        if (!res[0] || !res[1]) res = null;
	        return res;
	      }
	    }
	  },
	  _getRsCell: function (cells, i, step, dir) {
	    var cell = cells[i + dir * step];
	    if (cell && cell._settings.hidden) return this._getRsCell(cells, i, step + 1, dir);else if (cell && cell._settings.$noresize) return null;else return cell;
	  },
	  _rsEnd: function (result) {
	    if (typeof result == "undefined") return;
	    var cells, dir, diff, size;

	    var vertical = this.getParentView()._vertical_orientation;

	    this._resizerStick = null;

	    if (this._rs_progress) {
	      dir = this._rs_progress[0];
	      diff = result - this._rs_progress[2];
	      cells = this._getResizerCells();

	      if (cells[0] && cells[1]) {
	        /*new cell sizes*/
	        size = this._rsGetCellSizes(cells, dir, diff);

	        for (var i = 0; i < 2; i++) {
	          //cell has not fixed size, of fully fixed layout
	          var cell_size = cells[i].$getSize(0, 0);

	          if (vertical ? cell_size[2] == cell_size[3] : Math.abs(cell_size[1] - cell_size[0]) < 3) {
	            /*set fixed sizes for both cells*/
	            cells[i]._settings[this._resizer_dim] = size[i];
	            if (cells[i]._bubble_size) cells[i]._bubble_size(this._resizer_dim, size[i], vertical);
	          } else {
	            var actualSize = cells[i].$view[vertical ? "offsetHeight" : "offsetWidth"]; //cells[i]["$"+this._resizer_dim];

	            cells[i]._settings.gravity = size[i] / actualSize * cells[i]._settings.gravity;
	          }
	        }

	        cells[0].resize();

	        for (var _i = 0; _i < 2; _i++) {
	          if (cells[_i].callEvent) cells[_i].callEvent("onViewResize", []);

	          cells[_i].$view.removeAttribute("aria-dropeffect");
	        }

	        callEvent("onLayoutResize", [cells]);
	      }

	      this._rs_progress = false;
	    }

	    this._rs_progress = false;
	    this._rs_started = false;
	    this._rsLimit = null;
	    removeCss(document.body, "webix_noselect");

	    this._viewobj.setAttribute("aria-grabbed", "false");

	    this._viewobj.removeAttribute("aria-dropeffect");
	  },
	  _rsGetLimitCellSizes: function (cells) {
	    var size1, size2, totalSize;
	    totalSize = cells[0]["$" + this._resizer_dim] + cells[1]["$" + this._resizer_dim];

	    if (this._rsLimit[0]) {
	      size1 = this._rsLimit[0];
	      size2 = totalSize - size1;
	    } else if (this._rsLimit[1]) {
	      size2 = this._rsLimit[1];
	      size1 = totalSize - size2;
	    }

	    return [size1, size2];
	  },
	  _rsGetDiffCellSizes: function (cells, dir, diff) {
	    var sizes = [];
	    var styleDim = this._resizer_dim == "height" ? "offsetHeight" : "offsetWidth";

	    for (var i = 0; i < 2; i++) {
	      sizes[i] = cells[i].$view[styleDim] + (i ? -1 : 1) * diff;
	    }

	    return sizes;
	  },
	  _rsGetCellSizes: function (cells, dir, diff) {
	    var i, sizes, totalSize;
	    /*if max or min dimentsions are set*/

	    if (this._rsLimit[0] || this._rsLimit[1]) {
	      sizes = this._rsGetLimitCellSizes(cells, dir);
	    } else {
	      sizes = this._rsGetDiffCellSizes(cells, dir, diff);

	      for (i = 0; i < 2; i++) {
	        /*if stick moving is stopped outsize cells borders*/
	        if (sizes[i] < 0) {
	          totalSize = sizes[0] + sizes[1];
	          sizes[i] = 1;
	          sizes[1 - i] = totalSize - 1;
	        }
	      }
	    }

	    return sizes;
	  }
	};
	var view$e = exports.protoUI(api$e, MouseEvents, Destruction, base.view);

	var api$f = {
	  name: "align",
	  defaults: {
	    borderless: true,
	    left: 0,
	    top: 0,
	    right: 0,
	    bottom: 0
	  },
	  $init: function () {
	    this._viewobj.className += " webix_view_align";
	  },
	  getChildViews: function () {
	    return [this._body_cell];
	  },
	  body_setter: function (value) {
	    value._inner = {
	      top: false,
	      left: false,
	      right: false,
	      bottom: false
	    };
	    state._parent_cell = this;
	    this._body_cell = ui._view(value);

	    this._viewobj.appendChild(this._body_cell._viewobj);

	    return value;
	  },
	  align_setter: function (value) {
	    if (typeof value === "string") value = value.split(",");
	    this._x_align = this._y_align = this._p_align = "";

	    for (var i = 0; i < value.length; i++) {
	      var c = value[i];
	      if (c === "center" || c === "left" || c === "right") this._x_align = c;
	      if (c === "top" || c === "bottom" || c === "middle") this._y_align = c;
	      if (c === "absolute") this._x_align = this._y_align = this._p_align = "precise";
	    }

	    return value;
	  },
	  getBody: function () {
	    return this._body_cell;
	  },
	  $setSize: function (x, y) {
	    base.api.$setSize.call(this, x, y);
	    var dx, dy;

	    if (this._p_align) {
	      dx = x - this._settings.left - this._settings.right;
	      dy = y - this._settings.top - this._settings.bottom;
	    } else {
	      dx = this._desired_size[0] || x;
	      dy = this._desired_size[2] || y;
	    }

	    this._body_cell.$setSize(dx, dy);

	    var box = this._body_cell._viewobj;
	    if (this._x_align == "center") box.style.marginLeft = Math.ceil((x - dx) / 2) + "px";else if (this._x_align == "right") box.style.marginLeft = x - dx + "px";else box.style.marginLeft = (this._p_align ? this._settings.left : 0) + "px";
	    if (this._y_align == "middle") box.style.marginTop = Math.ceil((y - dy) / 2) + "px";else if (this._y_align == "bottom") box.style.marginTop = y - dy + "px";else box.style.marginTop = (this._p_align ? this._settings.top : 0) + "px";
	  },
	  $getSize: function (dx, dy) {
	    var size = this._desired_size = this._body_cell.$getSize(0, 0);

	    var self_size = baseview.api.$getSize.call(this, 0, 0);

	    if (this._p_align) {
	      dx += this._settings.left + this._settings.right;
	      dy += this._settings.top + this._settings.bottom;
	    }

	    if (!this._x_align || this._p_align) {
	      self_size[0] = size[0] + dx;
	      self_size[1] = size[1] + dx;
	    } else {
	      self_size[0] = (self_size[0] || size[0]) + dy;
	      self_size[1] += dx;
	    }

	    if (!this._y_align || this._p_align) {
	      self_size[2] = size[2] + dy;
	      self_size[3] = size[3] + dy;
	    } else {
	      self_size[2] = (self_size[2] || size[2]) + dy;
	      self_size[3] += dy;
	    }

	    return self_size;
	  }
	};
	var view$f = exports.protoUI(api$f, base.view);

	var api$g = {
	  name: "multiview",
	  defaults: {
	    animate: {}
	  },
	  setValue: function (val) {
	    $$(val).show();
	  },
	  getValue: function () {
	    return this.getActiveId();
	  },
	  $init: function () {
	    this._active_cell = 0;
	    this._vertical_orientation = 1;
	    this._viewobj.style.position = "relative";
	    this._viewobj.className += " webix_multiview";
	    this._back_queue = [];
	  },
	  _ask_render: function (cell_id, view_id) {
	    var cell = $$(cell_id);

	    if (!cell._render_hash) {
	      cell._render_queue = [];
	      cell._render_hash = {};
	    }

	    if (!cell._render_hash[view_id]) {
	      cell._render_hash[view_id] = true;

	      cell._render_queue.push(view_id);
	    }
	  },
	  _render_activation: function (cell_id) {
	    var cell = $$(cell_id);
	    if (this._settings.keepViews) cell._viewobj.style.display = "";
	    /*back array*/

	    if (this._back_queue[this._back_queue.length - 2] != cell_id) {
	      if (this._back_queue.length == 10) this._back_queue.splice(0, 1);

	      this._back_queue.push(cell_id);
	    } else this._back_queue.splice(this._back_queue.length - 1, 1);

	    if (cell._render_hash) {
	      for (var i = 0; i < cell._render_queue.length; i++) {
	        var subcell = $$(cell._render_queue[i]); //cell can be already destroyed

	        if (subcell) subcell.render();
	      }

	      cell._render_queue = [];
	      cell._render_hash = {};
	    }
	  },
	  addView: function (view, index$$1) {
	    var inc = !isUndefined(index$$1) && index$$1 <= this._active_cell ? 1 : 0;
	    var id = baselayout.api.addView.apply(this, arguments);

	    if (this._cells.length > 1) {
	      if (this._settings.keepViews) $$(id)._viewobj.style.display = "none";else remove($$(id)._viewobj);
	      this._active_cell += inc;
	    }

	    return id;
	  },
	  _beforeRemoveView: function (index$$1) {
	    //removing current view
	    if (index$$1 == this._active_cell) {
	      var next = index$$1 ? index$$1 - 1 : 1;

	      if (this._cells[next]) {
	        this._in_animation = false;

	        this._show(this._cells[next], false);
	      }
	    }

	    if (index$$1 < this._active_cell) this._active_cell--;
	  },
	  //necessary, as we want to ignore hide calls for elements in multiview
	  _hide: function () {},
	  _parse_cells: function (collection) {
	    collection = collection || this._collection;

	    for (var i = 0; i < collection.length; i++) {
	      collection[i]._inner = this._settings.borderless ? {
	        top: 1,
	        left: 1,
	        right: 1,
	        bottom: 1
	      } : this._settings._inner || {};
	    }

	    baselayout.api._parse_cells.call(this, collection);

	    for (var _i = 1; _i < this._cells.length; _i++) {
	      if (this._settings.keepViews) this._cells[_i]._viewobj.style.display = "none";else remove(this._cells[_i]._viewobj);
	    }

	    for (var _i2 = 0; _i2 < collection.length; _i2++) {
	      var cell = this._cells[_i2];
	      if (cell._cells && !cell._render_borders) continue;
	      var _inner = cell._settings._inner;
	      if (_inner.top) cell._viewobj.style.borderTopWidth = "0px";
	      if (_inner.left) cell._viewobj.style.borderLeftWidth = "0px";
	      if (_inner.right) cell._viewobj.style.borderRightWidth = "0px";
	      if (_inner.bottom) cell._viewobj.style.borderBottomWidth = "0px";

	      cell._viewobj.setAttribute("role", "tabpanel");
	    }

	    this._render_activation(this.getActiveId());
	  },
	  cells_setter: function (value) {
	    assert(value && value.length, "Multiview must have at least one view in 'cells'");
	    this._collection = value;
	  },
	  _getDirection: function (next, active) {
	    var dir = (this._settings.animate || {}).direction;
	    var vx = dir == "top" || dir == "bottom";
	    return next < active ? vx ? "bottom" : "right" : vx ? "top" : "left";
	  },
	  _show: function (obj, animation_options) {
	    var parent = this.getParentView();
	    if (parent && parent.getTabbar) parent.getTabbar().setValue(obj._settings.$id || obj._settings.id);
	    if (this._in_animation) return delay(this._show, this, [obj, animation_options], 100);

	    var _next_cell = -1;

	    for (var i = 0; i < this._cells.length; i++) {
	      if (this._cells[i] == obj) {
	        _next_cell = i;
	        break;
	      }
	    }

	    if (_next_cell < 0 || _next_cell == this._active_cell) return;
	    var prev = this._cells[this._active_cell];
	    var next = this._cells[_next_cell];
	    prev.$getSize(0, 0); //need to be moved in animate

	    if ((animation_options || typeof animation_options == "undefined") && animate.isSupported() && this._settings.animate) {
	      var aniset = exports.extend({}, this._settings.animate);
	      if (this._settings.keepViews) aniset.keepViews = true;
	      aniset.direction = this._getDirection(_next_cell, this._active_cell);
	      aniset = Settings._mergeSettings(animation_options || {}, aniset);
	      var line = animate.formLine(next._viewobj, prev._viewobj, aniset);
	      next.$getSize(0, 0);
	      next.$setSize(this._content_width, this._content_height);
	      var callback_original = aniset.callback;

	      aniset.callback = function () {
	        animate.breakLine(line, this._settings.keepViews);
	        this._in_animation = false;
	        if (callback_original) callback_original.call(this);
	        callback_original = aniset.master = aniset.callback = null;
	        this.resize();
	      };

	      aniset.master = this;
	      this._active_cell = _next_cell;

	      this._render_activation(this.getActiveId());

	      animate(line, aniset);
	      this._in_animation = true;
	    } else {
	      // browsers which don't support transform and transition, or animate:false in config
	      if (this._settings.keepViews) {
	        prev._viewobj.style.display = "none";
	      } else {
	        remove(prev._viewobj);

	        this._viewobj.appendChild(this._cells[i]._viewobj);
	      }

	      this._active_cell = _next_cell;
	      this.resizeChildren();

	      this._render_activation(this.getActiveId());
	    }

	    if (next.callEvent) {
	      next.callEvent("onViewShow", []);
	      each(next, this._signal_hidden_cells);
	    }

	    this.callEvent("onViewChange", [prev._settings.id, next._settings.id]);
	  },
	  $getSize: function (dx, dy) {
	    if (!this._cells.length) return baseview.api.$getSize.call(this, 0, 0);
	    debug_size_box_start(this, true);

	    var size = this._cells[this._active_cell].$getSize(0, 0);

	    if (this._settings.fitBiggest) {
	      for (var i = 0; i < this._cells.length; i++) {
	        if (i != this._active_cell) {
	          var other = this._cells[i].$getSize(0, 0);

	          for (var j = 0; j < 4; j++) {
	            size[j] = Math.max(size[j], other[j]);
	          }
	        }
	      }
	    } //get layout sizes


	    var self_size = baseview.api.$getSize.call(this, 0, 0); //use child settings if layout's one was not defined

	    if (self_size[1] >= 100000) self_size[1] = 0;
	    if (self_size[3] >= 100000) self_size[3] = 0;
	    self_size[0] = (self_size[0] || size[0]) + dx;
	    self_size[1] = (self_size[1] || size[1]) + dx;
	    self_size[2] = (self_size[2] || size[2]) + dy;
	    self_size[3] = (self_size[3] || size[3]) + dy;
	    debug_size_box_end(this, self_size);
	    return self_size;
	  },
	  $setSize: function (x, y) {
	    if (!this._cells.length) return;
	    this._layout_sizes = [x, y];
	    baseview.api.$setSize.call(this, x, y);

	    this._cells[this._active_cell].$setSize(x, y);
	  },
	  isVisible: function (base_id, cell_id) {
	    if (cell_id && cell_id != this.getActiveId()) {
	      if (base_id) this._ask_render(cell_id, base_id);
	      return false;
	    }

	    return base.api.isVisible.call(this, base_id, this._settings.id);
	  },
	  getActiveId: function () {
	    return this._cells.length ? this._cells[this._active_cell]._settings.id : null;
	  },
	  back: function (step) {
	    step = step || 1;

	    if (this.callEvent("onBeforeBack", [this.getActiveId(), step])) {
	      if (this._back_queue.length > step) {
	        var viewId = this._back_queue[this._back_queue.length - step - 1];
	        $$(viewId).show();
	        return viewId;
	      }

	      return null;
	    }

	    return null;
	  }
	};
	var view$g = exports.protoUI(api$g, baselayout.view);

	var api$h = {
	  name: "tabview",
	  setValue: function (val) {
	    this._cells[0].setValue(val);
	  },
	  getValue: function () {
	    return this._cells[0].getValue();
	  },
	  getTabbar: function () {
	    return this._cells[0];
	  },
	  getMultiview: function () {
	    return this._cells[1];
	  },
	  addView: function (obj) {
	    var nid = this.getMultiview().addView(obj.body);
	    obj.id = nid;
	    obj.value = obj.header;
	    delete obj.body;
	    delete obj.header;
	    var t = this.getTabbar();
	    t.addOption(obj);
	    return nid;
	  },
	  removeView: function (id) {
	    var t = this.getTabbar();
	    t.removeOption(id);
	    t.refresh();
	  },
	  $init: function (config) {
	    this.$ready.push(this._init_tabview_handlers);
	    var cells = config.cells;
	    var tabs = [];
	    assert(cells && cells.length, "tabview must have cells collection");

	    for (var i = cells.length - 1; i >= 0; i--) {
	      var view = cells[i].body || cells[i];
	      if (!view.id) view.id = "view" + uid();
	      tabs[i] = {
	        value: cells[i].header,
	        id: view.id,
	        close: cells[i].close,
	        width: cells[i].width,
	        hidden: !!cells[i].hidden
	      };
	      cells[i] = view;
	    }

	    var tabbar = {
	      view: "tabbar",
	      multiview: true
	    };
	    var mview = {
	      view: "multiview",
	      cells: cells,
	      animate: !!config.animate
	    };
	    if (config.value) tabbar.value = config.value;
	    if (config.tabbar) exports.extend(tabbar, config.tabbar, true);
	    if (config.multiview) exports.extend(mview, config.multiview, true);
	    tabbar.options = tabbar.options || tabs;
	    config.rows = [tabbar, mview];
	    delete config.cells;
	    delete config.tabs;
	  },
	  _init_tabview_handlers: function () {
	    this.getTabbar().attachEvent("onOptionRemove", function (id) {
	      var view = $$(id);

	      if (view) {
	        var parent = view.getParentView();
	        if (parent) parent.removeView(view);
	      }
	    });
	  }
	};
	var view$h = exports.protoUI(api$h, layout.view);

	var api$i = {
	  name: "carousel",
	  defaults: {
	    scrollSpeed: "300ms",
	    type: "clean",
	    navigation: {},
	    animate: true
	  },
	  $init: function () {
	    this._viewobj.className += " webix_carousel";
	    this._layout = null;
	    this._dataobj = null;
	    this._active_cell = 0;
	    this.$ready.unshift(this._initLayout);
	    this.$ready.push(this._after_init_call);
	  },
	  addView: function (view, index) {
	    var t = this._layout.addView(view, index);

	    this._fix_after_view_add();

	    return t;
	  },
	  removeView: function (id) {
	    this._layout.removeView(id);

	    this._fix_after_view_add();
	  },
	  _replace: function (new_view, target_id) {
	    this._layout._replace(new_view, target_id);

	    this._fix_after_view_add();
	  },
	  _fix_after_view_add: function () {
	    this._cells = this._layout._cells;

	    this._renderPanel();

	    this.setActiveIndex(Math.min(this._active_cell, this._cells.length - 1));
	  },
	  _initLayout: function () {
	    if (this._layout && this._layout.destructor) this._layout.destructor();
	    var layout = "";

	    if (this.config.cols) {
	      layout = "cols";
	      this._vertical_orientation = 0;
	    } else {
	      layout = "rows";
	      this._vertical_orientation = 1;
	    }

	    var config = {
	      borderless: true,
	      type: "clean"
	    };
	    config[layout] = copy(this._settings[layout]);
	    var layoutProp = ["type", "margin", "marginX", "marginY", "padding", "paddingX", "paddingY"];
	    var layoutConfig = {};

	    for (var i = 0; i < layoutProp.length; i++) {
	      if (this._settings[layoutProp[i]]) {
	        layoutConfig[layoutProp[i]] = this._settings[layoutProp[i]];
	      }
	    }

	    exports.extend(config, layoutConfig, true);
	    state._parent_cell = this;
	    this._layout = ui._view(config);

	    this._viewobj.appendChild(this._layout._viewobj);

	    this._cells = this._layout._cells;
	    this._layout._show = bind(api$i._show, this);
	    this._layout.adjustScroll = bind(api$i.adjustScroll, this);
	    attachEvent("onReconstruct", bind(function (view) {
	      if (view == this._layout) this._setScroll();
	    }, this));
	    this._contentobj = this._viewobj.firstChild;
	  },
	  _onKeyPress: function (code, e) {
	    if (this._settings.navigation.items && e.target.getAttribute("role") === "tab") this._moveActive(code, e);

	    baseview.api._onKeyPress.call(this, code, e);
	  },
	  getChildViews: function () {
	    return [this._layout];
	  },
	  getLayout: function () {
	    return this._layout;
	  },
	  _after_init_call: function () {
	    this._contentobj.setAttribute("touch_scroll", this._vertical_orientation ? "y" : "x");

	    this._layout.attachEvent("onAfterScroll", bind(function () {
	      this.callEvent("onShow", [this.getActiveId()]);
	    }, this));

	    each(this._layout, function (view) {
	      view._viewobj.setAttribute("role", "tabpanel");
	    });
	  },
	  adjustScroll: function (matrix) {
	    var size = this._vertical_orientation ? this._content_height : this._content_width;
	    var correction;

	    if (this._vertical_orientation) {
	      correction = Math.round(matrix.f / size);
	      matrix.f = correction * size;
	    } else {
	      correction = Math.round(matrix.e / size);
	      matrix.e = correction * size;
	    }

	    this._active_cell = -correction;
	    if (this._settings.navigation) this._renderNavItems();
	    return true;
	  },
	  _show: function (obj) {
	    var i, layout, _nextCell, _size, x, y;

	    _nextCell = -1;
	    layout = this._layout;

	    for (i = 0; i < layout._cells.length; i++) {
	      if (layout._cells[i] == obj) {
	        _nextCell = i;
	        break;
	      }
	    }

	    if (_nextCell < 0 || _nextCell == this._active_cell) return;
	    this._active_cell = _nextCell;
	    _size = layout._vertical_orientation ? this._content_height : this._content_width;
	    x = -(layout._vertical_orientation ? 0 : _nextCell * _size);
	    y = -(layout._vertical_orientation ? _nextCell * _size : 0);
	    this.scrollTo(x, y);
	    this.callEvent("onShow", [layout._cells[this._active_cell]._settings.id]);
	    if (this._settings.navigation) this._renderPanel();
	  },
	  scrollTo: function (x, y) {
	    if (Touch && animate.isSupported() && this._settings.animate) Touch._set_matrix(this._contentobj, x, y, this._settings.scrollSpeed || "100ms");else {
	      this._contentobj.style.marginLeft = x + "px";
	      this._contentobj.style.marginTop = y + "px";
	    }
	  },
	  navigation_setter: function (config) {
	    this._mergeSettings(config, {
	      type: "corner",
	      buttons: true,
	      items: true
	    });

	    return config;
	  },
	  showNext: function () {
	    if (this._active_cell < this._layout._cells.length - 1) this.setActiveIndex(this._active_cell + 1);
	  },
	  showPrev: function () {
	    if (this._active_cell > 0) this.setActiveIndex(this._active_cell - 1);
	  },
	  setActiveIndex: function (value) {
	    assert(value < this._layout._cells.length, "Not existing index in collection");
	    var id = this._layout._cells[value]._settings.id;
	    $$(id).show();
	  },
	  getActiveIndex: function () {
	    return this._active_cell;
	  },
	  $getSize: function (dx, dy) {
	    var layoutSizes = this._layout.$getSize(0, 0);

	    var selfSizes = base.api.$getSize.call(this, dx, dy);

	    if (this._layout._vertical_orientation) {
	      selfSizes[0] = Math.max(selfSizes[0], layoutSizes[0]);
	      selfSizes[1] = Math.min(selfSizes[1], layoutSizes[1]);
	    } else {
	      selfSizes[2] = Math.max(selfSizes[2], layoutSizes[2]);
	      selfSizes[3] = Math.min(selfSizes[3], layoutSizes[3]);
	    }

	    return selfSizes;
	  },
	  $setSize: function (x, y) {
	    var layout = this._layout;
	    var c = layout._cells.length;
	    var changed = base.api.$setSize.call(this, x, y);
	    var yc = this._content_height * (layout._vertical_orientation ? c : 1);
	    var xc = this._content_width * (layout._vertical_orientation ? 1 : c);

	    if (changed) {
	      this._contentobj.style.height = yc + "px";
	      this._contentobj.style.width = xc + "px";
	      layout.$setSize(xc, yc);

	      this._setScroll();
	    } else layout.$setSize(xc, yc);
	  },
	  _setScroll: function () {
	    var layout = this._layout;
	    var activeCell = this._active_cell || 0;
	    var size = layout._vertical_orientation ? this._content_height : this._content_width;
	    var x = -(layout._vertical_orientation ? 0 : activeCell * size);
	    var y = -(layout._vertical_orientation ? activeCell * size : 0);
	    this.scrollTo(x, y);
	    if (this._settings.navigation) this._renderPanel();
	  },
	  getActiveId: function () {
	    var cell = this._layout._cells[this._active_cell];
	    return cell ? cell._settings.id : null;
	  },
	  setActive: function (value) {
	    $$(value).show();
	  }
	};
	var view$i = exports.protoUI(api$i, EventSystem, NavigationButtons, base.view);

	var api$j = {
	  name: "proxy",
	  body_setter: function (value) {
	    state._parent_cell = this;
	    this._body_cell = ui._view(value);

	    this._viewobj.appendChild(this._body_cell._viewobj);

	    return value;
	  },
	  getChildViews: function () {
	    return [this._body_cell];
	  },
	  $setSize: function (x, y) {
	    base.api.$setSize.call(this, x, y);

	    this._body_cell.$setSize(this.$width, this.$height);
	  },
	  $getSize: function (dx, dy) {
	    var selfSize = base.api.$getSize.call(this, dx, dy);

	    var size = this._body_cell.$getSize(dx, dy);

	    size[0] = Math.max(selfSize[0], size[0]);
	    size[1] = Math.min(selfSize[1], size[1]);
	    size[2] = Math.max(selfSize[2], size[2]);
	    size[3] = Math.min(selfSize[3], size[3]);
	    size[4] = Math.max(selfSize[4], size[4]);
	    return size;
	  },
	  _replace: function (n) {
	    this._body_cell.destructor();

	    this._body_cell = n;

	    this._viewobj.appendChild(n._viewobj);

	    this.resize();
	  }
	};
	var view$j = exports.protoUI(api$j, base.view);

	exports.protoUI({
	  name: "portlet",
	  defaults: {
	    template: "GPL version doesn't support portlet <br> You need Webix PRO"
	  }
	}, template$1.view);

	exports.protoUI({
	  name: "dashboard",
	  defaults: {
	    template: "GPL version doesn't support dashboard <br> You need Webix PRO"
	  }
	}, template$1.view);

	exports.protoUI({
	  name: "panel",
	  defaults: {
	    template: "GPL version doesn't support panel <br> You need Webix PRO"
	  }
	}, template$1.view);

	exports.protoUI({
	  name: "abslayout",
	  defaults: {
	    template: "GPL version doesn't support abslayout <br> You need Webix PRO"
	  }
	}, template$1.view);

	exports.protoUI({
	  name: "gridlayout",
	  defaults: {
	    template: "GPL version doesn't support gridlayout <br> You need Webix PRO"
	  }
	}, template$1.view);

	var api$k = {
	  $init: function () {
	    exports.extend(this, FlexLayout, true);
	  },
	  name: "flexlayout"
	};
	var view$k = exports.protoUI(api$k, layout.view);

	exports.protoUI({
	  name: "flexdatalayout",
	  defaults: {
	    template: "GPL version doesn't support flexdatalayout <br> You need Webix PRO"
	  }
	}, template$1.view);

	var api$l = {
	  name: "popup",
	  $init: function () {
	    var _this = this;

	    this._settings.head = false;
	    this.$view.className += " webix_popup";
	    attachEvent("onClick", function (e) {
	      return _this._hide(e);
	    });
	    this.attachEvent("onHide", this._hide_point);
	  },
	  $skin: function () {
	    this.defaults.headHeight = $active.barHeight;
	    this.defaults.padding = $active.popupPadding;
	    this.defaults.point = !$active.popupNoPoint;
	    this.defaults.borderless = $active.borderlessPopup;
	  },
	  close: function () {
	    remove(this._point_element);
	    window$1.api.close.call(this);
	  },
	  $getSize: function (x, y) {
	    return window$1.api.$getSize.call(this, x + this._settings.padding * 2, y + this._settings.padding * 2);
	  },
	  $setSize: function (x, y) {
	    base.api.$setSize.call(this, x, y);
	    x = this._content_width - this._settings.padding * 2;
	    y = this._content_height - this._settings.padding * 2;
	    this._contentobj.style.padding = this._settings.padding + "px";
	    this._headobj.style.display = "none";

	    this._body_cell.$setSize(x, y);
	  },
	  //redefine to preserve inner borders
	  //_inner_body_set:function(){}, //same as win?
	  _inner_body_set: function (value) {
	    if (typeof value.borderless == "undefined") value.borderless = false;
	  },
	  head_setter: function () {},
	  _set_point: function (mode, left, top, fixed) {
	    this._hide_point();

	    document.body.appendChild(this._point_element = create("DIV", {
	      "class": "webix_point_" + mode
	    }, ""));
	    this._point_element.style.zIndex = zIndex();
	    this._point_element.style.position = fixed ? "fixed" : "absolute";
	    this._point_element.style.top = top + "px";
	    this._point_element.style.left = left + "px";
	  },
	  _hide_point: function () {
	    this._point_element = remove(this._point_element);
	  }
	};
	var view$l = exports.protoUI(api$l, window$1.view);
	var popup = {
	  api: api$l,
	  view: view$l
	};

	var api$m = {
	  name: "toolbar",
	  defaults: {
	    type: "toolbar"
	  },
	  _render_borders: true,
	  _form_classname: "webix_toolbar",
	  _form_vertical: false,
	  $init: function (config) {
	    if (!config.borderless) {
	      this._contentobj.style.borderWidth = "1px";
	      this._settings._inner = {
	        top: false,
	        left: false,
	        right: false,
	        bottom: false
	      };
	    }

	    this._contentobj.className += " " + this._form_classname;

	    this._viewobj.setAttribute("role", "toolbar");
	  },
	  _recollect_elements: function () {
	    var form = this;
	    form.elements = {};
	    each(this, function (view) {
	      if (view._settings.name && view.getValue && view.setValue) {
	        form.elements[view._settings.name] = view;
	        if (view.mapEvent) view.mapEvent({
	          onbeforetabclick: form,
	          onaftertabclick: form,
	          onitemclick: form,
	          onchange: form
	        });
	      }

	      if (view.setValues) return false;
	    });
	    var old = this._values;
	    this.setDirty(false);

	    if (old) {
	      //restore dirty state after form reconstructing
	      var now = this._values;

	      for (var key in form.elements) {
	        if (old[key] && now[key] != old[key]) {
	          now[key] = old[key];
	          this.setDirty(true);
	        }
	      }
	    }
	  },
	  _parse_cells_ext_end: function () {
	    this._recollect_elements();
	  },
	  _parse_cells_ext: function (collection) {
	    var config = this._settings;

	    if (config.elements && !collection) {
	      this._collection = collection = config.elements;
	      this._vertical_orientation = this._form_vertical;
	      delete config.elements;
	    }

	    if (this._settings.elementsConfig) this._rec_apply_settings(this._collection, config.elementsConfig);
	    return collection;
	  },
	  _rec_apply_settings: function (col, settings) {
	    for (var i = 0; i < col.length; i++) {
	      var element = col[i];
	      exports.extend(element, settings);
	      var nextsettings = settings;
	      if (element.elementsConfig) nextsettings = exports.extend(exports.extend({}, element.elementsConfig), settings);
	      var sub;
	      if (element.body) sub = [element.body];else sub = element.rows || element.cols || element.cells || element.body;
	      if (sub) this._rec_apply_settings(sub, nextsettings);
	    }
	  },
	  $getSize: function (dx, dy) {
	    var sizes = layout.api.$getSize.call(this, dx, dy);
	    var parent = this.getParentView();
	    var index = this._vertical_orientation ? 3 : 1;
	    if (parent && this._vertical_orientation != parent._vertical_orientation) sizes[index] += 100000;
	    debug_size_box(this, sizes, true);
	    return sizes;
	  },
	  render: function () {},
	  refresh: function () {
	    this.render();
	  }
	};
	var view$m = exports.protoUI(api$m, Scrollable, AtomDataLoader, Values, layout.view, ValidateData);
	var toolbar = {
	  api: api$m,
	  view: view$m
	};

	var api$n = {
	  name: "form",
	  defaults: {
	    type: "form",
	    autoheight: true
	  },
	  _default_height: -1,
	  _form_classname: "webix_form",
	  _form_vertical: true,
	  $init: function () {
	    this._viewobj.setAttribute("role", "form");
	  },
	  $getSize: function (dx, dy) {
	    if (this._scroll_y && !this._settings.width) dx += env.scrollSize;
	    var sizes = layout.api.$getSize.call(this, dx, dy);

	    if (this._settings.scroll || !this._settings.autoheight) {
	      sizes[2] = this._settings.height || this._settings.minHeight || 0;
	      sizes[3] = this._settings.height || 100000;
	    }

	    return sizes;
	  }
	};
	var view$n = exports.protoUI(api$n, toolbar.view);

	var api$o = {
	  name: "fieldset",
	  defaults: {
	    borderless: true,
	    $cssName: "webix_fieldset",
	    paddingX: 18,
	    paddingY: 30
	  },
	  $init: function (obj) {
	    obj.body = obj.body || {};
	    var css = this.defaults.$cssName;
	    this._viewobj.className += " " + css;
	    this._viewobj.innerHTML = "<fieldset><legend class='" + css + "_label" + (obj.required ? " webix_required" : "") + "'></legend><div class='" + css + "_body'></div></fieldset>";
	  },
	  label_setter: function (value) {
	    this._viewobj.firstChild.childNodes[0].innerHTML = value;
	    return value;
	  },
	  getChildViews: function () {
	    return [this._body_view];
	  },
	  body_setter: function (config) {
	    state._parent_cell = this;
	    this._body_view = ui(config, this._viewobj.firstChild.childNodes[1]);
	    return config;
	  },
	  getBody: function () {
	    return this._body_view;
	  },
	  resizeChildren: function () {
	    if (!this._body_view) return;
	    var x = this.$width - this._settings.paddingX;
	    var y = this.$height - this._settings.paddingY;

	    var sizes = this._body_view.$getSize(0, 0); //minWidth


	    if (sizes[0] > x) x = sizes[0]; //minHeight

	    if (sizes[2] > y) y = sizes[2];

	    this._body_view.$setSize(x, y);

	    this.resize();
	  },
	  $getSize: function (x, y) {
	    debug_size_box_start(this, true);
	    x += this._settings.paddingX;
	    y += this._settings.paddingY;

	    var t = this._body_view.$getSize(x, y);

	    var s = this._last_body_size = base.api.$getSize.call(this, x, y); //inner content minWidth > outer

	    if (s[0] < t[0]) s[0] = t[0];
	    if (s[2] < t[2]) s[2] = t[2]; //inner content maxWidth < outer

	    if (s[1] > t[1]) s[1] = t[1];
	    if (s[3] > t[3]) s[3] = t[3]; //make max size not less than min size

	    if (s[1] < s[0]) s[1] = s[0];
	    if (s[3] < s[2]) s[3] = s[2];
	    debug_size_box_end(this, s);
	    return s;
	  },
	  $setSize: function (x, y) {
	    if (base.api.$setSize.call(this, x, y)) {
	      x = Math.min(this._last_body_size[1], x);
	      y = Math.min(this._last_body_size[3], y);

	      this._body_view.$setSize(x - this._settings.paddingX, y - this._settings.paddingY);
	    }
	  }
	};
	var view$o = exports.protoUI(api$o, base.view);
	var fieldset = {
	  api: api$o,
	  view: view$o
	};

	var api$p = {
	  name: "forminput",
	  defaults: {
	    $cssName: "webix_forminput",
	    labelWidth: 80,
	    labelAlign: "left",
	    // remove fieldset sizing
	    paddingY: 0,
	    paddingX: 0
	  },
	  setValue: function (value) {
	    if (this._body_view.setValue) this._body_view.setValue(value);else if (this._body_view.setValues) this._body_view.setValues(value);
	  },
	  focus: function () {
	    if (this._body_view.focus) {
	      return this._body_view.focus();
	    }

	    return false;
	  },
	  getValue: function () {
	    if (this._body_view.getValue) return this._body_view.getValue();else if (this._body_view.getValues) return this._body_view.getValues();
	  },
	  getBody: function () {
	    return this._body_view;
	  },
	  $skin: function () {
	    this._inputPadding = $active.inputPadding;
	    this._inputSpacing = $active.inputSpacing;
	    this._labelTopHeight = $active.labelTopHeight || 15;
	  },
	  $init: function (obj) {
	    this.$ready.push(function () {
	      var label = this._viewobj.firstChild.childNodes[0];

	      if (!this._settings.label || !this._settings.labelWidth) {
	        label.style.display = "none";
	        this._settings.paddingX = this._settings.paddingY = 0;
	        return;
	      }

	      if (this._settings.labelPosition == "top") {
	        label.style.lineHeight = this._labelTopHeight - this._inputPadding + "px";
	        label.className += " " + this.defaults.$cssName + "_label_top";
	      } else label.style.width = this._settings.paddingX + "px";

	      label.style.textAlign = this._settings.labelAlign;
	      if (this._settings.value) this.setValue(this._settings.value);
	    });

	    if (obj.labelPosition != "top") {
	      var lw = isUndefined(obj.labelWidth) ? this.defaults.labelWidth : obj.labelWidth;
	      obj.paddingX = lw - this._inputPadding * 2 + this._inputSpacing * 2;
	    } else obj.paddingY = this._labelTopHeight;
	  },
	  setBottomText: function (text) {
	    var config = this._settings;

	    if (typeof text != "undefined") {
	      if (config.bottomLabel == text) return;
	      config.bottomLabel = text;
	    }

	    var message = (config.invalid ? config.invalidMessage : "") || config.bottomLabel;

	    if (this._invalidMessage) {
	      remove(this._invalidMessage);
	    }

	    if (message) {
	      this.$view.style.position = "relative";
	      this._invalidMessage = create("div", {
	        "class": "webix_inp_bottom_label",
	        role: config.invalid ? "alert" : "",
	        "aria-relevant": "all",
	        style: "position:absolute; bottom:0px; padding:2px; background: white; left:" + this._settings.labelWidth + "px; "
	      }, message);

	      this._viewobj.appendChild(this._invalidMessage);
	    }
	  }
	};
	var view$p = exports.protoUI(api$p, fieldset.view);

	function _tagname(el) {
	  if (!el.tagName) return null;
	  return el.tagName.toLowerCase();
	}

	function _attribute(el, name) {
	  if (!el.getAttribute) return null;
	  var attr = el.getAttribute(name);
	  return attr ? attr.toLowerCase() : null;
	}

	function _get_html_value() {
	  var tagname = _tagname(this);

	  if (_get_value[tagname]) return _get_value[tagname](this);
	  return _get_value.other(this);
	}

	var _get_value = {
	  radio: function (el) {
	    for (var i = 0; i < el.length; i++) {
	      if (el[i].checked) return el[i].value;
	    }

	    return "";
	  },
	  input: function (el) {
	    var type = _attribute(el, "type");

	    if (type === "checkbox") return el.checked;
	    return el.value;
	  },
	  textarea: function (el) {
	    return el.value;
	  },
	  select: function (el) {
	    var index$$1 = el.selectedIndex;
	    return el.options[index$$1].value;
	  },
	  other: function (el) {
	    return el.innerHTML;
	  }
	};

	function _set_html_value(value) {
	  var tagname = _tagname(this);

	  if (_set_value[tagname]) return _set_value[tagname](this, value);
	  return _set_value.other(this, value);
	}

	var _set_value = {
	  radio: function (el, value) {
	    for (var i = 0; i < el.length; i++) {
	      el[i].checked = el[i].value == value;
	    }
	  },
	  input: function (el, value) {
	    var type = _attribute(el, "type");

	    if (type === "checkbox") el.checked = value ? true : false;else el.value = value;
	  },
	  textarea: function (el, value) {
	    el.value = value;
	  },
	  select: function (el, value) {
	    el.value = value; //incorrect option applied, select first option

	    if (el.selectedIndex === -1) el.value = el.firstElementChild.value;
	  },
	  other: function (el, value) {
	    el.innerHTML = value;
	  }
	};
	var api$q = {
	  name: "htmlform",
	  $init: function (config) {
	    this.elements = {};
	    this._default_values = false;
	    if (config.content && (config.container == config.content || !config.container && config.content == document.body)) this._copy_inner_content = true;
	  },
	  content_setter: function (content) {
	    content = toNode(content);

	    if (this._copy_inner_content) {
	      while (content.childNodes.length > 1) {
	        this._viewobj.childNodes[0].appendChild(content.childNodes[0]);
	      }
	    } else {
	      this._viewobj.childNodes[0].appendChild(content);
	    }

	    this._parse_inputs();

	    return true;
	  },
	  render: function () {
	    template$1.api.render.apply(this, arguments);

	    this._parse_inputs();
	  },
	  _parse_inputs: function () {
	    var inputs = this._viewobj.querySelectorAll("[name]");

	    this.elements = {};

	    for (var i = 0; i < inputs.length; i++) {
	      var el = inputs[i];

	      var name = _attribute(el, "name");

	      if (name) {
	        var tag = _tagname(el) === "button";

	        var type = _attribute(el, "type");

	        var cant_clear = tag || type === "button" || type === "submit";

	        if (type === "radio") {
	          var stack = this.elements[name] || [];
	          stack.tagName = "radio";
	          stack.push(el);
	          el = stack;
	        }

	        this.elements[name] = el;
	        el.getValue = _get_html_value;
	        el.setValue = _set_html_value;
	        el.$allowsClear = !cant_clear;
	      }
	    }

	    return this.elements;
	  },
	  _mark_invalid: function (id, obj) {
	    this._clear_invalid(id, obj);

	    var el = this._viewobj.querySelector("[name=\"" + id + "\"]");

	    if (el) addCss(el, "invalid");
	  },
	  _clear_invalid: function (id) {
	    var el = this._viewobj.querySelector("[name=\"" + id + "\"]");

	    if (el) removeCss(el, "invalid");
	  },
	  focus: function (name) {
	    if (!name && this.$view.contains(document.activeElement)) {
	      // focus already inside of form, leaving
	      return false;
	    }

	    Values.focus.apply(this, arguments);
	  }
	};
	var view$q = exports.protoUI(api$q, template$1.view, Values);

    var api$ab = {
    	name:"fullscreenlayout",
    	$init:function(){
    		this.$view.className += " fullscreenlayout";
    		delete this.rows_setter;
    		delete this.cols_setter;
    		this._collection = [];
    	},
    	cells_setter:function(cells){
    		this._collection = cells;
    	},
    	_parse_cells:function(){
    		baselayout.api._parse_cells.call(this, this._collection);
    	},
    	$getSize:function(){
    		var self_size = baseview.api.$getSize.call(this, 0, 0);
    		var sub = null;
    
    		for (var i=0; i<this._cells.length; i++)
    			if (this._cells[i]._settings.relative)
    				sub = this._cells[i].$getSize(0,0);
    
    		if (sub){
    			if (self_size[1] >= 100000) self_size[1]=0;
    			if (self_size[3] >= 100000) self_size[3]=0;
    			self_size[0] = Math.max(self_size[0], sub[0]);
    			self_size[1] = Math.max(self_size[1], sub[1]);
    			self_size[2] = Math.max(self_size[2], sub[2]);
    			self_size[3] = Math.max(self_size[3], sub[3]);
    		}
    
    		return self_size;
    	},
    	$setSize:function(x,y){
    		this._layout_sizes = [x,y];
    		debug_size_box_start(this);
    
    		baseview.api.$setSize.call(this,x,y);
    		this._set_child_size(x,y);
    
    		debug_size_box_end(this, [x,y]);
    	},
    	_set_child_size:function(x,y){
    		for (var i=0; i<this._cells.length; i++){
    			var view = this._cells[i];
    			var conf = view._settings;
    
    			var sizes = view.$getSize(0,0);
    			if (conf.relative){
    				conf.left = conf.top = 0;
    				view.$setSize(x,y);
    			} else {
                    if(conf.id == 'script_expand_button' || conf.id == 'script_save_button'){
                        sizes[0] = 25;
                    }
                    if((conf.id == 'script_bet_start_stop_button' || conf.id == 'script_bet_stop_on_next_win_button' || conf.id =='script_bet_stop_on_next_loss_button')&& !env.mobile ){
                        sizes[0] = 180;
                    }
    				view.$setSize(sizes[0], sizes[2]);
    			}
    
    			var node = view.$view;
    			var options = ["left", "right", "top", "bottom"];
    
    			for (var j = 0; j < options.length; j++) {
    				var key = options[j];
    				if (key in conf)
    					node.style[key] = conf[key] + "px";
    			}
    		}
    	}
    };
	exports.protoUI(api$ab, layout.view);

	var api$r = {
	  name: "property",
	  $init: function () {
	    this._contentobj.className += " webix_property";

	    this._contentobj.setAttribute("role", "listbox");

	    this._destroy_with_me = [];
	    this.attachEvent("onAfterEditStart", function (id) {
	      var node = this.getItemNode(id);
	      addCss(node, "webix_focused");
	    });
	    this.attachEvent("onAfterEditStop", function (id, editor) {
	      var node = this.getItemNode(editor.config.id);
	      removeCss(node, "webix_focused");
	    });
	  },
	  defaults: {
	    nameWidth: 100,
	    editable: true
	  },
	  on_render: {
	    checkbox: function (value) {
	      return "<input type='checkbox' class='webix_property_check' " + (value ? "checked" : "") + ">";
	    },
	    color: function (value) {
	      return "<div class=\"webix_property_col_val\"><div class='webix_property_col_ind' style=\"background-color:" + (value || "#FFFFFF") + ";\"></div><span>" + value + "</span></div>";
	    }
	  },
	  on_edit: {
	    label: false
	  },
	  _id:
	  /*@attr*/
	  "webix_f_id",
	  on_click: {
	    webix_property_check: function (ev) {
	      var id = this.locate(ev);
	      this.getItem(id).value = !this.getItem(id).value;
	      this.callEvent("onCheck", [id, this.getItem(id).value]);
	      return false;
	    }
	  },
	  on_dblclick: {},
	  registerType: function (name, data) {
	    if (data.template) this.on_render[name] = data.template;
	    if (data.editor) this.on_edit[name] = data.editor;
	    if (data.click) for (var key in data.click) {
	      this.on_click[key] = data.click[key];
	    }
	  },
	  elements_setter: function (data) {
	    this._idToLine = {};

	    for (var i = 0; i < data.length; i++) {
	      var line = data[i];
	      if (line.type == "multiselect") line.optionslist = true; //line.type 	= 	line.type||"label";

	      line.id = line.id || uid();
	      line.label = line.label || "";
	      line.value = line.value || "";
	      this._idToLine[line.id] = i;

	      this._map_options(data[i]);
	    }

	    return data;
	  },
	  showItem: function (id) {
	    RenderStack.showItem.call(this, id);
	  },
	  locate: function () {
	    return locate(arguments[0], this._id);
	  },
	  getItemNode: function (id) {
	    return this._dataobj.childNodes[this._idToLine[id]];
	  },
	  getItem: function (id) {
	    return this._settings.elements[this._idToLine[id]];
	  },
	  _get_editor_type: function (id) {
	    var type = this.getItem(id).type;
	    if (type == "checkbox") return "inline-checkbox";
	    var alter_type = this.on_edit[type];
	    return alter_type === false ? false : alter_type || type;
	  },
	  _get_edit_config: function (id) {
	    return this.getItem(id);
	  },
	  _find_cell_next: function (start, check, direction) {
	    var row = this._idToLine[start.id];
	    var order = this._settings.elements;

	    if (direction) {
	      for (var i = row + 1; i < order.length; i++) {
	        if (check.call(this, order[i].id)) return order[i].id;
	      }
	    } else {
	      for (var _i = row - 1; _i >= 0; _i--) {
	        if (check.call(this, order[_i].id)) return order[_i].id;
	      }
	    }

	    return null;
	  },
	  updateItem: function (key, data) {
	    data = data || {};
	    var line = this.getItem(key);
	    if (line) exports.extend(line, data, true);
	    this.refresh();
	  },
	  _cellPosition: function (id) {
	    var html = this.getItemNode(id);
	    return {
	      left: html.offsetLeft + this._settings.nameWidth,
	      top: html.offsetTop,
	      height: html.firstChild.offsetHeight,
	      width: this._data_width,
	      parent: this._contentobj
	    };
	  },
	  _clear: function () {
	    var lines = this._settings.elements;

	    for (var i = 0; i < lines.length; i++) {
	      lines[i].value = "";
	    }
	  },
	  clear: function () {
	    this._clear();

	    this._props_dataset = {};
	    this.refresh();
	  },
	  setValues: function (data, update) {
	    var _this = this;

	    if (this._settings.complexData) data = CodeParser.collapseNames(data, "", {}, function (v) {
	      return isUndefined(_this._idToLine[v]);
	    });
	    if (!update) this._clear();

	    for (var key in data) {
	      var line = this.getItem(key);
	      if (line) line.value = data[key];
	    }

	    this._props_dataset = data;
	    this.refresh();
	  },
	  getValues: function () {
	    var data = clone(this._props_dataset || {});

	    for (var i = 0; i < this._settings.elements.length; i++) {
	      var line = this._settings.elements[i];
	      if (line.type != "label") data[line.id] = line.value;
	    }

	    if (this._settings.complexData) data = CodeParser.expandNames(data);
	    return data;
	  },
	  refresh: function () {
	    this.render();
	  },
	  $setSize: function (x, y) {
	    if (base.api.$setSize.call(this, x, y)) {
	      this._data_width = this._content_width - this._settings.nameWidth;
	      this.render();
	    }
	  },
	  $getSize: function (dx, dy) {
	    if (this._settings.autoheight) {
	      var count = this._settings.elements.length;
	      this._settings.height = Math.max(this.type.height * count, this._settings.minHeight || 0);
	    }

	    return base.api.$getSize.call(this, dx, dy);
	  },
	  _toHTML: function () {
	    var html = [];
	    var els = this._settings.elements;
	    if (els) for (var i = 0; i < els.length; i++) {
	      var data = els[i];
	      if (data.css && _typeof(data.css) == "object") data.css = createCss(data.css);
	      var pre = "<div " +
	      /*@attr*/
	      "webix_f_id" + "=\"" + data.id + "\"" + (data.type !== "label" ? "role=\"option\" tabindex=\"0\"" : "") + " class=\"webix_property_line " + (data.css || "") + "\">";
	      if (data.type == "label") html[i] = pre + "<div class='webix_property_label_line'>" + data.label + "</div></div>";else {
	        var render = this.on_render[data.type],
	            content;
	        var post = "<div class='webix_property_label' style='width:" + this._settings.nameWidth + "px'>" + data.label + "</div><div class='webix_property_value' style='width:" + this._data_width + "px'>";

	        if (data.collection || data.options) {
	          content = data.template(data);
	        } else if (data.format) content = data.format(data.value);else content = data.value;

	        if (render) content = render.call(this, data.value, data);
	        html[i] = pre + post + content + "</div></div>";
	      }
	    }
	    return html.join("");
	  },
	  type: {
	    height: 24,
	    templateStart: template(""),
	    templateEnd: template("</div>")
	  },
	  $skin: function () {
	    this.type.height = $active.propertyItemHeight;
	  }
	};
	var view$r = exports.protoUI(api$r, AutoTooltip, EditAbility, MapCollection, MouseEvents, Scrollable, SingleRender, AtomDataLoader, EventSystem, base.view);

	var api$s = {
	  name: "calendar",
	  defaults: {
	    date: new Date(),
	    //selected date, not selected by default
	    select: false,
	    navigation: true,
	    monthSelect: true,
	    weekHeader: true,
	    weekNumber: false,
	    skipEmptyWeeks: false,
	    calendarHeader: "%F %Y",
	    calendarWeekHeader: "W#",
	    //calendarTime: "%H:%i",
	    events: wDate.isHoliday,
	    minuteStep: 5,
	    timeIcon: "wxi-clock",
	    icons: false,
	    timepickerHeight: 30,
	    headerHeight: 70,
	    dayTemplate: function (d) {
	      return d.getDate();
	    },
	    width: 260,
	    height: 250
	  },
	  dayTemplate_setter: template,
	  calendarHeader_setter: wDate.dateToStr,
	  calendarWeekHeader_setter: wDate.dateToStr,
	  calendarTime_setter: function (format) {
	    this._calendarTime = format;
	    return wDate.dateToStr(format);
	  },
	  date_setter: function (date) {
	    return this._string_to_date(date);
	  },
	  maxDate_setter: function (date) {
	    return this._string_to_date(date);
	  },
	  minDate_setter: function (date) {
	    return this._string_to_date(date);
	  },
	  minTime_setter: function (time) {
	    if (typeof time == "string") {
	      time = i18n.parseTimeFormatDate(time);
	      time = [time.getHours(), time.getMinutes()];
	    }

	    return time;
	  },
	  maxTime_setter: function (time) {
	    if (typeof time == "string") {
	      time = i18n.parseTimeFormatDate(time);
	      time = [time.getHours(), time.getMinutes()];
	    }

	    return time;
	  },
	  _ariaFocus: function () {
	    var ev = "focus" + (env.isIE ? "in" : "");

	    if (!env.touch) {
	      _event(this.$view, ev, bind(function (e) {
	        var t = e.target.className;
	        var css = t.indexOf("webix_cal_day") !== -1 ? "webix_cal_day" : t.indexOf("webix_cal_block") !== -1 ? "webix_cal_block" : "";

	        if (new Date() - UIManager._tab_time > 300 && new Date() - UIManager._mouse_time > 100 && css) {
	          var prev = e.relatedTarget;

	          if (prev && !isUndefined(prev.className)) {
	            var date = css == "webix_cal_day" ? this._locate_day(e.target) : this._locate_date(e.target);

	            this._moveSelection(date);
	          }
	        }
	      }, this), {
	        capture: !env.isIE
	      });
	    }
	  },
	  $init: function () {
	    this._viewobj.className += " webix_calendar";

	    this._viewobj.setAttribute("role", "region");

	    this._viewobj.setAttribute("aria-label", i18n.aria.calendar); //special dates


	    this._special_dates = {};
	    this._selected_days = {};
	    this._zoom_level = 0; //navigation and aria

	    this._ariaFocus();

	    this.attachEvent("onKeyPress", this._onKeyPress);
	    this.attachEvent("onAfterZoom", function (zoom) {
	      if (zoom >= 0) this.$view.querySelector(".webix_cal_month_name").blur();
	    });
	  },
	  type_setter: function (value) {
	    if (value == "time") {
	      this._zoom_in = true;
	      this._zoom_level = -1;
	    } else if (value == "year") {
	      this._fixed = true;
	    }

	    return value;
	  },
	  $setSize: function (x, y) {
	    if (base.api.$setSize.call(this, x, y)) {
	      //repaint calendar when size changed
	      this.render();
	    }
	  },
	  $getSize: function (dx, dy) {
	    if (this._settings.cellHeight) {
	      var state = this._getDateBoundaries(this._settings.date);

	      this._settings.height = this._settings.cellHeight * state._rows + ($active.calendarHeight || 70);
	    }

	    return base.api.$getSize.call(this, dx, dy);
	  },
	  moveSelection: function (mode, details, focus) {
	    if (this.config.master) return; //in daterange

	    var start = this.getSelectedDate();
	    if (this.config.multiselect) start = start[0];
	    var date = wDate.copy(start || this.getVisibleDate());

	    this._moveSelection(date, mode, focus);
	  },
	  _moveSelection: function (date, mode, focus) {
	    var css = this._zoom_logic[this._zoom_level]._keyshift(date, mode, this);

	    if (focus !== false) {
	      var sel = this._viewobj.querySelector("." + css + "[tabindex='0']");

	      if (sel) sel.focus();
	    }
	  },
	  _getDateBoundaries: function (date, reset) {
	    // addition information about rendering event:
	    // how many days from the previous month,
	    // next,
	    // number of weeks to display and so on
	    if (!this._set_date_bounds || reset) {
	      var month = date.getMonth();
	      var year = date.getFullYear();
	      var next = new Date(year, month + 1, 1);
	      var start = wDate.weekStart(new Date(year, month, 1));
	      var days = Math.round((next.valueOf() - start.valueOf()) / (60 * 1000 * 60 * 24));
	      var rows = this._settings.skipEmptyWeeks ? Math.ceil(days / 7) : 6;
	      this._set_date_bounds = {
	        _month: month,
	        _start: start,
	        _next: next,
	        _rows: rows
	      };
	    }

	    return this._set_date_bounds;
	  },
	  $skin: function () {
	    if ($active.calendar) {
	      if ($active.calendar.width) this.defaults.width = $active.calendar.width;
	      if ($active.calendar.height) this.defaults.height = $active.calendar.height;
	      if ($active.calendar.headerHeight) this.defaults.headerHeight = $active.calendar.headerHeight;
	      if ($active.calendar.timepickerHeight) this.defaults.timepickerHeight = $active.calendar.timepickerHeight;
	    }
	  },
	  _getColumnConfigSizes: function (date) {
	    var bounds = this._getDateBoundaries(date);

	    var s = this._settings;
	    var _columnsHeight = [];
	    var _columnsWidth = [];
	    var min = Infinity;
	    var containerWidth = this._content_width - 36;
	    var containerHeight = this._content_height - this._settings.headerHeight - 10 - (this._settings.timepicker || this._icons ? this._settings.timepickerHeight : 0);
	    var columnsNumber = s.weekNumber ? 8 : 7;

	    for (var i = 0; i < columnsNumber; i++) {
	      _columnsWidth[i] = Math.ceil(containerWidth / (columnsNumber - i));
	      containerWidth -= _columnsWidth[i];
	      min = Math.min(min, _columnsWidth[i]);
	    }

	    var rowsNumber = bounds._rows;

	    for (var k = 0; k < rowsNumber; k++) {
	      _columnsHeight[k] = Math.ceil(containerHeight / (rowsNumber - k));
	      containerHeight -= _columnsHeight[k];
	      min = Math.min(min, _columnsHeight[k]);
	    }

	    return [_columnsWidth, _columnsHeight, min];
	  },
	  icons_setter: function (value) {
	    if (!value) this._icons = null;else if (_typeof(value) == "object") this._icons = value;else this._icons = this._icons2;
	  },
	  _icons: [],
	  _icons2: [{
	    template: function () {
	      return "<span role='button' tabindex='0' class='webix_cal_icon_today webix_cal_icon'>" + i18n.calendar.today + "</span>";
	    },
	    on_click: {
	      "webix_cal_icon_today": function () {
	        var date = new Date();
	        if (!this._settings.timepicker) date = wDate.datePart(date);
	        this.setValue(date);
	        this.callEvent("onTodaySet", [this.getSelectedDate()]);
	      }
	    }
	  }, {
	    template: function () {
	      return "<span role='button' tabindex='0' class='webix_cal_icon_clear webix_cal_icon'>" + i18n.calendar.clear + "</span>";
	    },
	    on_click: {
	      "webix_cal_icon_clear": function () {
	        this.setValue("");
	        this.callEvent("onDateClear", [this.getSelectedDate()]);
	      }
	    }
	  }],
	  refresh: function () {
	    this.render();
	  },
	  render: function () {
	    //reset zoom level
	    this._zoom_level = 0;
	    this._zoom_size = false;
	    var s = this._settings;
	    if (!this.isVisible(s.id)) return;
	    this._current_time = wDate.datePart(new Date());
	    this.callEvent("onBeforeRender", []);
	    var date = this._settings.date;

	    var bounds = this._getDateBoundaries(date, true);

	    var sizes = this._getColumnConfigSizes(date);

	    var width = sizes[0];
	    var height = sizes[1];
	    var html = "<div class='webix_cal_month'><span role='button' tabindex='0' aria-live='assertive' aria-atomic='true' class='webix_cal_month_name" + (!this._settings.monthSelect ? " webix_readonly" : "") + "'>" + s.calendarHeader(date) + "</span>";
	    if (s.navigation) html += "<div role='button' tabindex='0' aria-label='" + i18n.aria.navMonth[0] + "' class='webix_cal_prev_button'></div><div role='button' tabindex='0' aria-label='" + i18n.aria.navMonth[1] + "' class='webix_cal_next_button'></div>";
	    html += "</div>";
	    if (s.weekHeader) html += "<div class='webix_cal_header' aria-hidden='true'>" + this._week_template(width) + "</div>";
	    html += "<div class='webix_cal_body'>" + this._body_template(width, height, bounds, sizes[2]) + "</div>";

	    if (this._settings.timepicker || this._icons) {
	      html += "<div class='webix_cal_footer'>";
	      if (this._settings.timepicker) html += this._timepicker_template(date);
	      if (this._icons) html += this._icons_template();
	      html += "</div>";
	    }

	    this._contentobj.innerHTML = html;

	    if (this._settings.type == "time") {
	      var time = this._settings.date;

	      if (time) {
	        if (typeof time == "string") {
	          date = i18n.parseTimeFormatDate(time);
	        } else if (isArray(time)) {
	          date.setHours(time[0]);
	          date.setMinutes(time[1]);
	        }
	      }

	      this._changeZoomLevel(-1, date);
	    } else if (this._settings.type == "month") {
	      this._changeZoomLevel(1, date);
	    } else if (this._settings.type == "year") {
	      this._changeZoomLevel(2, date);
	    }

	    this.callEvent("onAfterRender", []);
	  },
	  _icons_template: function (date) {
	    var html = "<div class='webix_cal_icons'>";
	    var icons = this._icons;

	    for (var i = 0; i < icons.length; i++) {
	      if (icons[i].template) {
	        var template$$1 = typeof icons[i].template == "function" ? icons[i].template : template$$1(icons[i].template);
	        html += template$$1.call(this, date);
	      }

	      if (icons[i].on_click) {
	        exports.extend(this.on_click, icons[i].on_click);
	      }
	    }

	    html += "</div>";
	    return html;
	  },
	  _timepicker_template: function (date) {
	    var timeFormat = this._settings.calendarTime || i18n.timeFormatStr;
	    var clock = this._settings.timeIcon;
	    var tpl = "";
	    if (!this._settings.master) tpl = "<div role='button' tabindex='0' class='webix_cal_time" + (this._icons ? " webix_cal_time_icons" : "") + "'><span class='webix_icon " + clock + "'></span> " + timeFormat(date) + "</div>";else {
	      //daterange needs two clocks
	      var range_date = copy($$(this._settings.master)._settings.value);
	      if (wDate.equal(range_date.end, date)) range_date.start = range_date.end;

	      for (var i in range_date) {
	        tpl += "<div role='button' tabindex='0' class='webix_range_time_" + i + " webix_cal_time'><span class='webix_icon " + clock + "'></span> " + timeFormat(range_date[i]) + "</div>";
	      }
	    }
	    return tpl;
	  },
	  _week_template: function (widths) {
	    var s = this._settings;
	    var week_template = "";
	    var correction = 0;

	    if (s.weekNumber) {
	      correction = 1;
	      week_template += "<div class='webix_cal_week_header' style='width: " + widths[0] + "px;' >" + s.calendarWeekHeader() + "</div>";
	    }

	    var k = wDate.startOnMonday ? 1 : 0;

	    for (var i = 0; i < 7; i++) {
	      // 7 days total
	      var day_index = (k + i) % 7; // 0 - Sun, 6 - Sat as in Locale.date.day_short

	      var day = i18n.calendar.dayShort[day_index]; // 01, 02 .. 31

	      week_template += "<div day='" + day_index + "' style='width: " + widths[i + correction] + "px;' >" + day + "</div>";
	    }

	    return week_template;
	  },
	  blockDates_setter: function (value) {
	    return toFunctor(value, this.$scope);
	  },
	  _day_css: function (day, bounds) {
	    var css = "",
	        isOutside = false;
	    if (wDate.equal(day, this._current_time)) css += " webix_cal_today";
	    if (!this._checkDate(day)) css += " webix_cal_day_disabled";

	    if (day.getMonth() != bounds._month) {
	      isOutside = true;
	      css += " webix_cal_outside";
	    }

	    if (!isOutside && this._selectedDay(day)) css += " webix_cal_select";
	    if (this._settings.events) css += " " + (this._settings.events(day, isOutside) || "");
	    css += " webix_cal_day";
	    return css;
	  },
	  _body_template: function (widths, heights, bounds, sqSize) {
	    var s = this._settings;
	    var html = "";
	    var day = wDate.datePart(wDate.copy(bounds._start));
	    var start = s.weekNumber ? 1 : 0;
	    var weekNumber = wDate.getISOWeek(wDate.add(day, 2, "day", true));

	    for (var y = 0; y < heights.length; y++) {
	      html += "<div class='webix_cal_row' style='height:" + heights[y] + "px;line-height:" + heights[y] + "px'>";

	      if (start) {
	        // recalculate week number for the first week of a year
	        if (!day.getMonth() && day.getDate() < 7) weekNumber = wDate.getISOWeek(wDate.add(day, 2, "day", true));
	        html += "<div class='webix_cal_week_num' aria-hidden='true' style='width:" + widths[0] + "px'>" + weekNumber + "</div>";
	      }

	      for (var x = start; x < widths.length; x++) {
	        var css = this._day_css(day, bounds);

	        var d = this._settings.dayTemplate.call(this, day);

	        var sel = this._selectedDay(day);

	        var alabel = "";

	        var isOutside = day.getMonth() != bounds._month;

	        if (_typeof(d) == "object") {
	          alabel = d.aria || alabel;
	          d = d.text;
	        } else alabel = wDate.dateToStr(i18n.aria.dateFormat)(day);

	        html += "<div day='" + x + "' role='gridcell' " + (isOutside ? "aria-hidden='true'" : "") + " aria-label='" + alabel + "' tabindex='" + (sel && !isOutside ? "0" : "-1") + "' aria-selected='" + (sel && !isOutside ? "true" : "false") + "' class='" + css + "' style='text-align:center; width:" + widths[x] + "px'><span aria-hidden='true' class='webix_cal_day_inner' style='display:inline-block; " + this._getCalSizesString(sqSize, sqSize) + "'>" + d + "</span></div>";
	        day = wDate.add(day, 1, "day");

	        if (day.getHours()) {
	          day = wDate.datePart(day);
	        }
	      }

	      html += "</div>";
	      weekNumber++;
	    }

	    return html;
	  },
	  _changeDate: function (dir, step) {
	    var now = this._settings.date;

	    if (!step) {
	      step = this._zoom_logic[this._zoom_level]._changeStep;
	    }

	    if (!this._zoom_level) {
	      now = wDate.copy(now);
	      now.setDate(1);
	    }

	    var next = wDate.add(now, dir * step, "month", true);

	    this._changeDateInternal(now, next);
	  },
	  _changeDateInternal: function (now, next) {
	    if (this.callEvent("onBeforeMonthChange", [now, next])) {
	      if (this._zoom_level) {
	        this._update_zoom_level(next);
	      } else {
	        this.showCalendar(next);
	      }

	      this.callEvent("onAfterMonthChange", [next, now]);
	    }
	  },
	  _zoom_logic: {
	    "-2": {
	      _isBlocked: function (i) {
	        var config = this._settings,
	            date = config.date,
	            isBlocked = false;
	        var minHour = config.minTime ? config.minTime[0] : 0;
	        var maxHour = config.maxTime ? config.maxTime[0] + (config.maxTime[1] ? 1 : 0) : 24;
	        var minMinute = config.minTime && date.getHours() == minHour ? config.minTime[1] : 0;
	        var maxMinute = config.maxTime && config.maxTime[1] && date.getHours() == maxHour - 1 ? config.maxTime[1] : 60;

	        if (this._settings.blockTime) {
	          var d = wDate.copy(date);
	          d.setMinutes(i);
	          isBlocked = this._settings.blockTime(d);
	        }

	        return i < minMinute || i >= maxMinute || isBlocked;
	      },
	      _setContent: function (next, i) {
	        next.setMinutes(i);
	      },
	      _findActive: function (date, mode, calendar) {
	        if (!this._isBlocked.call(calendar, date.getMinutes())) return date;else {
	          var step = calendar._settings.minuteStep;
	          var newdate = wDate.add(date, mode == "right" ? step : -step, "minute", true);
	          if (date.getHours() === newdate.getHours()) return this._findActive(newdate, mode, calendar);
	        }
	      }
	    },
	    "-1": {
	      _isBlocked: function (i) {
	        var config = this._settings,
	            date = config.date;
	        var minHour = config.minTime ? config.minTime[0] : 0;
	        var maxHour = config.maxTime ? config.maxTime[0] + (config.maxTime[1] ? 1 : 0) : 24;
	        if (i < minHour || i >= maxHour) return true;

	        if (config.blockTime) {
	          var d = wDate.copy(date);
	          d.setHours(i);
	          var minMinute = config.minTime && i == minHour ? config.minTime[1] : 0;
	          var maxMinute = config.maxTime && config.maxTime[1] && i == maxHour - 1 ? config.maxTime[1] : 60;

	          for (var j = minMinute; j < maxMinute; j += config.minuteStep) {
	            d.setMinutes(j);
	            if (!config.blockTime(d)) return false;
	          }

	          return true;
	        }
	      },
	      _setContent: function (next, i) {
	        next.setHours(i);
	      },
	      _keyshift: function (date, mode, calendar) {
	        var newdate,
	            inc,
	            step = calendar._settings.minuteStep;

	        if (mode === "bottom" || mode === "top") {
	          date.setHours(mode === "bottom" ? 23 : 0);
	          date.setMinutes(mode === "bottom" ? 55 : 0);
	          date.setSeconds(0);
	          date.setMilliseconds(0);
	          newdate = date;
	        } else if (mode === "left" || mode === "right") {
	          //minutes
	          inc = mode === "right" ? step : -step;
	          if (mode === "left" && date.getMinutes() < step) inc = 60 - step;
	          if (mode === "right" && date.getMinutes() >= 60 - step) inc = step - 60;
	          inc -= date.getMinutes() % step;
	          newdate = calendar._zoom_logic["-2"]._findActive(wDate.add(date, inc, "minute"), mode, calendar);
	        } else if (mode === "up" || mode === "down") {
	          //hours
	          inc = mode === "down" ? 1 : -1;
	          if (mode === "down" && date.getHours() === 23) inc = -23;
	          if (mode === "up" && date.getHours() === 0) inc = 23;
	          newdate = this._findActive(wDate.add(date, inc, "hour"), mode, calendar);
	        } else if (mode === false) newdate = this._findActive(date, mode, calendar);

	        calendar.selectDate(newdate, false);

	        if (newdate) {
	          calendar._update_zoom_level(newdate);

	          calendar.selectDate(newdate, false);
	        }

	        return "webix_cal_block" + (mode === "left" || mode === "right" ? "_min" : "");
	      },
	      _findActive: function (date, mode, calendar) {
	        if (!this._isBlocked.call(calendar, date.getHours())) return date;else {
	          var newdate = wDate.add(date, mode == "down" ? 1 : -1, "hour", true);
	          if (date.getDate() === newdate.getDate()) return this._findActive(newdate, mode, calendar);
	        }
	      }
	    },
	    "0": {
	      //days
	      _changeStep: 1,
	      _keyshift: function (date, mode, calendar) {
	        var newdate = date;
	        if (mode === "pgup" || mode === "pgdown") newdate = wDate.add(date, mode === "pgdown" ? 1 : -1, "month");else if (mode === "bottom") newdate = new Date(date.getFullYear(), date.getMonth() + 1, 0);else if (mode === "top") newdate = new Date(date.setDate(1));else if (mode === "left" || mode === "right") newdate = wDate.add(date, mode === "right" ? 1 : -1, "day");else if (mode === "up" || mode === "down") newdate = wDate.add(date, mode === "down" ? 1 : -1, "week");
	        if (!calendar._checkDate(newdate)) newdate = calendar._findActive(date, mode);
	        if (newdate) calendar.selectDate(newdate, true);
	        return "webix_cal_day";
	      }
	    },
	    "1": {
	      //months
	      _isBlocked: function (i, calendar) {
	        var blocked = false,
	            min = calendar._settings.minDate,
	            max = calendar._settings.maxDate,
	            year = calendar._settings.date.getFullYear();

	        if (min) {
	          var minYear = min.getFullYear();
	          blocked = year < minYear || year == minYear && min.getMonth() > i;
	        }

	        if (max && !blocked) {
	          var maxYear = max.getFullYear();
	          blocked = year > maxYear || year == maxYear && max.getMonth() < i;
	        }

	        return blocked;
	      },
	      _correctDate: function (date, calendar) {
	        if (date < calendar._settings.minDate) {
	          date = wDate.copy(calendar._settings.minDate);
	        } else if (date > calendar._settings.maxDate) {
	          date = wDate.copy(calendar._settings.maxDate);
	        }

	        return wDate.monthStart(date);
	      },
	      _getTitle: function (date) {
	        return date.getFullYear();
	      },
	      _getContent: function (i) {
	        return i18n.calendar.monthShort[i];
	      },
	      _setContent: function (next, i) {
	        if (i != next.getMonth()) next.setDate(1);
	        next.setMonth(i);
	      },
	      _changeStep: 12,
	      _keyshift: function (date, mode, calendar) {
	        var newdate = date;
	        if (mode === "pgup" || mode === "pgdown") newdate = wDate.add(date, mode === "pgdown" ? 1 : -1, "year");else if (mode === "bottom") newdate = new Date(date.setMonth(11));else if (mode === "top") newdate = new Date(date.setMonth(0));else if (mode === "left" || mode === "right") newdate = wDate.add(date, mode === "right" ? 1 : -1, "month");else if (mode === "up" || mode === "down") newdate = wDate.add(date, mode === "down" ? 4 : -4, "month");
	        if (!calendar._checkDate(newdate)) newdate = calendar._findActive(date, mode);

	        if (newdate) {
	          calendar._update_zoom_level(newdate);

	          calendar.selectDate(newdate, false);
	        }

	        return "webix_cal_block";
	      }
	    },
	    "2": {
	      //years
	      _isBlocked: function (i, calendar) {
	        i += calendar._zoom_start_date;
	        var min = calendar._settings.minDate;
	        var max = calendar._settings.maxDate;
	        if (min && min.getFullYear() > i || max && max.getFullYear() < i) return true;
	        return false;
	      },
	      _correctDate: function (date, calendar) {
	        if (date < calendar._settings.minDate) {
	          date = wDate.copy(calendar._settings.minDate);
	        } else if (date > calendar._settings.maxDate) {
	          date = wDate.copy(calendar._settings.maxDate);
	        }

	        return wDate.yearStart(date);
	      },
	      _getTitle: function (date, calendar) {
	        var start = date.getFullYear();
	        calendar._zoom_start_date = start = start - start % 10 - 1;
	        return start + " - " + (start + 10 + 1);
	      },
	      _getContent: function (i, calendar) {
	        return calendar._zoom_start_date + i;
	      },
	      _setContent: function (next, i, calendar) {
	        next.setFullYear(calendar._zoom_start_date + i);
	      },
	      _changeStep: 12 * 10,
	      _keyshift: function (date, mode, calendar) {
	        var newdate = date;
	        if (mode === "pgup" || mode === "pgdown") newdate = wDate.add(date, mode === "pgdown" ? 10 : -10, "year");else if (mode === "bottom") newdate = new Date(date.setYear(calendar._zoom_start_date + 10));else if (mode === "top") newdate = new Date(date.setYear(calendar._zoom_start_date));else if (mode === "left" || mode === "right") newdate = wDate.add(date, mode === "right" ? 1 : -1, "year");else if (mode === "up" || mode === "down") newdate = wDate.add(date, mode === "down" ? 4 : -4, "year");
	        if (!calendar._checkDate(newdate)) newdate = calendar._findActive(date, mode);

	        if (newdate) {
	          calendar._update_zoom_level(newdate);

	          calendar.selectDate(newdate, false);
	        }

	        return "webix_cal_block";
	      }
	    }
	  },
	  _correctBlockedTime: function () {
	    var i, isDisabledHour, isDisabledMinutes;
	    isDisabledHour = this._zoom_logic[-1]._isBlocked.call(this, this._settings.date.getHours());

	    if (isDisabledHour) {
	      for (i = 0; i < 24; i++) {
	        if (!this._zoom_logic[-1]._isBlocked.call(this, i)) {
	          this._settings.date.setHours(i);

	          break;
	        }
	      }
	    }

	    isDisabledMinutes = this._zoom_logic[-2]._isBlocked.call(this, this._settings.date.getMinutes());

	    if (isDisabledMinutes) {
	      for (i = 0; i < 60; i += this._settings.minuteStep) {
	        if (!this._zoom_logic[-2]._isBlocked.call(this, i)) {
	          this._settings.date.setMinutes(i);

	          break;
	        }
	      }
	    }
	  },
	  _update_zoom_level: function (date) {
	    var config, css, height, i, index$$1, sections, selected, type, width, zlogic, temp, sqSize;
	    var html = "";
	    config = this._settings;
	    index$$1 = config.weekHeader ? 2 : 1;
	    zlogic = this._zoom_logic[this._zoom_level];
	    sections = this._contentobj.childNodes;

	    if (date) {
	      config.date = date;
	    }

	    type = config.type; //store width and height of draw area

	    if (!this._zoom_size) {
	      /*this._reserve_box_height = sections[index].offsetHeight +(index==2?sections[1].offsetHeight:0);*/
	      this._reserve_box_height = this._contentobj.offsetHeight - config.headerHeight;
	      if (type != "year" && type != "month") this._reserve_box_height -= config.timepickerHeight;else if (this._icons) {
	        this._reserve_box_height -= 10;
	      }
	      this._reserve_box_width = sections[index$$1].offsetWidth;
	      this._zoom_size = 1;
	    } //main section


	    if (this._zoom_in) {
	      //hours and minutes
	      height = this._reserve_box_height / 6;
	      var timeColNum = 6;
	      var timeFormat = this._calendarTime || i18n.timeFormat;
	      var enLocale = timeFormat.match(/%([a,A])/);
	      if (enLocale) timeColNum++;
	      width = parseInt((this._reserve_box_width - 3) / timeColNum, 10);
	      sqSize = Math.min(width, height);
	      html += "<div class='webix_time_header'>" + this._timeHeaderTemplate(width, enLocale) + "</div>";
	      html += "<div  class='webix_cal_body' style='height:" + this._reserve_box_height + "px'>"; // check and change blocked selected time

	      this._correctBlockedTime();

	      html += "<div class='webix_hours'>";
	      selected = config.date.getHours();
	      temp = wDate.copy(config.date);

	      for (i = 0; i < 24; i++) {
	        css = "";

	        if (enLocale) {
	          if (i % 4 === 0) {
	            var label = !i ? i18n.am[0] : i == 12 ? i18n.pm[0] : "";
	            html += "<div class='webix_cal_block_empty" + css + "' style='" + this._getCalSizesString(width, height) + "clear:both;" + "'>" + label + "</div>";
	          }
	        }

	        if (this._zoom_logic[-1]._isBlocked.call(this, i)) {
	          css += " webix_cal_day_disabled";
	        } else if (selected == i) css += " webix_selected";

	        temp.setHours(i);
	        html += "<div aria-label='" + wDate.dateToStr(i18n.aria.hourFormat)(temp) + "' role='gridcell'" + " tabindex='" + (selected == i ? "0" : "-1") + "' aria-selected='" + (selected == i ? "true" : "false") + "' class='webix_cal_block" + css + "' data-value='" + i + "' style='" + this._getCalSizesString(width, height) + (i % 4 === 0 && !enLocale ? "clear:both;" : "") + "'><span style='display:inline-block; " + this._getCalSizesString(sqSize, sqSize) + "'>" + wDate.toFixed(enLocale ? !i || i == 12 ? 12 : i % 12 : i) + "</span></div>";
	      }

	      html += "</div>";
	      html += "<div class='webix_minutes'>";
	      selected = config.date.getMinutes();
	      temp = wDate.copy(config.date);

	      for (i = 0; i < 60; i += config.minuteStep) {
	        css = "";

	        if (this._zoom_logic[-2]._isBlocked.call(this, i)) {
	          css = " webix_cal_day_disabled";
	        } else if (selected == i) css = " webix_selected";

	        temp.setMinutes(i);
	        html += "<div aria-label='" + wDate.dateToStr(i18n.aria.minuteFormat)(temp) + "' role='gridcell' tabindex='" + (selected == i ? "0" : "-1") + "' aria-selected='" + (selected == i ? "true" : "false") + "' class='webix_cal_block webix_cal_block_min" + css + "' data-value='" + i + "' style='" + this._getCalSizesString(width, height) + (i % 2 === 0 ? "clear:both;" : "") + "'><span style='display:inline-block; " + this._getCalSizesString(sqSize, sqSize) + "'>" + wDate.toFixed(i) + "</span></div>";
	      }

	      html += "</div>";
	      html += "</div>";
	      html += "<div  class='webix_time_footer'>" + this._timeButtonsTemplate() + "</div>";
	      this._contentobj.innerHTML = html;
	    } else {
	      //years and months
	      //reset header
	      var header = sections[0].childNodes;
	      var labels = i18n.aria["nav" + (this._zoom_level == 1 ? "Year" : "Decade")];
	      header[0].innerHTML = zlogic._getTitle(config.date, this);
	      header[1].setAttribute("aria-label", labels[0]);
	      header[2].setAttribute("aria-label", labels[1]);
	      height = Math.floor(this._reserve_box_height / 3);
	      width = Math.floor(this._reserve_box_width / 4);
	      sqSize = Math.min(height, width);
	      if (this._checkDate(config.date)) selected = this._zoom_level == 1 ? config.date.getMonth() : config.date.getFullYear();

	      for (i = 0; i < 12; i++) {
	        css = selected == (this._zoom_level == 1 ? i : zlogic._getContent(i, this)) ? " webix_selected" : "";

	        if (zlogic._isBlocked(i, this)) {
	          css += " webix_cal_day_disabled";
	        }

	        var format = i18n.aria[(this._zoom_level == 1 ? "month" : "year") + "Format"];
	        html += "<div role='gridcell' aria-label='" + wDate.dateToStr(format)(config.date) + "' tabindex='" + (css.indexOf("selected") !== -1 ? "0" : "-1") + "' aria-selected='" + (css.indexOf("selected") !== -1 ? "true" : "false") + "' class='webix_cal_block" + css + "' data-value='" + i + "' style='" + this._getCalSizesString(width, height) + "'><span style='display:inline-block; " + this._getCalSizesString(sqSize, sqSize) + "'>" + zlogic._getContent(i, this) + "</span></div>";
	      }

	      if (index$$1 - 1) {
	        sections[index$$1 - 1].style.display = "none";
	      }

	      sections[index$$1].innerHTML = html;

	      if (type != "year" && type != "month") {
	        if (!sections[index$$1 + 1]) this._contentobj.innerHTML += "<div  class='webix_time_footer'>" + this._timeButtonsTemplate() + "</div>";else sections[index$$1 + 1].innerHTML = this._timeButtonsTemplate();
	      }

	      sections[index$$1].style.height = this._reserve_box_height + "px";
	    }
	  },
	  _getCalSizesString: function (width, height) {
	    return "width:" + width + "px; height:" + height + "px; line-height:" + height + "px;";
	  },
	  _timeButtonsTemplate: function () {
	    return "<input type='button' style='width:100%' class='webix_cal_done' value='" + i18n.calendar.done + "'>";
	  },
	  _timeHeaderTemplate: function (width, enLocale) {
	    var w1 = width * (enLocale ? 5 : 4);
	    var w2 = width * 2;
	    return "<div class='webix_cal_hours' style='width:" + w1 + "px'>" + i18n.calendar.hours + "</div><div class='webix_cal_minutes' style='width:" + w2 + "px'>" + i18n.calendar.minutes + "</div>";
	  },
	  _changeZoomLevel: function (zoom, date) {
	    var oldzoom = this._zoom_level;

	    if (this.callEvent("onBeforeZoom", [zoom, oldzoom])) {
	      this._zoom_level = zoom;
	      if (zoom) this._update_zoom_level(date);else this.showCalendar(date);
	      this.callEvent("onAfterZoom", [zoom, oldzoom]);
	    }
	  },
	  _correctDate: function (date) {
	    if (!this._checkDate(date) && this._zoom_logic[this._zoom_level]._correctDate) date = this._zoom_logic[this._zoom_level]._correctDate(date, this);
	    return date;
	  },
	  _mode_selected: function (target) {
	    var next = this._locate_date(target);

	    var zoom = this._zoom_level - (this._fixed ? 0 : 1);
	    next = this._correctDate(next);

	    if (this._checkDate(next)) {
	      this._changeZoomLevel(zoom, next);

	      var type = this._settings.type;
	      if (type == "month" || type == "year") this._selectDate(next);
	    }
	  },
	  // selects date and redraw calendar
	  _selectDate: function (date, add) {
	    if (this.callEvent("onBeforeDateSelect", [date])) {
	      this.selectDate(date, true, add);
	      this.callEvent("onDateSelect", [date]); // should be deleted in a future version

	      this.callEvent("onAfterDateSelect", [date]);
	    }
	  },
	  _locate_day: function (target) {
	    var cind = index(target) - (this._settings.weekNumber ? 1 : 0);
	    var rind = index(target.parentNode);
	    var date = wDate.add(this._getDateBoundaries()._start, cind + rind * 7, "day", true);

	    if (this._settings.timepicker) {
	      date.setHours(this._settings.date.getHours());
	      date.setMinutes(this._settings.date.getMinutes());
	    }

	    return date;
	  },
	  _locate_date: function (target) {
	    var value = target.getAttribute("data-value") * 1;
	    var level = target.className.indexOf("webix_cal_block_min") != -1 ? this._zoom_level - 1 : this._zoom_level;
	    var now = this._settings.date;
	    var next = wDate.copy(now);

	    this._zoom_logic[level]._setContent(next, value, this);

	    return next;
	  },
	  on_click: {
	    webix_cal_prev_button: function () {
	      this._changeDate(-1);
	    },
	    webix_cal_next_button: function () {
	      this._changeDate(1);
	    },
	    webix_cal_day_disabled: function () {
	      return false;
	    },
	    webix_cal_outside: function () {
	      if (!this._settings.navigation) return false;
	    },
	    webix_cal_day: function (e, id, target) {
	      var date = this._locate_day(target);

	      var add = this._settings.multiselect === "touch" || e.ctrlKey || e.metaKey;

	      this._selectDate(date, add);
	    },
	    webix_cal_time: function () {
	      if (this._zoom_logic[this._zoom_level - 1]) {
	        this._zoom_in = true;
	        var zoom = this._zoom_level - 1;

	        this._changeZoomLevel(zoom);
	      }
	    },
	    webix_range_time_start: function () {
	      $$(this._settings.master)._time_mode = "start";
	    },
	    webix_range_time_end: function () {
	      $$(this._settings.master)._time_mode = "end";
	    },
	    webix_cal_done: function () {
	      var date = wDate.copy(this._settings.date);
	      date = this._correctDate(date);

	      this._selectDate(date);
	    },
	    webix_cal_month_name: function () {
	      this._zoom_in = false; //maximum zoom reached

	      if (this._zoom_level == 2 || !this._settings.monthSelect) return;
	      var zoom = Math.max(this._zoom_level, 0) + 1;

	      this._changeZoomLevel(zoom);
	    },
	    webix_cal_block: function (e, id, trg) {
	      if (this._zoom_in) {
	        if (trg.className.indexOf("webix_cal_day_disabled") !== -1) return false;

	        var next = this._locate_date(trg);

	        this._update_zoom_level(next);
	      } else {
	        if (trg.className.indexOf("webix_cal_day_disabled") == -1) this._mode_selected(trg);
	      }
	    }
	  },
	  _string_to_date: function (date, format) {
	    if (!date) {
	      return wDate.datePart(new Date());
	    }

	    if (typeof date == "string") {
	      if (format) date = wDate.strToDate(format)(date);else date = i18n.parseFormatDate(date);
	    }

	    return date;
	  },
	  _checkDate: function (date) {
	    var blockedDate = this._settings.blockDates && this._settings.blockDates.call(this, date);

	    var minDate = this._settings.minDate;
	    var maxDate = this._settings.maxDate;
	    var outOfRange = date < minDate || date > maxDate;
	    return !blockedDate && !outOfRange;
	  },
	  _findActive: function (date, mode) {
	    var dir = mode === "top" || mode === "left" || mode === "pgup" || mode === "up" ? -1 : 1;
	    var newdate = wDate.add(date, dir, "day", true);
	    if (this._checkDate(newdate)) return newdate;else {
	      var compare;
	      if (this._zoom_level === 0) compare = date.getMonth() === newdate.getMonth();else if (this._zoom_level === 1) compare = date.getFullYear() === newdate.getFullYear();else if (this._zoom_level === 2) compare = newdate.getFullYear() > this._zoom_start_date && newdate.getFullYear() < this._zoom_start_date + 10;
	      if (compare) return this._findActive(newdate, mode);
	    }
	  },
	  showCalendar: function (date) {
	    date = this._string_to_date(date);
	    this._settings.date = date;
	    this.render();
	    this.resize();
	  },
	  _selectedDay: function (day) {
	    return day && this._selected_days[day.valueOf()];
	  },
	  getSelectedDate: function () {
	    var result = [];

	    for (var key in this._selected_days) {
	      result.push(wDate.copy(this._selected_days[key]));
	    }

	    return this.config.multiselect ? result : result[0] || null;
	  },
	  getVisibleDate: function () {
	    return wDate.copy(this._settings.date);
	  },
	  setValue: function (date) {
	    this.selectDate(date, true);
	  },
	  getValue: function (format) {
	    var date = this.getSelectedDate();
	    if (format) date = wDate.dateToStr(format)(date);
	    return date;
	  },
	  selectDate: function (date, show, add) {
	    if (!date || !add || !this.config.multiselect) this._selected_days = {};

	    if (date) {
	      if (!isArray(date)) date = [date];

	      for (var i = 0; i < date.length; i++) {
	        var days = this._string_to_date(date[i]);

	        var key = wDate.datePart(wDate.copy(days)).valueOf();
	        if (this._selected_days[key] && add) delete this._selected_days[key];else this._selected_days[key] = days;
	        if (!this.config.multiselect) break;
	      }

	      if (date.length && show) this.showCalendar(date[0]);
	    }

	    if (show !== false) this.render();
	    this.callEvent("onChange", [date]);
	  },
	  locate: function () {
	    return null;
	  }
	};
	var view$s = exports.protoUI(api$s, KeysNavigation, MouseEvents, base.view, EventSystem);

	var api$t = {
	  name: "colorboard",
	  defaults: {
	    template: "<div style=\"width:100%;height:100%;background-color:{obj.val}\"></div>",
	    palette: null,
	    height: 220,
	    width: 220,
	    cols: 12,
	    rows: 10,
	    minLightness: 0.15,
	    maxLightness: 1,
	    navigation: true
	  },
	  $init: function () {
	    _event(this._viewobj, "click", bind(function (e) {
	      var value = locate(e,
	      /*@attr*/
	      "webix_val");
	      this.setValue(value);
	      this.callEvent("onItemClick", [this._settings.value, e]);
	      this.callEvent("onSelect", [this._settings.value]);
	    }, this));

	    this.$view.setAttribute("role", "grid");

	    this._viewobj.setAttribute("aria-readonly", "true");
	  },
	  _set_item_focus: function () {
	    if (!this.getValue()) this.moveSelection("up");
	  },
	  _findIndex: function (value) {
	    var pal = this._settings.palette;
	    value = (value || "").toUpperCase();

	    for (var r = 0, rows = pal.length; r < rows; r++) {
	      for (var c = 0, cols = pal[r].length; c < cols; c++) {
	        if (pal[r][c].toUpperCase() == value) {
	          return {
	            row: r,
	            col: c
	          };
	        }
	      }
	    }

	    return null;
	  },
	  $setSize: function (x, y) {
	    if (base.api.$setSize.call(this, x, y)) {
	      this.render();
	    }
	  },
	  getValue: function () {
	    return this._settings.value;
	  },
	  _getBox: function () {
	    return this._viewobj.firstChild;
	  },
	  setValue: function (value) {
	    if (value && value.toString().charAt(0) != "#") value = "#" + value;
	    var oldvalue = this._settings.value;
	    this._settings.value = value;
	    this.$setValue(value, oldvalue);
	    return value;
	  },
	  _selectBox: null,
	  _getSelectBox: function () {
	    if (this._selectBox && this._selectBox.parentNode) {
	      return this._selectBox;
	    } else {
	      var div = this._selectBox = document.createElement("div");
	      div.className = "webix_color_selector";

	      this._viewobj.lastChild.appendChild(div);

	      return div;
	    }
	  },
	  $setValue: function (value, oldvalue) {
	    if (this.isVisible(this._settings.id)) {
	      var cell,
	          div,
	          ind,
	          parent,
	          style,
	          left = 0,
	          top = 0; //remove tabindex for previous selection

	      if (oldvalue) ind = this._findIndex(oldvalue);
	      if (!ind) ind = {
	        row: 0,
	        col: 0
	      };

	      this._viewobj.lastChild.childNodes[ind.row].childNodes[ind.col].setAttribute("tabindex", "-1");

	      ind = this._findIndex(value);

	      if (ind) {
	        cell = this._viewobj.lastChild.childNodes[ind.row].childNodes[ind.col];
	      }

	      if (cell && cell.parentNode && cell.parentNode.parentNode) {
	        parent = cell.parentNode;
	        left = cell.offsetLeft - parent.offsetLeft;
	        top = -(this.$height - (cell.offsetTop - parent.parentNode.offsetTop));
	        cell.setAttribute("tabindex", "0");
	        cell.setAttribute("aria-selected", "true");
	        cell.setAttribute("tabindex", "0");
	        cell.setAttribute("aria-selected", "true");
	      } else {
	        if (this._selectBox) this._selectBox.style.left = "-100px";

	        this._viewobj.lastChild.childNodes[0].childNodes[0].setAttribute("tabindex", "0");

	        return;
	      }

	      div = this._getSelectBox();
	      style = ["left:" + left + "px", "top:" + top + "px", "width:" + cell.style.width, "height:" + cell.style.height].join(";");

	      if (typeof div.style.cssText !== "undefined") {
	        div.style.cssText = style;
	      } else {
	        div.setAttribute("style", style);
	      }
	    }
	  },
	  _initPalette: function (config) {
	    function numToHex(n) {
	      return color.toHex(n, 2);
	    }

	    function rgbToHex(r, g, b) {
	      return "#" + numToHex(Math.floor(r)) + numToHex(Math.floor(g)) + numToHex(Math.floor(b));
	    }

	    function hslToRgb(h, s, l) {
	      var r, g, b;

	      if (!s) {
	        r = g = b = l; // achromatic
	      } else {
	        var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
	        var p = 2 * l - q;
	        r = hue2rgb(p, q, h + 1 / 3);
	        g = hue2rgb(p, q, h);
	        b = hue2rgb(p, q, h - 1 / 3);
	      }

	      return {
	        r: r * 255,
	        g: g * 255,
	        b: b * 255
	      };
	    }

	    function hue2rgb(p, q, t) {
	      if (t < 0) t += 1;
	      if (t > 1) t -= 1;
	      if (t < 1 / 6) return p + (q - p) * 6 * t;else if (t <= 1 / 2) return q;else if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;else return p;
	    }

	    function renderGrayBar(colCount) {
	      var gray = [],
	          val = 255,
	          step = val / colCount;

	      for (var i = 0; i < colCount; i++) {
	        val = Math.round(val > 0 ? val : 0);
	        gray.push(rgbToHex(val, val, val));
	        val -= step;
	      }

	      gray[gray.length - 1] = "#000000";
	      return gray;
	    }

	    var colors = [];
	    var colorRows = config.rows - 1;
	    var colorStep = 1 / config.cols;
	    var lightStep = (config.maxLightness - config.minLightness) / colorRows;
	    var colorRange = null;
	    colors.push(renderGrayBar(config.cols));

	    for (var step = 0, lt = config.minLightness; step < colorRows; step++) {
	      colorRange = [];

	      for (var c = 0, col = 0; c < config.cols; c++) {
	        var val = hslToRgb(col, 1, lt);
	        colorRange.push(rgbToHex(val.r, val.g, val.b));
	        col += colorStep;
	      }

	      colors.push(colorRange);
	      lt += lightStep;
	    }

	    this._settings.palette = colors;
	  },
	  moveSelection: function (mode, details, focus) {
	    var value = this.getValue(),
	        ind,
	        cell;
	    if (value) ind = this._findIndex(value);
	    if (!ind) ind = {
	      row: 0,
	      col: 0
	    };

	    if (ind) {
	      if (mode == "up" || mode == "down") ind.row = ind.row + (mode == "up" ? -1 : 1);else if (mode == "right" || mode == "left") ind.col = ind.col + (mode == "right" ? 1 : -1);else if (mode == "top") ind.row = ind.col = 0;else if (mode == "bottom") {
	        ind.row = this._viewobj.lastChild.querySelectorAll(".webix_color_row").length - 1;
	        ind.col = this._viewobj.lastChild.childNodes[ind.row].childNodes.length - 1;
	      }
	      ind.row = Math.max(ind.row, 0);
	      if (ind.row >= 0) cell = this._viewobj.lastChild.childNodes[ind.row].childNodes[ind.col];

	      if (cell) {
	        value = cell.getAttribute(
	        /*@attr*/
	        "webix_val");
	        this.setValue(value);
	        this.callEvent("onSelect", [this._settings.value]);

	        if (focus !== false) {
	          var sel = this._viewobj.querySelector("div[tabindex='0']");

	          if (sel) sel.focus();
	        }
	      }
	    }
	  },
	  render: function () {
	    if (!this.isVisible(this._settings.id)) return;
	    if (!this._settings.palette) this._initPalette(this._settings);
	    var palette = this._settings.palette;
	    this.callEvent("onBeforeRender", []);
	    var config = this._settings,
	        itemTpl = template("<div role='gridcell' tabindex='-1' aria-label=\"{obj.val}\" style=\"width:{obj.width}px;height:{obj.height}px;\" " +
	    /*@attr*/
	    "webix_val" + "=\"{obj.val}\">" + (config.template || "") + "</div>"),
	        data = {
	      width: 0,
	      height: 0,
	      val: 0
	    },
	        width = this.$width,
	        height = this.$height,
	        widths = [];
	    var html = "<div class=\"webix_color_palette\"role=\"rowgroup\">";
	    var firstRow = _typeof(palette[0]) == "object" ? palette[0] : palette;

	    for (var i = 0; i < firstRow.length; i++) {
	      widths[i] = Math.floor(width / (firstRow.length - i));
	      width -= widths[i];
	    }

	    if (_typeof(palette[0]) == "object") {
	      for (var r = 0; r < palette.length; r++) {
	        var cellHeight = Math.floor(height / (palette.length - r));
	        height -= cellHeight;
	        var row = palette[r];
	        html += renderRow(row, widths, cellHeight);
	      }
	    } else {
	      html += renderRow(palette, widths, height);
	    }

	    html += "</div>";
	    this._viewobj.innerHTML = html;

	    function renderRow(row, widths, height) {
	      var rowHtml = "<div class=\"webix_color_row\" role=\"row\">";

	      for (var cell = 0; cell < row.length; cell++) {
	        data.width = widths[cell];
	        data.height = height;
	        data.val = row[cell];
	        rowHtml += itemTpl(data);
	      }

	      rowHtml += "</div>";
	      return rowHtml;
	    }

	    this._selectBox = null;
	    if (this._settings.value) this.$setValue(this._settings.value);else this._viewobj.lastChild.childNodes[0].childNodes[0].setAttribute("tabindex", "0");
	    this.callEvent("onAfterRender", []);
	  },
	  refresh: function () {
	    this.render();
	  }
	};
	var view$t = exports.protoUI(api$t, KeysNavigation, base.view, EventSystem);

	var api$u = {
	  name: "button",
	  touchable: true,
	  $skin: function () {
	    this.defaults.height = $active.buttonHeight || $active.inputHeight; //used in "text"

	    this._labelTopHeight = $active.labelTopHeight || 15;
	    this._borderWidth = $active.borderWidth;
	  },
	  defaults: {
	    template: function (obj, common) {
	      var text = common.$renderInput(obj, common);
	      if (obj.badge) text = text.replace("</button>", "<span class='webix_badge'>" + obj.badge + "</span></button>");
	      return "<div class='webix_el_box' style='width:" + obj.awidth + "px; height:" + obj.aheight + "px'>" + text + "</div>";
	    },
	    label: "",
	    borderless: true
	  },
	  $renderInput: function (obj) {
	    return "<button type='button' " + (obj.popup ? "aria-haspopup='true'" : "") + " class='webix_button'>" + template.escape(obj.label || obj.value) + "</button>";
	  },
	  $init: function (config) {
	    this._viewobj.className += " webix_control webix_el_" + (this.$cssName || this.name);

	    this._set_default_css(config);

	    this.data = this._settings;
	    this._dataobj = this._viewobj;
	    this.$ready.push(function () {
	      this._calc_size(this.config);
	    });
	  },
	  hotkey_setter: function (key) {
	    var control = this;

	    this._addElementHotKey(key, function (view, ev) {
	      if (control.isVisible()) {
	        var elem = control.$view.firstChild;
	        triggerEvent(elem, "MouseEvents", "click");
	        preventEvent(ev);
	      }
	    });
	  },
	  _set_default_css: function (config) {
	    if (config.type && this._deprecated_css_types[config.type]) this._viewobj.className += " " + this._deprecated_css_types[config.type];else if (!config.css || config.css && !this._classes[config.css]) this._viewobj.className += " webix_secondary";
	  },
	  _classes: {
	    webix_danger: 1,
	    webix_transparent: 1,
	    webix_primary: 1
	  },
	  // will be deprecated in 7.0
	  _deprecated_css_types: {
	    form: "webix_primary",
	    danger: "webix_danger"
	  },
	  _deprecated_types: {
	    imageButton: "image",
	    imageButtonTop: "imageTop",
	    iconButton: "icon",
	    iconButtonTop: "iconTop"
	  },
	  _addElementHotKey: function (key, func, view) {
	    var keyCode = UIManager.addHotKey(key, func, view);
	    this.attachEvent("onDestruct", function () {
	      UIManager.removeHotKey(keyCode, func, view);
	    });
	  },
	  type_setter: function (value) {
	    if (this._deprecated_types[value]) value = this._deprecated_types[value];
	    if (this._types[value]) this.$renderInput = template(this._types[value]);
	    return value;
	  },
	  _set_inner_size: false,
	  _types: {
	    htmlbutton: "<button type='button' class='webix_button'>#label#</button>",
	    image: "<button type='button' class='webix_button webix_img_btn' style='line-height:#cheight#px;'><img class='webix_image' style='max-width:#cheight#px; max-height:#cheight#px;' src = '#image#'>#label#</button>",
	    imageTop: "<button type='button' class='webix_button webix_img_btn_top'><div class='webix_image' style='width:100%;height:100%;background-image:url(#image#);'></div><div class='webix_img_btn_text'>#label#</div></button>",
	    icon: "<button type='button' class='webix_button webix_img_btn' style='line-height:#cheight#px;'><span class='webix_icon_btn #icon#' style='max-width:#cheight#px;'></span>#label#</button>",
	    iconTop: "<button type='button' class='webix_button webix_img_btn_top' style='width:100%;text-align:center;'><span class='webix_icon #icon#'></span><div class='webix_img_btn_text'>#label#</div></button>"
	  },
	  _findAllInputs: function () {
	    var result = [];
	    var tagNames = ["input", "select", "textarea", "button"];

	    for (var i = 0; i < tagNames.length; i++) {
	      var inputs = this.$view.getElementsByTagName(tagNames[i]);

	      for (var j = 0; j < inputs.length; j++) {
	        result.push(inputs[j]);
	      }
	    }

	    return result;
	  },
	  disable: function () {
	    var i,
	        node,
	        elem = this._getBox();

	    baseview.api.disable.apply(this, arguments);

	    if (elem && elem.className.indexOf(" webix_disabled_box") == -1) {
	      elem.className += " webix_disabled_box";

	      var inputs = this._findAllInputs();

	      for (i = 0; i < inputs.length; i++) {
	        inputs[i].setAttribute("disabled", true);
	      } // richselect and based on it


	      node = this.getInputNode();

	      if (node && node.tagName.toLowerCase() == "div") {
	        this._disabledTabIndex = node.getAttribute("tabIndex");
	        node.removeAttribute("tabIndex");
	      }

	      if (this._settings.labelPosition == "top") {
	        var label = this._dataobj.firstChild;
	        if (label) label.className += " webix_disabled_top_label";
	      }
	    }
	  },
	  enable: function () {
	    baseview.api.enable.apply(this, arguments);

	    var node,
	        elem = this._getBox();

	    if (elem) {
	      elem.className = elem.className.replace(" webix_disabled_box", "");

	      var inputs = this._findAllInputs();

	      for (var i = 0; i < inputs.length; i++) {
	        inputs[i].removeAttribute("disabled");
	      }

	      node = this.getInputNode();
	      if (node && !isUndefined(this._disabledTabIndex)) node.setAttribute("tabIndex", this._disabledTabIndex);

	      if (this._settings.labelPosition == "top") {
	        var label = this._dataobj.firstChild;
	        if (label) label.className = label.className.replace(" webix_disabled_top_label", "");
	      }
	    }
	  },
	  $setSize: function (x, y) {
	    if (base.api.$setSize.call(this, x, y)) {
	      this.render();
	    }
	  },
	  setValue: function (value) {
	    value = this.$prepareValue(value);
	    var oldvalue = this._settings.value;
	    if (this.$compareValue(oldvalue, value)) return false;
	    this._settings.value = value;
	    if (this._rendered_input) this.$setValue(value);
	    this.callEvent("onChange", [value, oldvalue]);
	  },
	  $compareValue: function (oldvalue, value) {
	    if (typeof value === "number") value = value.toString();
	    if (typeof oldvalue === "number") oldvalue = oldvalue.toString();
	    return oldvalue == value;
	  },
	  $prepareValue: function (value) {
	    return this._pattern(value, false);
	  },
	  _pattern: function (value) {
	    return value === 0 ? "0" : (value || "").toString();
	  },
	  //visual part of setValue
	  $setValue: function (value) {
	    //		this._settings.label = value;
	    var node = this.getInputNode();

	    if (node) {
	      if (node.tagName == "BUTTON") node.innerHTML = value;else node.value = value;
	    }
	  },
	  getValue: function () {
	    //if button was rendered - returning actual value
	    //otherwise - returning last set value
	    var value = this._rendered_input ? this.$getValue() : this._settings.value;
	    return typeof value == "undefined" ? "" : value;
	  },
	  $getValue: function () {
	    return this._settings.value || "";
	  },
	  focus: function () {
	    if (!UIManager.canFocus(this)) return false;
	    var input = this.getInputNode();
	    if (input && input.focus) input.focus();
	  },
	  blur: function () {
	    var input = this.getInputNode();
	    if (input && input.blur) input.blur();
	  },
	  //get input element
	  getInputNode: function () {
	    return this._dataobj.getElementsByTagName("input")[0] || this._dataobj.getElementsByTagName("button")[0];
	  },
	  //get top-level sub-container
	  _getBox: function () {
	    for (var i = 0; i < this._dataobj.childNodes.length; i++) {
	      if (this._dataobj.childNodes[i].className.indexOf("webix_el_box") >= 0) return this._dataobj.childNodes[i];
	    }

	    return null;
	  },
	  _get_tooltip_data: function (t, e) {
	    var node = e.target || e.srcElement;

	    var box = this._getBox();

	    if (box && box.contains(node)) return this._settings;
	    return null;
	  },
	  _sqrt_2: Math.sqrt(2),
	  _calc_size: function (config) {
	    config = config || this._settings;
	    if (config.autowidth) config.width = getTextSize(config.value || config.label || "", "webixbutton").width + (config.badge ? 16 : 0) + (config.type === "iconButton" || config.type === "icon" ? 24 : 0) + (config.type === "imageButton" || config.type === "image" ? config.height - $active.inputPadding : 0);
	  },
	  _calck_input_size: function () {
	    //use width for both width and inputWidth settings in clever way
	    //in form, we can define width for some element smaller than for siblings
	    //it will use inputWidth to render the desired view
	    this._input_width = this._settings.inputWidth || (this._content_width - this._settings.width > 2 ? this._settings.width : 0) || this._content_width;
	    this._input_height = this._settings.inputHeight || this._inputHeight || 0;
	  },
	  resize: function () {
	    this._calc_size();

	    return base.api.resize.apply(this, arguments);
	  },
	  render: function () {
	    this._calck_input_size();

	    this._settings.awidth = this._input_width || this._content_width;
	    this._settings.aheight = this._input_height || this._content_height; //image button - image width

	    this._settings.bheight = this._settings.aheight + 2;
	    this._settings.cheight = this._settings.aheight - 2 * $active.inputPadding;
	    this._settings.dheight = this._settings.cheight - 2; // - borders

	    if (AtomRender.render.call(this)) {
	      this._rendered_input = true;
	      if (this._set_inner_size) this._set_inner_size();

	      if (this._settings.align) {
	        var handle = this._dataobj.firstChild;
	        if (this._settings.labelPosition == "top" && handle.nextSibling) handle = handle.nextSibling;

	        switch (this._settings.align) {
	          case "right":
	            handle.style.cssFloat = "right";
	            handle.style.textAlign = "right";
	            break;

	          case "center":
	            handle.style.display = "inline-block";
	            handle.parentNode.style.textAlign = "center";
	            break;

	          case "middle":
	            handle.style.marginTop = Math.round((this._content_height - this._input_height) / 2) + "px";
	            break;

	          case "bottom":
	            handle.style.marginTop = this._content_height - this._input_height + "px";
	            break;

	          case "left":
	            handle.style.cssFloat = "left";
	            break;

	          default:
	            assert(false, "Unknown align mode: " + this._settings.align);
	            break;
	        }
	      }

	      if (this.$render) this.$render(this.data);
	      if (this._settings.disabled) this.disable();

	      if (this._init_once) {
	        this._init_once(this.data);

	        this._init_once = 0;
	      }
	    }
	  },
	  refresh: function () {
	    this.render();
	  },
	  on_click: {
	    _handle_tab_click: function (ev) {
	      var id = locate(ev,
	      /*@attr*/
	      "button_id");

	      if (id && this.callEvent("onBeforeTabClick", [id, ev])) {
	        this.setValue(id);
	        this.callEvent("onAfterTabClick", [id, ev]);
	      }
	    },
	    webix_all_segments: function (ev, button) {
	      this.on_click._handle_tab_click.call(this, ev, button);
	    },
	    webix_all_tabs: function (ev, button) {
	      this.on_click._handle_tab_click.call(this, ev, button);
	    },
	    webix_inp_counter_next: function () {
	      if (!this._settings.readonly) this.next();
	    },
	    webix_inp_counter_prev: function () {
	      if (!this._settings.readonly) this.prev();
	    },
	    webix_input_icon: function () {
	      this.getInputNode().focus();
	    },
	    webix_inp_checkbox_border: function (e) {
	      if (!this._settings.disabled && (e.target || e.srcElement).tagName != "DIV" && !this._settings.readonly) this.toggle();
	    },
	    webix_inp_checkbox_label: function () {
	      if (!this._settings.readonly) this.toggle();
	    },
	    webix_inp_radio_border: function (e) {
	      var value = locate(e,
	      /*@attr*/
	      "radio_id");
	      this.setValue(value);
	      this.focus();
	    },
	    webix_inp_radio_label: function (e, obj, node) {
	      node = node.parentNode.getElementsByTagName("input")[0];
	      return this.on_click.webix_inp_radio_border.call(this, node, obj, node);
	    },
	    webix_tab_more_icon: function (ev, obj, node) {
	      this.getPopup().resize();
	      this.getPopup().show(node, null, true);
	    },
	    webix_tab_close: function (ev) {
	      var id = locate(ev,
	      /*@attr*/
	      "button_id");
	      if (id && this.callEvent("onBeforeTabClose", [id, ev])) this.removeOption(id);
	    }
	  },
	  //method do not used by button, but  used by other child-views
	  _check_options: function (opts) {
	    assert(opts, this.name + ": options not defined");

	    for (var i = 0; i < opts.length; i++) {
	      // asserts need to be removed in final version			
	      assert(!opts[i].text, "Please replace .text with .value in control config");
	      assert(!opts[i].label, "Please replace .label with .value in control config");

	      if (typeof opts[i] == "string") {
	        opts[i] = {
	          id: opts[i],
	          value: opts[i]
	        };
	      } else {
	        if (isUndefined(opts[i].id)) opts[i].id = opts[i].value;
	        if (isUndefined(opts[i].value)) opts[i].value = opts[i].id;
	      }
	    }

	    return opts;
	  },
	  _get_div_placeholder: function (obj) {
	    var placeholder = obj ? obj.placeholder : this._settings.placeholder;
	    return placeholder ? "<span class='webix_placeholder'>" + placeholder + "</span>" : "";
	  }
	};
	var view$u = exports.protoUI(api$u, base.view, AutoTooltip, AtomRender, Settings, EventSystem);
	var button$1 = {
	  api: api$u,
	  view: view$u
	};

	var api$v = {
	  name: "label",
	  defaults: {
	    template: "<div class='webix_el_box' style='width:#awidth#px;height:#aheight#px;line-height:#cheight#px'>#label#</div>"
	  },
	  $skin: function () {
	    this.defaults.height = $active.inputHeight;
	  },
	  focus: function () {
	    return false;
	  },
	  _getBox: function () {
	    return this._dataobj.firstChild;
	  },
	  setHTML: function (html) {
	    this._settings.label = html;
	    this.refresh();
	  },
	  setValue: function (value) {
	    this._settings.label = value;
	    button$1.api.setValue.apply(this, arguments);
	  },
	  $setValue: function (value) {
	    this._dataobj.firstChild.innerHTML = value;
	  },
	  _set_inner_size: false,
	  _set_default_css: function () {},
	  _calc_size: function (config) {
	    config = config || this._settings;
	    if (config.autowidth) config.width = getTextSize(config.value || config.label, "webix_el_box").width;
	  }
	};
	var view$v = exports.protoUI(api$v, button$1.view);

	var controls = {};

	for (var i in UIManager._controls) {
	  controls[UIManager._controls[i]] = i;
	}

	var nav_controls = {
	  9: "tab",
	  38: "up",
	  40: "down",
	  37: "left",
	  39: "right"
	};
	var TextPattern = {
	  $init: function (config) {
	    var pattern = this.defaults.pattern || config.pattern;
	    var format = this.defaults.format || config.format;
	    config.value = isUndefined(config.value) ? "" : config.value;

	    if (pattern || format && !this.format_setter) {
	      this.attachEvent("onKeyPress", function (code, e) {
	        if (e.ctrlKey || e.altKey || this._custom_format) return;
	        if (code > 105 && code < 112) //numpad operators
	          code -= 64;

	        if (controls[code] && code !== 8 && code !== 46) {
	          //del && bsp
	          if (!nav_controls[code]) preventEvent(e);
	          return;
	        }

	        preventEvent(e);

	        this._on_key_pressed(e, code);
	      });
	      this.attachEvent("onAfterRender", this._after_render);

	      this.getText = function () {
	        return this.getInputNode().value;
	      };

	      this._pattern = function (value, mode) {
	        if (mode === false) return this._getRawValue(value);else return this._matchPattern(value);
	      };

	      if (format) {
	        if (_typeof(format) === "object") {
	          this._custom_format = format;
	        } else {
	          format = Number$1.getConfig(format);
	          this._custom_format = {
	            parse: function (value) {
	              return Number$1.parse(value, format);
	            },
	            edit: function (value) {
	              return Number$1.format(value, format);
	            }
	          };
	        }
	      }
	    }
	  },
	  pattern_setter: function (value) {
	    var pattern = patterns[value] || value;
	    if (typeof pattern == "string") pattern = {
	      mask: pattern
	    };
	    pattern.allow = pattern.allow || /[A-Za-z0-9]/g;

	    this._patternScheme(pattern);

	    return pattern;
	  },
	  _init_validation: function () {
	    this.config.validate = this.config.validate || bind(function () {
	      var value = this.getText();
	      var raw = value.replace(this._pattern_chars, "");
	      var matches = (value.toString().match(this._pattern_allows) || []).join("");
	      return matches.length == raw.length && value.length == this._settings.pattern.mask.length;
	    }, this);
	  },
	  _after_render: function () {
	    var ev = env.isIE8 ? "propertychange" : "input";
	    if (!this._custom_format) _event(this.getInputNode(), ev, function () {
	      var stamp = new Date().valueOf(); //dark ie8 magic

	      var width = this.$view.offsetWidth; //eslint-disable-line

	      if (!this._property_stamp || stamp - this._property_stamp > 100) {
	        this._property_stamp = stamp;
	        this.$setValue(this.getText());
	      }
	    }, {
	      bind: this
	    });

	    _event(this.getInputNode(), "blur", function () {
	      this._applyChanges();
	    }, {
	      bind: this
	    });
	  },
	  _patternScheme: function (pattern) {
	    var mask = pattern.mask,
	        scheme = {},
	        chars = "",
	        count = 0;

	    for (var i = 0; i < mask.length; i++) {
	      if (mask[i] === "#") {
	        scheme[i] = count;
	        count++;
	      } else {
	        scheme[i] = false;
	        if (chars.indexOf(mask[i]) === -1) chars += "\\" + mask[i];
	      }
	    }

	    this._pattern_allows = pattern.allow;
	    this._pattern_chars = new RegExp("[" + chars + "]", "g");
	    this._pattern_scheme = scheme;

	    this._init_validation();
	  },
	  _on_key_pressed: function (e, code) {
	    var node = this.getInputNode();
	    var value = node.value;
	    var pos$$1 = getSelectionRange(node);
	    var chr = "";

	    if (code == 8 || code == 46) {
	      if (pos$$1.start == pos$$1.end) {
	        if (code == 8) pos$$1.start--;else pos$$1.end++;
	      }
	    } else {
	      chr = String.fromCharCode(code);
	      if (!e.shiftKey) chr = chr.toLowerCase();
	    }

	    value = value.substr(0, pos$$1.start) + chr + value.substr(pos$$1.end);
	    pos$$1 = this._getCaretPos(chr, value.length, pos$$1.start, code);
	    this._input_code = code;
	    this.$setValue(value);
	    setSelectionRange(node, pos$$1);
	  },
	  _getCaretPos: function (chr, len, pos$$1, code) {
	    if (chr && chr.match(this._pattern_allows) || code == 8 || code == 46) {
	      pos$$1 = chr ? pos$$1 + 1 : pos$$1;
	      pos$$1 = this._fixCaretPos(pos$$1, code);
	    } else if (len - 1 == pos$$1 && code !== 8 && code !== 46) {
	      var rest = this._settings.pattern.mask.indexOf("#", pos$$1);

	      if (rest > 0) pos$$1 += rest;
	    }

	    return pos$$1;
	  },
	  _fixCaretPos: function (pos$$1, code) {
	    var prev = pos$$1 - (code !== 46) * 1;

	    if (this._pattern_scheme[prev] === false) {
	      pos$$1 = pos$$1 + (code == 8 ? -1 : 1);
	      return this._fixCaretPos(pos$$1, code);
	    }

	    if (this._pattern_scheme[pos$$1] === false && code !== 8) return this._fixCaretPos(pos$$1 + 1, code) - 1;
	    return pos$$1;
	  },
	  _getRawValue: function (value) {
	    if (this._custom_format) return this._custom_format.parse(value);
	    value = value || value === 0 ? value : "";
	    var matches = value.toString().match(this._pattern_allows) || [];
	    return matches.join("").replace(this._pattern_chars, "");
	  },
	  _matchPattern: function (value) {
	    if (this._custom_format) return this._custom_format.edit(this._custom_format.parse(value));

	    var raw = this._getRawValue(value),
	        pattern = this._settings.pattern.mask,
	        mask = this._settings.pattern.mask,
	        scheme = this._pattern_scheme,
	        end = false,
	        index$$1 = 0,
	        rawIndex = 0,
	        rawLength = 0;

	    for (var i in scheme) {
	      if (scheme[i] !== false) {
	        if (!end) {
	          index$$1 = i * 1;
	          rawIndex = scheme[i];
	          var rchar = raw[rawIndex] || "";
	          var next = raw[rawIndex + 1];
	          pattern = (rchar ? pattern.substr(0, index$$1) : "") + rchar + (rchar && next ? pattern.substr(index$$1 + 1) : "");
	          if (!next) end = true;
	        }

	        rawLength++;
	      }
	    } //finalize value with subsequent mask chars 


	    var icode = this._input_code;

	    if (icode && icode !== 8 || !icode && rawLength - 1 === rawIndex && pattern.length < mask.length) {
	      if (raw) {
	        var nind = index$$1 + 1;

	        if (mask.charAt(nind) !== "#" && pattern.length < mask.length) {
	          var lind = mask.indexOf("#", nind);
	          if (lind < 0) lind = mask.length;
	          pattern += mask.substr(nind, lind - nind);
	        }
	      } else if (icode !== 46) {
	        pattern += mask.substr(0, mask.indexOf("#"));
	      }
	    }

	    this._input_code = null;
	    return pattern;
	  }
	};

	var api$w = {
	  name: "text",
	  $allowsClear: true,
	  _init_onchange: function () {
	    if (this.$allowsClear) {
	      //attach onChange handler only for controls which do not manage blur on their own
	      //for example - combo
	      if (!this._onBlur) _event(this.getInputNode(), "change", this._applyChanges, {
	        bind: this
	      });
	      if (this._settings.suggest) $$(this._settings.suggest).linkInput(this);
	    }
	  },
	  _applyChanges: function () {
	    var newvalue = this.getValue();
	    var res = this.setValue(newvalue, true); //controls with post formating, we need to repaint value

	    if (this._custom_format && res === false) {
	      this.$setValue(newvalue);
	    }
	  },
	  $skin: function () {
	    this.defaults.height = $active.inputHeight;
	    this.defaults.inputPadding = $active.inputPadding;
	    this._inputSpacing = $active.inputSpacing;
	  },
	  $init: function (config) {
	    if (config.labelPosition == "top") if (isUndefined(config.height) && this.defaults.height) // textarea
	      config.height = this.defaults.height + (config.label ? this._labelTopHeight : 0); //suggest reference for destructor

	    this._destroy_with_me = [];
	    this.attachEvent("onAfterRender", this._init_onchange);
	    this.attachEvent("onBlur", function () {
	      if (this._onBlur) this._onBlur();
	    });
	  },
	  $renderIcon: function () {
	    var config = this._settings;

	    if (config.icon) {
	      var height = config.aheight - 2 * config.inputPadding,
	          padding = (height - 18) / 2 - 1,
	          aria = this.addSection ? "role='button' tabindex='0' aria-label='" + i18n.aria["multitext" + (config.mode || "") + "Section"] + "'" : "";
	      return "<span style='height:" + (height - padding) + "px;padding-top:" + padding + "px;' class='webix_input_icon " + config.icon + "' " + aria + "></span>";
	    }

	    return "";
	  },
	  relatedView_setter: function (value) {
	    this.attachEvent("onChange", function () {
	      var value = this.getValue();
	      var mode = this._settings.relatedAction;
	      var viewid = this._settings.relatedView;
	      var view = $$(viewid);

	      if (!view) {
	        var top = this.getTopParentView();
	        if (top && top.$$) view = top.$$(viewid);
	      }

	      assert(view, "Invalid relatedView: " + viewid);

	      if (mode == "enable") {
	        if (value) view.enable();else view.disable();
	      } else {
	        if (value) view.show();else view.hide();
	      }
	    });
	    return value;
	  },
	  validateEvent_setter: function (value) {
	    if (value == "blur") this.attachEvent("onBlur", this.validate);
	    if (value == "key") this.attachEvent("onTimedKeyPress", this.validate);
	    return value;
	  },
	  validate: function () {
	    var rule = this._settings.validate;
	    if (!rule && this._settings.required) rule = rules.isNotEmpty;
	    var form = this.getFormView();
	    var name = this._settings.name;
	    var value = this.getValue();
	    var data = {};
	    data[name] = value;
	    assert(form, "Validation works only for fields in the form");
	    assert(name, "Validation works only for fields with name");
	    if (rule && !form._validate(rule, value, data, name)) return false;
	    return true;
	  },
	  bottomLabel_setter: function (value) {
	    if (!this._settings.bottomPadding) this._settings.bottomPadding = 18;
	    return value;
	  },
	  _getInvalidText: function () {
	    var text = this._settings.invalidMessage;

	    if (typeof text == "function") {
	      text.call(this);
	    }

	    return text;
	  },
	  setBottomText: function (text, height) {
	    var config = this._settings;

	    if (typeof text != "undefined") {
	      if (config.bottomLabel == text) return;
	      config.bottomLabel = text;
	    }

	    var message = (config.invalid ? config.invalidMessage : "") || config.bottomLabel;
	    if (!message && !config.bottomPadding) config.inputHeight = 0;

	    if (message && !config.bottomPadding) {
	      this._restorePadding = 1;
	      config.bottomPadding = config.bottomPadding || height || 18;
	      this.render();
	      this.resize();
	    } else if (!message && this._restorePadding) {
	      config.bottomPadding = this._restorePadding = 0; //textarea

	      if (!config.height) this.render();
	      this.resize();
	    } else this.render();
	  },
	  $getSize: function () {
	    var sizes = base.api.$getSize.apply(this, arguments);
	    var heightInc = this.config.bottomPadding;

	    if (heightInc) {
	      sizes[2] += heightInc;
	      sizes[3] += heightInc;
	    }

	    return sizes;
	  },
	  $setSize: function (x, y) {
	    var config = this._settings;

	    if (base.api.$setSize.call(this, x, y)) {
	      if (!x || !y) return;

	      if (config.labelPosition == "top") {
	        // textarea
	        if (!config.inputHeight) this._inputHeight = this._content_height - (config.label ? this._labelTopHeight : 0) - (this.config.bottomPadding || 0);
	        config.labelWidth = 0;
	      } else if (config.bottomPadding) {
	        config.inputHeight = this._content_height - this.config.bottomPadding;
	      }

	      this.render();
	    }
	  },
	  _get_input_width: function (config) {
	    var width = (this._input_width || 0) - (config.label ? this._settings.labelWidth : 0) - this._inputSpacing - (config.iconWidth || 0); //prevent js error in IE

	    return width < 0 ? 0 : width;
	  },
	  _render_div_block: function (obj, common) {
	    var id = "x" + uid();

	    var width = common._get_input_width(obj);

	    var inputAlign = obj.inputAlign || "left";
	    var height = this._settings.aheight - 2 * $active.inputPadding - 2 * this._borderWidth;

	    var text = obj.text || obj.value || this._get_div_placeholder(obj);

	    var html = "<div class='webix_inp_static' role='combobox' aria-label='" + template.escape(obj.label) + "' tabindex='0'" + (obj.readonly ? " aria-readonly='true'" : "") + (obj.invalid ? "aria-invalid='true'" : "") + " onclick='' style='line-height:" + height + "px;width: " + width + "px; text-align: " + inputAlign + ";' >" + text + "</div>";
	    return common.$renderInput(obj, html, id);
	  },
	  _baseInputHTML: function (tag) {
	    var html = "<" + tag + (this._settings.placeholder ? " placeholder='" + this._settings.placeholder + "' " : " ");
	    if (this._settings.readonly) html += "readonly='true' aria-readonly=''";
	    if (this._settings.required) html += "aria-required='true'";
	    if (this._settings.invalid) html += "aria-invalid='true'";
	    var attrs = this._settings.attributes;
	    if (attrs) for (var prop in attrs) {
	      html += prop + "='" + attrs[prop] + "' ";
	    }
	    return html;
	  },
	  $renderLabel: function (config, id) {
	    var label = "";

	    if (config.label) {
	      var labelAlign = config.labelAlign || "left";
	      var top = this._settings.labelPosition == "top";
	      var labelTop = top ? "display:block;" : "width: " + this._settings.labelWidth + "px;";

	      var labelHeight = this._getLabelHeight(top);

	      label = "<label style='" + labelTop + "text-align: " + labelAlign + ";line-height:" + labelHeight + "px;' onclick='' for='" + id + "' class='webix_inp_" + (top ? "top_" : "") + "label " + (config.required ? "webix_required" : "") + "'>" + (config.label || "") + "</label>";
	    }

	    return label;
	  },
	  _getLabelHeight: function (top) {
	    return top ? this._labelTopHeight - this._settings.inputPadding : this._settings.aheight - 2 * this._settings.inputPadding;
	  },
	  $renderInput: function (config, div_start, id) {
	    var inputAlign = config.inputAlign || "left";
	    var top = config.labelPosition == "top";

	    var inputWidth = this._get_input_width(config);

	    id = id || uid();
	    var label = this.$renderLabel(config, id);
	    var html = "";

	    if (div_start) {
	      html += div_start;
	    } else {
	      var value = template.escape(config.text || this._pattern(config.value));
	      html += this._baseInputHTML("input") + "id='" + id + "' type='" + (config.type || this.name) + "'" + (config.editable ? " role='combobox'" : "") + " value='" + value + "' style='width: " + inputWidth + "px; text-align: " + inputAlign + ";'";
	      var attrs = config.attributes;
	      if (attrs) for (var prop in attrs) {
	        html += " " + prop + "='" + attrs[prop] + "'";
	      }
	      html += " />";
	    }

	    var icon = this.$renderIcon ? this.$renderIcon(config) : "";
	    html += icon;
	    var result = ""; //label position, top or left

	    if (top) result = label + "<div class='webix_el_box' style='width:" + config.awidth + "px; height:" + config.aheight + "px'>" + html + "</div>";else result = "<div class='webix_el_box' style='width:" + config.awidth + "px; height:" + config.aheight + "px'>" + label + html + "</div>"; //bottom message width

	    var padding = config.awidth - inputWidth - $active.inputPadding * 2; //bottom message text

	    var message = (config.invalid ? config.invalidMessage : "") || config.bottomLabel;
	    if (message) result += "<div class='webix_inp_bottom_label'" + (config.invalid ? "role='alert' aria-relevant='all'" : "") + " style='width:" + (inputWidth || config.awidth) + "px;margin-left:" + Math.max(padding, $active.inputPadding) + "px;'>" + message + "</div>";
	    return result;
	  },
	  defaults: {
	    template: function (obj, common) {
	      return common.$renderInput(obj);
	    },
	    label: "",
	    labelWidth: 80
	  },
	  type_setter: function (value) {
	    return value;
	  },
	  _set_inner_size: false,
	  _set_default_css: function () {},
	  $setValue: function (value) {
	    this.getInputNode().value = this._pattern(value);
	  },
	  $getValue: function () {
	    return this._pattern(this.getInputNode().value, false);
	  },
	  suggest_setter: function (value) {
	    if (value) {
	      assert(value !== true, "suggest options can't be set as true, data need to be provided instead");

	      if (typeof value == "string") {
	        var attempt = $$(value);
	        if (attempt) return $$(value)._settings.id;
	        value = {
	          body: {
	            url: value,
	            dataFeed: value
	          }
	        };
	      } else if (value.getItem) value = {
	        body: {
	          data: value
	        }
	      };else if (isArray(value)) value = {
	        body: {
	          data: this._check_options(value)
	        }
	      };else if (!value.body) value.body = {};

	      exports.extend(value, {
	        view: "suggest"
	      });
	      var view = ui(value);

	      this._destroy_with_me.push(view);

	      return view._settings.id;
	    }

	    return false;
	  }
	};
	var view$w = exports.protoUI(api$w, TextPattern, button$1.view);
	var text = {
	  api: api$w,
	  view: view$w
	};

	var DataCollection = exports.proto({
	  name: "DataCollection",
	  isVisible: function () {
	    if (!this.data.order.length && !this.data._filter_order && !this._settings.dataFeed) return false;
	    return true;
	  },
	  $init: function (config) {
	    this.data.provideApi(this, true);
	    var id = config && config.id ? config.id : uid();
	    this._settings.id = id;
	    ui.views[id] = this;
	    this.data.attachEvent("onStoreLoad", bind(function () {
	      this.callEvent("onBindRequest", []);
	    }, this));
	  },
	  refresh: function () {
	    this.callEvent("onBindRequest", []);
	  }
	}, DataMove, CollectionBind, BindSource, ValidateCollection, DataLoader, MapCollection, EventSystem, BaseBind, Destruction, Settings);
	define("DataCollection", DataCollection);

	var api$x = {
	  name: "select",
	  defaults: {
	    template: function (obj, common) {
	      var options = common._check_options(obj.options);

	      var id = "x" + uid();
	      var html = common._baseInputHTML("select") + "id='" + id + "' style='width:" + common._get_input_width(obj) + "px;'>";
	      var optview = $$(options);

	      if (optview && optview.data && optview.data.each) {
	        optview.data.each(function (option) {
	          html += "<option" + (option.id == obj.value ? " selected='true'" : "") + " value='" + option.id + "'>" + option.value + "</option>";
	        });
	      } else for (var i = 0; i < options.length; i++) {
	        html += "<option" + (options[i].id == obj.value ? " selected='true'" : "") + " value='" + options[i].id + "'>" + options[i].value + "</option>";
	      }

	      html += "</select>";
	      return common.$renderInput(obj, html, id);
	    }
	  },
	  options_setter: function (value) {
	    if (value) {
	      if (typeof value == "string") {
	        var collection = new DataCollection({
	          url: value
	        });
	        collection.data.attachEvent("onStoreLoad", bind(this.refresh, this));
	        return collection;
	      } else return value;
	    }
	  },
	  //get input element
	  getInputNode: function () {
	    return this._dataobj.getElementsByTagName("select")[0];
	  }
	};
	var view$x = exports.protoUI(api$x, text.view);

	var api$y = {
	  name: "checkbox",
	  defaults: {
	    checkValue: 1,
	    uncheckValue: 0,
	    template: function (config, common) {
	      var id = "x" + uid();
	      var rightlabel = "";

	      if (config.labelRight) {
	        rightlabel = "<label class='webix_label_right'>" + config.labelRight + "</label>"; //user clearly attempts to hide the label, help him

	        if (config.labelWidth) config.label = config.label || "&nbsp;";
	      }

	      var checked = config.checkValue == config.value;
	      var margin = Math.floor((common._settings.aheight - 16) / 2);
	      var ch = common._baseInputHTML("input") + "style='margin-top:" + margin + "px;" + (config.customCheckbox ? "display:none" : "") + "' id='" + id + "' type='checkbox' " + (checked ? "checked='1'" : "") + (config.labelRight ? " aria-label='" + template.escape(config.labelRight) + "'" : "") + "/>";
	      var className = "webix_inp_checkbox_border webix_el_group webix_checkbox_" + (checked ? "1" : "0");
	      var customCheckbox = config.customCheckbox || "";

	      if (customCheckbox) {
	        customCheckbox = customCheckbox.replace(/(aria-checked=')\w*(?=')/, "$1" + (config.value == config.checkValue ? "true" : "false"));
	        customCheckbox = customCheckbox.replace(/(aria-label=')\w*(?=')/, "$1" + template.escape(config.labelRight || config.label));
	        customCheckbox = customCheckbox.replace(/(aria-invalid=')\w*(?=')/, "$1" + (config.invalid ? "true" : "false"));
	      }

	      var html = "<div style='line-height:" + common._settings.cheight + "px' class='" + className + "'>" + ch + customCheckbox + rightlabel + "</div>";
	      return common.$renderInput(config, html, id);
	    }
	  },
	  customCheckbox_setter: function (value) {
	    if (value === true && $active.customCheckbox) {
	      value = "<a role='presentation' onclick='javascript:void(0)'><button role='checkbox' aria-checked='false' aria-label='' type='button' aria-invalid='' class='webix_custom_checkbox'></button></a>";
	    }

	    return value;
	  },
	  blur: function () {
	    var input = this.getInputNode();
	    if (input) input.blur();
	  },
	  _pattern: function (value) {
	    return value;
	  },
	  _init_onchange: function () {},
	  $setValue: function (value) {
	    var isChecked = value == this._settings.checkValue;
	    var input = this.$view.getElementsByTagName("input")[0];
	    var parentNode = input ? input.parentNode : null;

	    if (parentNode && this._settings.customCheckbox) {
	      var button = parentNode.getElementsByTagName("BUTTON");
	      if (button[0]) button[0].setAttribute("aria-checked", isChecked ? "true" : "false");
	    }

	    if (parentNode) {
	      parentNode.className = parentNode.className.replace(/(webix_checkbox_)\d/, "$1" + (isChecked ? 1 : 0));
	    }

	    input.checked = isChecked;
	  },
	  toggle: function () {
	    var value = this.getValue() != this._settings.checkValue ? this._settings.checkValue : this._settings.uncheckValue;
	    this.setValue(value);
	  },
	  getValue: function () {
	    var value = this._settings.value;
	    return value == this._settings.checkValue ? this._settings.checkValue : this._settings.uncheckValue;
	  },
	  getInputNode: function () {
	    return this.$view.getElementsByTagName(this._settings.customCheckbox ? "button" : "input")[0];
	  },
	  $skin: function () {
	    this.defaults.customCheckbox = !!$active.customCheckbox;
	  }
	};
	var view$y = exports.protoUI(api$y, text.view);
	var checkbox = {
	  api: api$y,
	  view: view$y
	};

	var api$z = {
	  name: "radio",
	  defaults: {
	    template: function (config, common) {
	      var options = common._check_options(config.options);

	      var html = [];
	      var id;

	      for (var i = 0; i < options.length; i++) {
	        var eachid = "x" + uid();
	        id = id || eachid;
	        if (i && (options[i].newline || config.vertical)) html.push("<div class='webix_line_break'></div>");
	        var isChecked = options[i].id == config.value;
	        var label = options[i].value || "";
	        var tooltip = config.tooltip ? " webix_t_id='" + options[i].id + "'" : "";
	        var customRadio = config.customRadio || "";

	        if (customRadio) {
	          var optlabel = (i === 0 ? config.label + " " : "") + label;
	          customRadio = customRadio.replace(/(aria-label=')\w*(?=')/, "$1" + template.escape(optlabel));
	          customRadio = customRadio.replace(/(aria-checked=')\w*(?=')/, "$1" + (isChecked ? "true" : "false"));
	          customRadio = customRadio.replace(/(tabindex=')\w*(?=')/, "$1" + (isChecked || i === 0 && !config.value ? "0" : "-1"));
	          customRadio = customRadio.replace(/(aria-invalid=')\w*(?=')/, "$1" + (config.invalid ? "true" : "false"));
	          customRadio = customRadio.replace(/(button_id=')\w*(?=')/, "$1" + options[i].id);
	        }

	        var rd = common._baseInputHTML("input") + " name='" + (config.name || config.id) + "' type='radio' " + (isChecked ? "checked='1'" : "") + "tabindex=" + (isChecked || i === 0 && !config.value ? "0" : "-1") + " value='" + options[i].id + "' id='" + eachid + "' style='" + (customRadio ? "display:none" : "") + "' />";
	        var input = "<div " +
	        /*@attr*/
	        "radio_id" + "='" + options[i].id + "' class='webix_inp_radio_border webix_radio_" + (isChecked ? "1" : "0") + "' role='presentation'>" + rd + customRadio + "</div>";
	        if (label) label = "<label for='" + eachid + "' class='webix_label_right'>" + label + "</label>";
	        html.push("<div class='webix_radio_option' role='presentation'" + tooltip + ">" + input + label + "</div>");
	      }

	      html = "<div class='webix_el_group' role='radiogroup' style='margin-left:" + (config.label ? config.labelWidth : 0) + "px;'>" + html.join("") + "</div>";
	      return common.$renderInput(config, html, id);
	    }
	  },
	  refresh: function () {
	    this.render();
	    if (this._last_size && this.$getSize(0, 0)[2] != this._last_size[1]) this.resize();
	  },
	  $getSize: function (dx, dy) {
	    var size = button$1.api.$getSize.call(this, dx, dy);

	    if (this._settings.options) {
	      var count = this._settings.vertical ? 0 : 1;

	      for (var i = 0; i < this._settings.options.length; i++) {
	        if (this._settings.vertical || this._settings.options[i].newline) count++;
	      }

	      size[3] = size[2] = Math.max(size[2], (this._settings.optionHeight || 25) * count + this._settings.inputPadding * 2 + (this._settings.labelPosition == "top" ? this._labelTopHeight : 0));
	    }

	    var heightInc = this.config.bottomPadding;

	    if (heightInc) {
	      size[2] += heightInc;
	      size[3] += heightInc;
	    }

	    return size;
	  },
	  _getInputNode: function () {
	    return this._dataobj.getElementsByTagName(this._settings.customRadio ? "button" : "input");
	  },
	  $setValue: function (value) {
	    var inp = this._dataobj.getElementsByTagName("input");

	    for (var i = 0; i < inp.length; i++) {
	      if (inp[i].parentNode.getAttribute(
	      /*@attr*/
	      "radio_id") == value) {
	        inp[i].checked = true;
	        inp[i].setAttribute("tabindex", "0");
	      } else {
	        inp[i].checked = false;
	        inp[i].setAttribute("tabindex", "-1");
	      }

	      var parentNode = inp[i] ? inp[i].parentNode : null;

	      if (parentNode) {
	        parentNode.className = parentNode.className.replace(/(webix_radio_)\d/, "$1" + (inp[i].checked ? 1 : 0));

	        if (this._settings.customRadio) {
	          var button = parentNode.getElementsByTagName("BUTTON");

	          if (button[0]) {
	            button[0].setAttribute("aria-checked", inp[i].checked ? "true" : "false");
	            button[0].setAttribute("tabindex", inp[i].checked ? "0" : "-1");
	          }
	        }
	      }
	    }
	  },
	  getValue: function () {
	    return this._settings.value;
	  },
	  focus: function () {
	    return this._focus();
	  },
	  blur: function () {
	    this._blur();
	  },
	  customRadio_setter: function (value) {
	    if (value === true && $active.customRadio) value = "<a role='presentation' onclick='javascript:void(0)'><button type='button' class='webix_custom_radio' " +
	    /*@attr*/
	    "button_id" + "='' role='radio' aria-checked='false' aria-label='' aria-invalid='' tabindex=''></button></a>";
	    return value;
	  },
	  $skin: function () {
	    this.defaults.customRadio = !!$active.customRadio;
	    if ($active.optionHeight) this.defaults.optionHeight = $active.optionHeight;
	  }
	};
	var view$z = exports.protoUI(api$z, text.view, HTMLOptions);

	var api$A = {
	  name: "datepicker",
	  $init: function () {
	    this.$ready.push(this._init_popup);
	  },
	  defaults: {
	    template: function (obj, common) {
	      if (common._settings.type == "time") {
	        common._settings.icon = common._settings.timeIcon;
	      } //temporary remove obj.type [[DIRTY]]


	      var t = obj.type;
	      obj.type = "";
	      var res = obj.editable ? common.$renderInput(obj) : common._render_div_block(obj, common);
	      obj.type = t;
	      return res;
	    },
	    stringResult: false,
	    timepicker: false,
	    icon: "wxi-calendar",
	    icons: true,
	    timeIcon: "wxi-clock",
	    separator: ", "
	  },
	  _onBlur: function () {
	    if (this._settings.text == this.getText() || isUndefined(this._settings.text) && !this.getText()) return;
	    var value = this.getPopup().getValue();
	    if (value) this.setValue(value);
	  },
	  $skin: function () {
	    this.defaults.inputPadding = $active.inputPadding;
	    this.defaults.point = !$active.popupNoPoint;
	  },
	  getPopup: function () {
	    return $$(this._settings.popup);
	  },
	  _init_popup: function () {
	    var obj = this._settings;
	    if (obj.suggest) obj.popup = obj.suggest;else if (!obj.popup) {
	      var timepicker = this._settings.timepicker;
	      obj.popup = obj.suggest = this.suggest_setter({
	        type: "calendar",
	        point: this._settings.point === false ? false : true,
	        padding: 0,
	        body: {
	          height: 240 + (timepicker || this._settings.icons ? 30 : 0),
	          width: 250,
	          multiselect: this._settings.multiselect,
	          timepicker: timepicker,
	          type: this._settings.type,
	          icons: this._settings.icons,
	          timeIcon: this._settings.timeIcon
	        }
	      });
	    }

	    this._init_once = function () {};
	  },
	  $render: function (obj) {
	    if (isUndefined(obj.value)) return;
	    obj.value = this.$prepareValue(obj.value);
	    this.$setValue(obj.value);
	  },
	  $prepareValue: function (value) {
	    if (this._settings.multiselect) {
	      if (typeof value === "string") value = value.split(this._settings.separator);else if (value instanceof Date) {
	        value = [value];
	      } else if (!value) {
	        value = [];
	      }

	      for (var i = 0; i < value.length; i++) {
	        value[i] = this._prepareSingleValue(value[i]);
	      }

	      return value;
	    } else {
	      return this._prepareSingleValue(value);
	    }
	  },
	  _prepareSingleValue: function (value) {
	    var type = this._settings.type;
	    var timeMode = type == "time"; //setValue("1980-12-25")

	    if (!isNaN(parseFloat(value))) value = "" + value;

	    if (typeof value == "string" && value) {
	      var formatDate = null;

	      if ((type == "month" || type == "year") && this._formatDate) {
	        formatDate = this._formatDate;
	      } else formatDate = timeMode ? i18n.parseTimeFormatDate : i18n.parseFormatDate;

	      value = formatDate(value);
	    }

	    if (value) {
	      //time mode
	      if (timeMode) {
	        //setValue([16,24])
	        if (isArray(value)) {
	          var time = new Date();
	          time.setHours(value[0]);
	          time.setMinutes(value[1]);
	          value = time;
	        }
	      } //setValue(invalid date)


	      if (isNaN(value.getTime())) value = "";
	    }

	    return value;
	  },
	  _get_visible_text: function (value) {
	    if (this._settings.multiselect) {
	      return [].concat(value).map(function (a) {
	        return this._get_visible_text_single(a);
	      }.bind(this)).join(this.config.separator);
	    } else return this._get_visible_text_single(value);
	  },
	  _get_visible_text_single: function (value) {
	    var timeMode = this._settings.type == "time";
	    var timepicker = this.config.timepicker;
	    var formatStr = this._formatStr || (timeMode ? i18n.timeFormatStr : timepicker ? i18n.fullDateFormatStr : i18n.dateFormatStr);
	    return formatStr(value);
	  },
	  _set_visible_text: function () {
	    var node = this.getInputNode();

	    if (node.value == undefined) {
	      node.innerHTML = this._settings.text || this._get_div_placeholder();
	    } else {
	      node.value = this._settings.text || "";
	    }
	  },
	  $compareValue: function (oldvalue, value) {
	    if (!oldvalue && !value) return true;
	    return wDate.equal(oldvalue, value);
	  },
	  $setValue: function (value) {
	    this._settings.text = value ? this._get_visible_text(value) : "";

	    this._set_visible_text();
	  },
	  format_setter: function (value) {
	    if (value) {
	      if (typeof value === "function") this._formatStr = value;else {
	        this._formatStr = wDate.dateToStr(value);
	        this._formatDate = wDate.strToDate(value);
	      }
	    } else this._formatStr = this._formatDate = null;

	    return value;
	  },
	  getInputNode: function () {
	    return this._settings.editable ? this._dataobj.getElementsByTagName("input")[0] : this._dataobj.getElementsByTagName("DIV")[1];
	  },
	  getValue: function () {
	    if (this._settings.multiselect) {
	      var value = this._settings.value;
	      if (!value) return [];
	      var result = [].concat(value).map(function (a) {
	        return this._get_value_single(a);
	      }.bind(this));
	      if (this._settings.stringResult) return result.join(this._settings.separator);
	      return result;
	    }

	    return this._get_value_single(this._settings.value);
	  },
	  _get_value_single: function (value) {
	    var type = this._settings.type; //time mode

	    var timeMode = type == "time"; //date and time mode

	    var timepicker = this.config.timepicker; //input was not rendered, we need to parse value from setValue method

	    if (!this._rendered_input) value = this.$prepareValue(value) || null; //rendere and in edit mode
	    else if (this._settings.editable) {
	        var formatDate = this._formatDate || (timeMode ? i18n.timeFormatDate : timepicker ? i18n.fullDateFormatDate : i18n.dateFormatDate);
	        value = formatDate(this.getInputNode().value);
	      } //return string from getValue

	    if (this._settings.stringResult) {
	      var formatStr = i18n.parseFormatStr;
	      if (timeMode) formatStr = i18n.parseTimeFormatStr;

	      if (this._formatStr && (type == "month" || type == "year")) {
	        formatStr = this._formatStr;
	      }

	      if (this._settings.multiselect) return [].concat(value).map(function (a) {
	        return a ? formatStr(a) : "";
	      });
	      return value ? formatStr(value) : "";
	    }

	    return value || null;
	  },
	  getText: function () {
	    var node = this.getInputNode();
	    return node ? typeof node.value == "undefined" ? this.getValue() ? node.innerHTML : "" : node.value : "";
	  }
	};
	var view$A = exports.protoUI(api$A, text.view);
	var datepicker = {
	  api: api$A,
	  view: view$A
	};

	var api$B = {
	  name: "colorpicker",
	  $init: function () {
	    this.$ready.push(this._init_popup);
	  },
	  defaults: {
	    icon: true
	  },
	  _init_popup: function () {
	    var obj = this._settings;
	    if (obj.suggest) obj.popup = obj.suggest;else if (!obj.popup) obj.popup = obj.suggest = this.suggest_setter({
	      type: "colorboard",
	      height: 200
	    });

	    this._init_once = function () {};
	  },
	  $render: function (obj) {
	    if (isUndefined(obj.value)) return;
	    obj.value = this.$prepareValue(obj.value);
	    this.$setValue(obj.value);
	  },
	  getValue: function () {
	    if (this._rendered_input && this._settings.editable) return this.getInputNode().value;else return this._settings.value;
	  },
	  $prepareValue: function (value) {
	    if (value && value.charAt && value.charAt(0) != "#") value = "#" + value;
	    return value || "";
	  },
	  _getColorNode: function () {
	    return this.$view.getElementsByTagName("DIV")[this._settings.editable ? 1 : 2];
	  },
	  _get_visible_text: function (value) {
	    return value;
	  },
	  $setValue: function (value) {
	    this._getColorNode().style.backgroundColor = value;
	    this._settings.text = value;
	    var node = this.getInputNode();
	    if (node.value == undefined) node.innerHTML = value;else node.value = value;
	  },
	  $renderIcon: function () {
	    var config = this.config;
	    return "<div class=\"webix_input_icon\" style=\"background-color:" + config.value + ";\"> </div>";
	  }
	};
	var view$B = exports.protoUI(api$B, datepicker.view);

	var api$C = {
	  name: "richselect",
	  defaults: {
	    template: function (obj, common) {
	      return common._render_div_block(obj, common);
	    },
	    popupWidth: 200,
	    icon: "wxi-menu-down"
	  },
	  _onBlur: function () {
	    if (this._settings.text == this.getText() || isUndefined(this._settings.text) && !this.getText()) return;
	    var suggest = this.getPopup(),
	        nodeValue = this.getInputNode().value,
	        value = suggest.getSuggestion(nodeValue),
	        oldvalue = this.getValue(); //non-empty value that differs from old value and matches filtering rule

	    if (value && value != oldvalue && !(nodeValue === "" && suggest.getItemText(value) !== "")) this.setValue(value);else if (nodeValue === "") this.setValue("");else if (this._revertValue) this._revertValue();
	  },
	  suggest_setter: function (value) {
	    return this.options_setter(value);
	  },
	  options_setter: function (value) {
	    value = this._suggest_config ? this._suggest_config(value) : value;
	    var suggest = this._settings.popup = this._settings.suggest = text.api.suggest_setter.call(this, value);
	    var list = $$(suggest).getList();
	    if (list) list.attachEvent("onAfterLoad", bind(this._reset_value, this));
	    return suggest;
	  },
	  getList: function () {
	    var suggest = $$(this._settings.suggest);
	    assert(suggest, "Input doesn't have a list");
	    return suggest.getList();
	  },
	  _pattern: function (value) {
	    return value;
	  },
	  _reset_value: function () {
	    var value = this._settings.value; //this._dataobj.firstChild - check that input is already rendered, as in IE11 it can be destroy during parent repainting

	    if (!isUndefined(value) && !this.getPopup().isVisible() && !this._settings.text && this._dataobj.firstChild) this.$setValue(value);
	  },
	  $skin: function () {
	    this.defaults.inputPadding = $active.inputPadding;
	  },
	  $render: function (obj) {
	    if (isUndefined(obj.value)) return;
	    this.$setValue(obj.value);
	  },
	  getInputNode: function () {
	    return this._dataobj.getElementsByTagName("DIV")[1];
	  },
	  getPopup: function () {
	    return $$(this._settings.popup);
	  },
	  getText: function () {
	    var value = this._settings.value,
	        node = this.getInputNode();
	    if (!node) return value ? this.getPopup().getItemText(value) : "";

	    if (typeof node.value == "undefined") {
	      if (node.firstChild && node.firstChild.className === "webix_placeholder") return "";
	      return node.innerHTML;
	    }

	    return node.value;
	  },
	  $setValue: function (value) {
	    if (!this._rendered_input) return;
	    var text$$1 = value;
	    var popup = this.getPopup();
	    if (popup) text$$1 = this.getPopup().getItemText(value);

	    if (!text$$1 && value && value.id) {
	      //add new value
	      var _popup = this.getPopup();

	      var list = _popup.getList(); // add new item only when item with such id doesn't exists yet


	      var exists = list.exists(value.id);
	      if (!exists) list.add(value);
	      text$$1 = _popup.getItemText(value.id); // in case of dynamic list, we can't add extra items
	      // to not interfere with dynamic loading

	      if (list._settings.dynamic && !exists) list.remove(value.id);
	      this._settings.value = value.id;
	    }

	    var node = this.getInputNode();
	    if (isUndefined(node.value)) node.innerHTML = text$$1 || this._get_div_placeholder();else node.value = text$$1 = text$$1.replace(/<[^>]*>/g, "");
	    this._settings.text = text$$1;
	  },
	  getValue: function () {
	    return this._settings.value || "";
	  },
	  _ignoreLabelClick: function (ev) {
	    this.focus();
	    preventEvent(ev);
	  }
	};
	var view$C = exports.protoUI(api$C, text.view);
	var richselect = {
	  api: api$C,
	  view: view$C
	};

	var api$D = {
	  name: "combo",
	  getInputNode: function () {
	    return this._dataobj.getElementsByTagName("input")[0];
	  },
	  $render: function (obj) {
	    if (isUndefined(obj.value)) return;
	    this.$setValue(obj.value);
	  },
	  _revertValue: function () {
	    if (!this._settings.editable) {
	      var value = this.getValue();
	      this.$setValue(isUndefined(value) ? "" : value);
	    }
	  },
	  _applyChanges: function () {
	    var input = this.getInputNode(),
	        value = "",
	        suggest = this.getPopup();

	    if (input.value) {
	      value = this._settings.value;
	      if (suggest.getItemText(value) != this.getText()) value = suggest.getSuggestion() || value;
	    }

	    if (value != this._settings.value) this.setValue(value, true);else this.$setValue(value);
	  },
	  $compareValue: function (value) {
	    var result = richselect.api.$compareValue.apply(this, arguments);
	    if (result && value != this.getText()) this._revertValue();
	    return result;
	  },
	  defaults: {
	    template: function (config, common) {
	      return common.$renderInput(config).replace(/(<input)\s*(?=\w)/, "$1" + " role='combobox'");
	    },
	    icon: "wxi-menu-down"
	  },
	  on_click: {
	    "webix_inp_label": function (e) {
	      this._ignoreLabelClick(e);
	    },
	    "webix_inp_top_label": function (e) {
	      this._ignoreLabelClick(e);
	    }
	  }
	};
	var view$D = exports.protoUI(api$D, richselect.view);

	var api$E = {
	  name: "counter",
	  defaults: {
	    template: function (config, common) {
	      var value = config.value;
	      var id = "x" + uid();
	      var html = "<div role='spinbutton' aria-label='" + template.escape(config.label) + "' aria-valuemin='" + config.min + "' aria-valuemax='" + config.max + "' aria-valuenow='" + config.value + "' class='webix_el_group' style='width:" + common._get_input_width(config) + "px'>";
	      html += "<button type='button' class='webix_inp_counter_prev' tabindex='-1' aria-label='" + i18n.aria.decreaseValue + "'>-</button>";
	      html += common._baseInputHTML("input") + " id='" + id + "' type='text' class='webix_inp_counter_value' aria-live='assertive'" + " value='" + value + "'></input>";
	      html += "<button type='button' class='webix_inp_counter_next' tabindex='-1' aria-label='" + i18n.aria.increaseValue + "'>+</button></div>";
	      return common.$renderInput(config, html, id);
	    },
	    min: 0,
	    max: Infinity,
	    value: 0,
	    step: 1
	  },
	  $init: function () {
	    _event(this.$view, "keydown", this._keyshift, {
	      bind: this
	    });
	  },
	  _keyshift: function (e) {
	    var code = e.which || e.keyCode,
	        c = this._settings,
	        value = this.getValue();

	    if (code > 32 && code < 41) {
	      if (code === 36) value = c.min;else if (code === 35) value = c.max === Infinity ? 1000000 : c.max;else if (code === 33) this.next();else if (code === 34) this.prev();else value = value + (code === 37 || code === 40 ? -1 : 1);
	      if (code > 34 && value >= c.min && value <= c.max) this.setValue(value);
	    }
	  },
	  $setValue: function (value) {
	    this.getInputNode().value = value;
	  },
	  $prepareValue: function (value) {
	    value = parseFloat(value);
	    return isNaN(value) ? 0 : value;
	  },
	  value_setter: function (value) {
	    return this.$prepareValue(value);
	  },
	  getInputNode: function () {
	    return this._dataobj.getElementsByTagName("input")[0];
	  },
	  getValue: function () {
	    return button$1.api.getValue.apply(this, arguments) * 1;
	  },
	  next: function (step) {
	    step = 1 * (step || this._settings.step);
	    this.shift(step);
	  },
	  prev: function (step) {
	    step = -1 * (step || this._settings.step);
	    this.shift(step);
	  },
	  shift: function (step) {
	    var min = this._settings.min;
	    var max = this._settings.max;
	    var new_value = this.getValue() + step;
	    if (new_value >= min && new_value <= max) this.setValue(new_value);
	  }
	};
	var view$E = exports.protoUI(api$E, text.view);

	var api$F = {
	  name: "icon",
	  $skin: function () {
	    this.defaults.height = $active.inputHeight;
	    this.defaults.width = $active.inputHeight;
	  },
	  defaults: {
	    template: function (obj, view) {
	      var min = Math.min(obj.awidth, obj.aheight);
	      var top = Math.round((view._content_height - obj.aheight) / 2);
	      var inner = "<button type='button' style='height:" + min + "px;width:" + min + "px;' class='webix_icon_button'>" + "<span class='webix_icon " + obj.icon + "'></span></button>";
	      return "<div class='webix_el_box' style='width:" + obj.awidth + "px;height:" + obj.aheight + "px;line-height:" + obj.aheight + "px;margin-top:" + top + "px'>" + inner + (obj.badge ? "<span class='webix_badge'>" + obj.badge + "</span>" : "") + "</div>";
	    }
	  },
	  _set_inner_size: false,
	  _set_default_css: function () {},
	  $setValue: function () {}
	};
	var view$F = exports.protoUI(api$F, button$1.view);

	var api$G = {
	  name: "search",
	  on_click: {
	    "webix_input_icon": function (e) {
	      this.getInputNode().focus();
	      this.callEvent("onSearchIconClick", [e]);
	    }
	  },
	  $skin: function () {
	    this.defaults.inputPadding = $active.inputPadding;
	  },
	  defaults: {
	    type: "text",
	    icon: "wxi-search"
	  }
	};
	var view$G = exports.protoUI(api$G, text.view);

	var api$H = {
	  name: "segmented",
	  $allowsClear: false,
	  $init: function () {
	    this.attachEvent("onChange", function (value) {
	      if (this._settings.multiview) this._show_view(value);
	    });
	    this.attachEvent("onAfterRender", once(function () {
	      if (this._settings.multiview && this._settings.value) this._show_view(this._settings.value);
	    }));
	  },
	  _show_view: function (value) {
	    var top = this.getTopParentView();
	    var view = null; //get from local isolate

	    if (top && top.$$) view = top.$$(value); //or check globally

	    if (!view) view = $$(value);
	    if (view && view.show) view.show();
	  },
	  defaults: {
	    template: function (obj, common) {
	      if (!obj.options) assert(false, "segmented: options undefined");
	      var options = obj.options;

	      common._check_options(options);

	      options = common._filterOptions(options);

	      var width = common._get_input_width(obj);

	      var id = uid();
	      var html = "<div style='width:" + width + "px' class='webix_all_segments' role='tablist' aria-label='" + template.escape(obj.label) + "'>";
	      var optionWidth = obj.optionWidth || Math.floor(width / options.length);
	      if (!obj.value) obj.value = options[0].id;
	      var tooltip;

	      for (var i = 0; i < options.length; i++) {
	        tooltip = obj.tooltip ? " webix_t_id='" + options[i].id + "'" : "";
	        html += "<button type='button' style='width:" + (options[i].width || optionWidth) + "px' role='tab' aria-selected='" + (obj.value == options[i].id ? "true" : "false") + "' tabindex='" + (obj.value == options[i].id ? "0" : "-1") + "'";
	        html += "class='" + "webix_segment_" + (i == options.length - 1 ? "N" : i > 0 ? 1 : 0) + (obj.value == options[i].id ? " webix_selected " : "") + "' " +
	        /*@attr*/
	        "button_id='" + options[i].id + "'" + tooltip + ">";
	        html += options[i].value + "</button>";
	      }

	      return common.$renderInput(obj, html + "</div>", id);
	    }
	  },
	  _getInputNode: function () {
	    return this.$view.getElementsByTagName("BUTTON");
	  },
	  focus: function () {
	    return this._focus();
	  },
	  blur: function () {
	    this._blur();
	  },
	  $setValue: function (value) {
	    var options = this._getInputNode();

	    for (var i = 0; i < options.length; i++) {
	      var id = options[i].getAttribute(
	      /*@attr*/
	      "button_id");
	      options[i].setAttribute("aria-selected", value == id ? "true" : "false");
	      options[i].setAttribute("tabindex", value == id ? "0" : "-1");
	      if (value == id) addCss(options[i], "webix_selected");else removeCss(options[i], "webix_selected");
	    } //refresh tabbar if the option is in the popup list


	    var popup = this.config.tabbarPopup;
	    if (popup && $$(popup) && $$(popup).getBody().exists(value)) this.refresh();
	  },
	  getValue: function () {
	    return this._settings.value;
	  },
	  getInputNode: function () {
	    return null;
	  },
	  optionIndex: function (id) {
	    var pages = this._settings.options;

	    for (var i = 0; i < pages.length; i++) {
	      if (pages[i].id == id) return i;
	    }

	    return -1;
	  },
	  addOption: function (id, value, show, index$$1) {
	    var obj = id;

	    if (_typeof(id) != "object") {
	      value = value || id;
	      obj = {
	        id: id,
	        value: value
	      };
	    } else {
	      id = obj.id;
	      index$$1 = show;
	      show = value;
	    }

	    if (this.optionIndex(id) < 0) PowerArray.insertAt.call(this._settings.options, obj, index$$1);
	    this.refresh();
	    if (show) this.setValue(id);
	  },
	  removeOption: function (id) {
	    var index$$1 = this.optionIndex(id);
	    var options = this._settings.options;
	    if (index$$1 >= 0) PowerArray.removeAt.call(options, index$$1); // if we remove a selected option

	    if (this._settings.value == id) this._setNextVisible(options, index$$1);
	    this.refresh();
	    this.callEvent("onOptionRemove", [id, this._settings.value]);
	  },
	  _setNextVisible: function (options, index$$1) {
	    var size = options.length;

	    if (size) {
	      index$$1 = Math.min(index$$1, size - 1); //forward search

	      for (var i = index$$1; i < size; i++) {
	        if (!options[i].hidden) return this.setValue(options[i].id);
	      } //backward search


	      for (var _i = index$$1; _i >= 0; _i--) {
	        if (!options[_i].hidden) return this.setValue(options[_i].id);
	      }
	    } //nothing found		


	    this.setValue("");
	  },
	  _filterOptions: function (options) {
	    var copy$$1 = [];

	    for (var i = 0; i < options.length; i++) {
	      if (!options[i].hidden) copy$$1.push(options[i]);
	    }

	    return copy$$1;
	  },
	  _setOptionVisibility: function (id, state) {
	    var options = this._settings.options;
	    var index$$1 = this.optionIndex(id);
	    var option = options[index$$1];

	    if (option && state == !!option.hidden) {
	      //new state differs from previous one
	      option.hidden = !state;

	      if (state || this._settings.value != id) {
	        //show item, no need for extra steps
	        this.refresh();
	      } else {
	        //hide item, switch to next visible one
	        this._setNextVisible(options, index$$1);
	      }
	    }
	  },
	  hideOption: function (id) {
	    this._setOptionVisibility(id, false);
	  },
	  showOption: function (id) {
	    this._setOptionVisibility(id, true);
	  },
	  _set_inner_size: false
	};
	var view$H = exports.protoUI(api$H, text.view, HTMLOptions);
	var segmented = {
	  api: api$H,
	  view: view$H
	};

	var api$I = {
	  name: "textarea",
	  defaults: {
	    template: function (obj, common) {
	      var name = obj.name || obj.id;
	      var id = "x" + uid();
	      var html = common._baseInputHTML("textarea") + "style='width:" + common._get_input_width(obj) + "px;'";
	      html += " id='" + id + "' name='" + name + "' class='webix_inp_textarea'>" + common._pattern(obj.value) + "</textarea>";
	      return common.$renderInput(obj, html, id);
	    },
	    height: 0,
	    minHeight: 60
	  },
	  $skin: function () {
	    this.defaults.inputPadding = $active.inputPadding;
	    this._inputSpacing = $active.inputSpacing;
	  },
	  _skipSubmit: true,
	  _getLabelHeight: function (top) {
	    return top ? this._labelTopHeight - this._settings.inputPadding : "";
	  },
	  //get input element
	  getInputNode: function () {
	    return this._dataobj.getElementsByTagName("textarea")[0];
	  }
	};
	var view$I = exports.protoUI(api$I, text.view);

	var api$J = {
	  name: "toggle",
	  $allowsClear: true,
	  $init: function () {
	    this.attachEvent("onItemClick", function () {
	      this.toggle();
	    });
	  },
	  $setValue: function (value) {
	    var input = this.getInputNode();
	    var obj = this._settings;
	    var isPressed = value && value != "0";
	    var text = (isPressed ? obj.onLabel : obj.offLabel) || obj.label;
	    var textNode = input.lastChild;
	    input.setAttribute("aria-pressed", isPressed ? "true" : false);
	    input.value = text;
	    if (textNode) (textNode.firstChild || textNode).nodeValue = text; //icon or image button

	    if (input.firstChild && input.firstChild.nodeName === "SPAN" && obj.onIcon && obj.offIcon && obj.onIcon !== obj.offIcon) input.firstChild.className = input.firstChild.className.replace(isPressed ? obj.offIcon : obj.onIcon, isPressed ? obj.onIcon : obj.offIcon);
	    var parent = input.parentNode;
	    if (isPressed) addCss(parent, "webix_pressed");else removeCss(parent, "webix_pressed");
	  },
	  toggle: function () {
	    this.setValue(!this.getValue());
	  },
	  getValue: function () {
	    var value = this._settings.value;
	    return !value || value == "0" ? 0 : 1;
	  },
	  defaults: {
	    template: function (obj, common) {
	      var isPressed = obj.value && obj.value != "0";
	      var css = isPressed ? " webix_pressed" : "";
	      obj.label = (isPressed ? obj.onLabel : obj.offLabel) || obj.label;
	      obj.icon = (isPressed ? obj.onIcon : obj.offIcon) || obj.icon;
	      var html = "<div class='webix_el_box" + css + "' style='width:" + obj.awidth + "px; height:" + obj.aheight + "px'>" + common.$renderInput(obj, common) + "</div>";
	      html = html.replace(/(button)\s*(?=\w)/, "$1" + (" aria-pressed='" + (isPressed ? "true" : "false") + "' "));
	      if (obj.badge) html = html.replace(/<\/div>$/, "<span class='webix_badge'>" + obj.badge + "</span></div>");
	      return html;
	    }
	  },
	  _set_inner_size: false
	};
	var view$J = exports.protoUI(api$J, button$1.view);

	var api$K = {
	  name: "multitext",
	  $cssName: "text",
	  defaults: {
	    icon: "wxi-plus-circle",
	    iconWidth: 25,
	    separator: ", "
	  },
	  getValueHere: function () {
	    return text.api.getValue.call(this);
	  },
	  setValueHere: function (value) {
	    return text.api.$setValue.call(this, value);
	  },
	  getValue: function () {
	    if (this.config.mode == "extra") return this.getValueHere();
	    if (this._full_value) return this._full_value;
	    var values = [this.getValueHere(this)];

	    for (var i = 0; i < this._subs.length; i++) {
	      var seg = $$(this._subs[i]).getValueHere();
	      if (seg) values.push(seg);
	    }

	    return values.join(this.config.separator);
	  },
	  $setValue: function (value) {
	    value = value || "";
	    if (this.config.mode == "extra") return this.setValueHere(value);
	    this._full_value = value;
	    var parts = value.split(this.config.separator);

	    if (parts.length == this._subs.length + 1) {
	      this.setValueHere(parts[0]);

	      for (var i = 0; i < this._subs.length; i++) {
	        $$(this._subs[i]).setValueHere(parts[i + 1]);
	      }

	      this._full_value = "";
	      return;
	    }

	    this.removeSection();
	    this.setValueHere.call(this, parts[0]);

	    for (var _i = 1; _i < parts.length; _i++) {
	      this.addSection(parts[_i]);
	    }

	    this._full_value = "";
	  },
	  _subOnChange: function () {
	    var parent = this.config.master ? $$(this.config.master) : this;
	    var newvalue = parent.getValue();
	    var oldvalue = parent._settings.value;

	    if (newvalue !== oldvalue) {
	      parent._settings.value = newvalue;
	      parent.callEvent("onChange", [newvalue, oldvalue]);
	    }
	  },
	  addSection: function (text$$1) {
	    var config = this.config,
	        newConfig = {
	      labelWidth: config.labelWidth,
	      inputWidth: config.inputWidth,
	      width: config.width,
	      label: config.label ? "&nbsp;" : "",
	      view: this.name,
	      mode: "extra",
	      value: text$$1 || "",
	      icon: "wxi-minus-circle",
	      tooltip: config.tooltip,
	      suggest: config.suggest || null,
	      master: config.id
	    };
	    exports.extend(newConfig, config.subConfig || {}, true);
	    var newone = this.getParentView().addView(newConfig);
	    $$(newone).attachEvent("onChange", this._subOnChange);

	    this._subs.push(newone);

	    this.callEvent("onSectionAdd", [newone, this._subs.length]);
	    return newone;
	  },
	  removeSection: function (id) {
	    var parent = this.config.master ? $$(this.config.master) : this;

	    for (var i = parent._subs.length - 1; i >= 0; i--) {
	      var section = parent._subs[i];

	      if (!id || section == id) {
	        parent._subs.removeAt(i);

	        this.getParentView().removeView(section);
	        parent.callEvent("onSectionRemove", [section, i + 1]);
	      }
	    }
	  },
	  on_click: {
	    "webix_input_icon": function () {
	      if (this.config.mode == "extra") {
	        this.removeSection(this.config.id);
	        var childs = this.getParentView().getChildViews();
	        childs[childs.length - 1].focus();

	        this._subOnChange();
	      } else $$(this.addSection()).focus();

	      return false;
	    }
	  },
	  $init: function () {
	    this._subs = toArray([]);
	    this.attachEvent("onKeyPress", this._onKeyPress);
	  },
	  $render: function (obj) {
	    this.$setValue(obj.value);
	  }
	};
	var view$K = exports.protoUI(api$K, text.view);

	exports.protoUI({
	  name: "multiselect",
	  defaults: {
	    template: "GPL version doesn't support multiselect <br> You need Webix PRO"
	  }
	}, template$1.view);

	exports.protoUI({
	  name: "multicombo",
	  defaults: {
	    template: "GPL version doesn't support multicombo <br> You need Webix PRO"
	  }
	}, template$1.view);

	var api$L = {
	  name: "slider",
	  $touchCapture: true,
	  defaults: {
	    min: 0,
	    max: 100,
	    value: 50,
	    step: 1,
	    title: false,
	    moveTitle: true,
	    template: function (obj, common) {
	      var id = common._handle_id = "x" + uid();
	      var html = "";
	      var title = "<div class='webix_slider_title" + (obj.moveTitle ? " webix_slider_move" : "") + "'" + (!obj.moveTitle && obj.vertical ? " style='line-height:" + (obj.aheight - obj.inputPadding * 2) + "px;'" : "") + "></div>";
	      var left = "<div class='webix_slider_left'>&nbsp;</div>";
	      var right = "<div class='webix_slider_right'></div>";
	      var handle = "<div class='webix_slider_handle' " +
	      /*@attr*/
	      "webix_disable_drag" + "='true' role='slider' aria-label='" + obj.label + (obj.title ? " " + obj.title(obj) : "") + "' aria-valuemax='" + obj.max + "' aria-valuemin='" + obj.min + "' aria-valuenow='" + obj.value + "' tabindex='0' id='" + id + "'>&nbsp;</div>";
	      if (obj.vertical) html = "<div class='webix_slider_box'>" + right + left + handle + "</div>" + title;else html = title + "<div class='webix_slider_box'>" + left + right + handle + "</div>";
	      return common.$renderInput(obj, html, id);
	    }
	  },
	  type_setter: function (type) {
	    this._viewobj.className += " webix_slider_" + type;
	  },
	  title_setter: function (value) {
	    if (typeof value == "string") return template(value);
	    return value;
	  },
	  _get_slider_handle: function () {
	    return this.$view.querySelector(".webix_slider_handle");
	  },
	  _set_inner_size: function () {
	    var handle = this._get_slider_handle();

	    var config = this._settings;

	    if (handle) {
	      //view is rendered for sure
	      var size = config.vertical ? this._content_height : this._get_input_width(config); //width or height

	      var value = config.value % config.step ? Math.round(config.value / config.step) * config.step : config.value;
	      var max = config.max - config.min;
	      value = Math.max(Math.min(value, config.max), config.min);
	      value = config.vertical ? max - (value - config.min) : value - config.min; //top or left

	      var corner1 = Math.ceil((size - 2 * this._sliderPadding) * value / max); //bottom or right

	      var corner2 = size - 2 * this._sliderPadding - corner1;
	      var cornerStr = config.vertical ? "top" : "left";
	      var sizeStr = config.vertical ? "height" : "width";
	      handle.style[cornerStr] = this._sliderPadding + corner1 - this._sliderHandleWidth / 2 + "px";
	      handle.parentNode.style[sizeStr] = size + "px"; //1px border

	      corner2 = Math.min(Math.max(corner2, 2 * this._sliderBorder), size - this._sliderPadding * 2 - 2 * this._sliderBorder);
	      corner1 = Math.min(Math.max(corner1, 2 * this._sliderBorder), size - this._sliderPadding * 2 - 2 * this._sliderBorder); //width for left/top and right/bottom bars

	      var part = handle.previousSibling;
	      part.style[sizeStr] = corner2 + "px";
	      var last = part.previousSibling;
	      last.style[sizeStr] = corner1 + "px";

	      this._set_title(handle, corner1, corner2, cornerStr);
	    }
	  },
	  _set_title: function (handle, corner1, corner2, cornerStr) {
	    var config = this._settings;

	    if (this._settings.title) {
	      var title = handle.parentNode[config.vertical ? "nextSibling" : "previousSibling"];
	      title.innerHTML = this._settings.title(this._settings, this);

	      if (this._settings.moveTitle) {
	        var pos$$1 = 0;
	        if (config.vertical) pos$$1 = corner1 + 2 * this._sliderBorder - this._sliderHandleWidth / 2;else {
	          var half = title.clientWidth / 2;
	          var pos1 = half > corner1 ? half - corner1 - 2 * this._sliderBorder : 0; //left/top text is to large

	          var pos2 = half > corner2 ? half - corner2 - 2 * this._sliderBorder - this._sliderHandleWidth / 2 : 0; //right/bottom text is too large

	          pos$$1 = this._sliderPadding + corner1 - half + pos1 - pos2;
	        }
	        title.style[cornerStr] = pos$$1 + "px";
	      }
	    }
	  },
	  _set_value_now: function () {
	    this._get_slider_handle().setAttribute("aria-valuenow", this._settings.value);
	  },
	  refresh: function () {
	    var handle = this._get_slider_handle();

	    if (handle) {
	      this._set_value_now();

	      if (this._settings.title) handle.setAttribute("aria-label", this._settings.label + " " + this._settings.title(this._settings, this));

	      this._set_inner_size();
	    }
	  },
	  value_setter: function (value) {
	    return this.$prepareValue(value);
	  },
	  $setValue: function () {
	    this.refresh();
	  },
	  $getValue: function () {
	    return this._settings.value;
	  },
	  $prepareValue: function (value) {
	    value = parseFloat(value);
	    return isNaN(value) ? 0 : value;
	  },
	  $init: function (config) {
	    if (env.touch) this.attachEvent("onTouchStart", bind(this._on_mouse_down_start, this));else _event(this._viewobj, "mousedown", bind(this._on_mouse_down_start, this));

	    _event(this.$view, "keydown", bind(this._handle_move_keyboard, this));

	    if (config.vertical) {
	      config.height = config.height || $active.vSliderHeight;
	      this._viewobj.className += " webix_slider_vertical";
	      this._sliderPadding = $active.vSliderPadding;
	    }
	  },
	  $skin: function () {
	    this._sliderHandleWidth = $active.sliderHandleWidth; //8 - width of handle / 2

	    this._sliderPadding = $active.sliderPadding; //10 - padding of webix_slider_box ( 20 = 10*2 )

	    this._sliderBorder = $active.sliderBorder; //1px border
	  },
	  _handle_move_keyboard: function (e) {
	    var code = e.keyCode,
	        c = this._settings,
	        value = c.value;

	    if (code > 32 && code < 41) {
	      preventEvent(e);
	      var trg = e.target || e.srcElement;
	      var match = /webix_slider_handle_(\d)/.exec(trg.className);
	      this._activeIndex = match ? parseInt(match[1], 10) : -1;
	      if (match) value = c.value[this._activeIndex];
	      value = value < c.min ? c.min : value > c.max ? c.max : value;
	      if (code === 36) value = c.min;else if (code === 35) value = c.max;else {
	        var inc = code === 37 || code === 40 || code === 34 ? -1 : 1;
	        if (code === 33 || code === 34 || c.step > 1) inc = inc * c.step;
	        value = value * 1 + inc;
	      }

	      if (match) {
	        var other = c.value[this._activeIndex ? 0 : 1];
	        value = this._activeIndex && value <= other || !this._activeIndex && value >= other ? other : value;
	      }

	      if (value >= c.min && value <= c.max) {
	        if (match) {
	          var temp = [];

	          for (var i = 0; i < c.value.length; i++) {
	            temp[i] = i === this._activeIndex ? value : c.value[i];
	          }

	          value = temp;
	        }

	        this.setValue(value);
	        this._activeIndex = -1;
	      }
	    }
	  },
	  _on_mouse_down_start: function (e) {
	    var trg = e.target || e.srcElement;

	    if (this._mouse_down_process) {
	      this._mouse_down_process(e);
	    }

	    var value = this._settings.value;
	    if (isArray(value)) value = copy(value);

	    if (trg.className.indexOf("webix_slider_handle") != -1) {
	      this._start_value = value;
	      return this._start_handle_dnd.apply(this, arguments);
	    } else if (trg.className.indexOf("webix_slider") != -1) {
	      this._start_value = value;
	      this._settings.value = this._get_value_from_event.apply(this, arguments);

	      this._start_handle_dnd(e);
	    }
	  },
	  _start_handle_dnd: function () {
	    if (env.touch) {
	      this._handle_drag_events = [this.attachEvent("onTouchMove", bind(this._handle_move_process, this)), this.attachEvent("onTouchEnd", bind(this._handle_move_stop, this))];
	    } else this._handle_drag_events = [event$1(document.body, "mousemove", bind(this._handle_move_process, this)), event$1(window, "mouseup", bind(this._handle_move_stop, this))];

	    addCss(document.body, "webix_noselect");
	  },
	  _handle_move_stop: function () {
	    //detach event handlers
	    if (this._handle_drag_events) {
	      if (env.touch) {
	        this.detachEvent(this._handle_drag_events[0]);
	        this.detachEvent(this._handle_drag_events[1]);
	      } else {
	        eventRemove(this._handle_drag_events[0]);
	        eventRemove(this._handle_drag_events[1]);
	      }

	      this._handle_drag_events = [];
	    }

	    removeCss(document.body, "webix_noselect");
	    var value = this._settings.value;
	    if (isArray(value)) value = copy(value);
	    this._settings.value = this._start_value;
	    this.setValue(value);

	    this._get_slider_handle(this._activeIndex).focus();

	    this._activeIndex = -1;
	  },
	  _handle_move_process: function () {
	    this._settings.value = this._get_value_from_event.apply(this, arguments);
	    this.refresh();
	    this.callEvent("onSliderDrag", []);
	  },
	  _get_value_from_event: function (event, touchContext) {
	    // this method takes 2 arguments in case of touch env
	    var pos$$1 = 0;
	    var ax = this._settings.vertical ? "y" : "x";
	    if (env.touch) pos$$1 = touchContext ? touchContext[ax] : event[ax];else pos$$1 = pos(event)[ax];
	    return this._get_value_from_pos(pos$$1);
	  },
	  _get_value_from_pos: function (pos$$1) {
	    var config = this._settings;
	    var max = config.max - config.min;
	    var ax = config.vertical ? "y" : "x"; //top or left depending on slider type

	    var corner = offset(this._get_slider_handle().parentNode)[ax] + this._sliderPadding; //height or width depending on slider type


	    var size = (config.vertical ? this._content_height : this._get_input_width(config)) - 2 * this._sliderPadding;
	    var newvalue = size ? (pos$$1 - corner) * max / size : 0;
	    if (config.vertical) newvalue = max - newvalue;
	    newvalue = Math.round((newvalue + 1 * config.min) / config.step) * config.step;
	    return Math.max(Math.min(newvalue, config.max), config.min);
	  },
	  _init_onchange: function () {} //need not ui.text logic

	};
	var view$L = exports.protoUI(api$L, text.view);

	exports.protoUI({
	  name: "rangeslider",
	  defaults: {
	    template: "GPL version doesn't support rangeslider <br> You need Webix PRO"
	  }
	}, template$1.view);

	var api$M = {
	  name: "switch",
	  defaults: {
	    template: function (config, common) {
	      common._calck_switch_size();

	      var id = config.name || "x" + uid();
	      var rightlabel = "";

	      if (config.labelRight) {
	        rightlabel = "<label class='webix_label_right'>" + config.labelRight + "</label>";
	        if (config.labelWidth) config.label = config.label || "&nbsp;";
	      }

	      var checked = config.checkValue == config.value;
	      var aria = "aria-label=\"" + (config.label || config.labelRight || "") + "\" role=\"checkbox\" tabindex=\"0\" aria-checked=\"" + (checked ? "true" : "false") + "\" " + (config.readonly ? "aria-readonly='true'" : "") + "\"";
	      var html = "<div class=\"webix_switch_box " + (checked ? " webix_switch_on" : "") + "\" style=\"width:" + common._switchWidth + "px\">" + "<span class=\"webix_switch_text\">" + ((checked ? config.onLabel : config.offLabel) || "") + "</span>" + "<button class=\"webix_switch_handle\" " + aria + " style=\"left:" + (checked ? common._switchWidth - common._switchHeight : 0) + "px;\">" + "<input  id=\"" + id + "\" class=\"webix_switch_toggle\" type=\"checkbox\" " + (checked ? "checked" : "") + "></button>" + "</div>" + rightlabel;
	      return common.$renderInput(config, html, id);
	    }
	  },
	  $skin: function () {
	    this._switchHeight = $active.switchHeight;
	    this._switchWidth = $active.switchWidth;
	  },
	  $setValue: function (value) {
	    var config = this._settings;
	    var checked = value == config.checkValue;
	    var box = this.$view.querySelector(".webix_switch_box");

	    if (box) {
	      var handle = box.childNodes[1];
	      var text = (checked ? config.onLabel : config.offLabel) || "";
	      if (checked) addCss(box, "webix_switch_on");else removeCss(box, "webix_switch_on");
	      handle.style.left = (checked ? this._switchWidth - this._switchHeight : 0) + "px";
	      handle.firstChild.checked = checked;
	      handle.setAttribute("aria-checked", checked ? "true" : "false");

	      if (text) {
	        box.childNodes[0].innerHTML = text;
	      }
	    }
	  },
	  _calck_switch_size: function () {
	    var config = this._settings;

	    if (config.onLabel || config.offLabel) {
	      var onWidth = config.onLabel ? getTextSize(config.onLabel, "webix_switch_text").width : 0;
	      var offWidth = config.onLabel ? getTextSize(config.offLabel, "webix_switch_text").width : 0;
	      this._switchWidth = Math.max(onWidth, offWidth) + this._switchHeight;
	    }
	  },
	  on_click: {
	    "webix_switch_box": function () {
	      if (!this._settings.readonly) this.toggle();
	    },
	    "webix_label_right": function () {
	      if (!this._settings.readonly) this.toggle();
	    }
	  }
	};
	var view$M = exports.protoUI(api$M, checkbox.view);

	var api$N = {
	  name: "tabbar",
	  $init: function () {
	    this.attachEvent("onKeyPress", this._onKeyPress);
	  },
	  $skin: function () {
	    var skin = $active;
	    var defaults = this.defaults;
	    defaults.topOffset = skin.tabTopOffset || 0;
	    defaults.tabOffset = typeof skin.tabOffset != "undefined" ? skin.tabOffset : 10;
	    defaults.bottomOffset = skin.tabBottomOffset || 0;
	    defaults.height = skin.tabbarHeight;
	    defaults.tabMargin = skin.tabMargin;
	    defaults.inputPadding = skin.inputPadding;
	    defaults.tabMinWidth = skin.tabMinWidth || 100;
	    defaults.tabMoreWidth = skin.tabMoreWidth || 40;
	    defaults.borderless = !skin.tabBorder;
	  },
	  _getTabbarSizes: function () {
	    var config = this._settings,
	        i,
	        len,
	        tabs = this._tabs || config.options,
	        totalWidth = this._input_width - config.tabOffset * 2,
	        limitWidth = config.optionWidth || config.tabMinWidth;
	    len = tabs.length;

	    if (config.tabMinWidth && totalWidth / len < limitWidth) {
	      return {
	        max: parseInt(totalWidth / limitWidth, 10) || 1
	      };
	    }

	    if (!config.optionWidth) {
	      for (i = 0; i < len; i++) {
	        if (tabs[i].width) {
	          totalWidth -= tabs[i].width + (!i && !config.type ? config.tabMargin : 0);
	          len--;
	        }
	      }
	    }

	    return {
	      width: len ? totalWidth / len : config.tabMinWidth
	    };
	  },
	  _init_popup: function () {
	    var obj = this._settings;

	    if (!obj.tabbarPopup) {
	      var popupConfig = {
	        view: "popup",
	        width: obj.popupWidth || 200,
	        body: {
	          view: "list",
	          borderless: true,
	          select: true,
	          css: "webix_tab_list",
	          autoheight: true,
	          yCount: obj.yCount,
	          type: {
	            template: obj.popupTemplate
	          }
	        }
	      };
	      var view = ui(popupConfig);
	      view.getBody().attachEvent("onBeforeSelect", bind(function (id) {
	        if (id && this.callEvent("onBeforeTabClick", [id])) {
	          this.setValue(id);
	          $$(this._settings.tabbarPopup).hide();
	          this.callEvent("onAfterTabClick", [id]);
	          return true;
	        }
	      }, this));
	      view.getBody().attachEvent("onAfterSelect", bind(function () {
	        this.refresh();
	      }, this));
	      obj.tabbarPopup = view._settings.id;

	      this._destroy_with_me.push(view);
	    }

	    this._init_popup = function () {};
	  },
	  getPopup: function () {
	    this._init_popup();

	    return $$(this._settings.tabbarPopup);
	  },
	  moreTemplate_setter: template,
	  popupTemplate_setter: template,
	  defaults: {
	    popupWidth: 200,
	    popupTemplate: "#value#",
	    yCount: 7,
	    moreTemplate: "<span class=\"webix_icon wxi-dots\"></span>",
	    template: function (obj, common) {
	      var contentWidth, html, i, leafWidth, resultHTML, style, sum, tabs, verticalOffset, width;
	      common._tabs = tabs = common._filterOptions(obj.options);

	      if (!tabs.length) {
	        html = "<div class='webix_tab_filler' style='width:" + common._input_width + "px; border-right:0px;'></div>";
	      } else {
	        common._check_options(tabs);

	        if (!obj.value && tabs.length) obj.value = tabs[0].id;
	        html = "";
	        if (obj.tabOffset) html += "<div class='webix_tab_filler' style='width:" + obj.tabOffset + "px;'>&nbsp;</div>";
	        contentWidth = common._input_width - obj.tabOffset * 2 - (!obj.type ? obj.tabMargin * (tabs.length - 1) : 0);
	        verticalOffset = obj.topOffset + obj.bottomOffset;

	        var sizes = common._getTabbarSizes();

	        if (sizes.max && sizes.max < tabs.length) {
	          //we need popup
	          var popup = common.getPopup();
	          popup.hide();
	          var list = popup.getBody() || null;

	          if (list) {
	            if (sizes.max) {
	              var found = false;

	              for (i = 0; i < tabs.length && !found; i++) {
	                if (tabs[i].id == obj.value) {
	                  found = true;

	                  if (i + 1 > sizes.max) {
	                    var selectedTab = tabs.splice(i, 1);
	                    var displayTabs = tabs.splice(0, sizes.max - 1).concat(selectedTab);
	                    tabs = displayTabs.concat(tabs);
	                  }
	                }
	              }

	              list.clearAll();
	              list.parse(tabs.slice(sizes.max));
	            } else {
	              list.clearAll();
	            }
	          }
	        } else if (common._settings.tabbarPopup) $$(common._settings.tabbarPopup).hide();

	        sum = obj.tabOffset;
	        var lastTab = false;

	        for (i = 0; i < tabs.length && !lastTab; i++) {
	          // tab width
	          if (sizes && sizes.max) {
	            if (sizes.max == i + 1) {
	              lastTab = true;
	            }

	            contentWidth = common._input_width - obj.tabOffset * 2 - (!obj.type && sizes.max > 1 ? obj.tabMargin * (sizes.max - 1) : 0);
	            width = (contentWidth - obj.tabMoreWidth) / sizes.max;
	          } else width = sizes.width;

	          width = tabs[i].width || obj.optionWidth || width;
	          sum += width + (i && !obj.type ? obj.tabMargin : 0);
	          if (obj.tabMargin > 0 && i && !obj.type) html += "<div class='webix_tab_filler' style='width:" + obj.tabMargin + "px;'></div>"; // tab innerHTML

	          html += common._getTabHTML(tabs[i], width);

	          if (lastTab) {
	            html += "<div role=\"button\" tabindex=\"0\" aria-label=\"" + i18n.aria.showTabs + "\" class=\"webix_tab_more_icon\" style=\"width:" + obj.tabMoreWidth + "px;\">" + obj.moreTemplate(obj, common) + "</div>";
	            sum += obj.tabMoreWidth;
	          }
	        }

	        leafWidth = common._content_width - sum;
	        if (leafWidth > 0 && !obj.type) html += "<div class='webix_tab_filler' style='width:" + leafWidth + "px;'>&nbsp;</div>";
	      }

	      resultHTML = ""; // consider top and bottom offset in tabs height (top tabbar)

	      style = verticalOffset && !obj.type ? "height:" + (common._content_height - verticalOffset) + "px" : ""; //space above tabs (top tabbar)

	      if (obj.topOffset && !obj.type) resultHTML += "<div class='webix_before_all_tabs' style='width:100%;height:" + obj.topOffset + "px'></div>"; // tabs html

	      resultHTML += "<div style='" + style + "' role='tablist' class='webix_all_tabs " + (obj.type ? "webixtype_" + obj.type : "") + "'>" + html + "</div>"; //space below to tabs (top tabbar)

	      if (obj.bottomOffset && !obj.type) resultHTML += "<div class='webix_after_all_tabs' style='width:100%;height:" + obj.bottomOffset + "px'></div>";
	      return resultHTML;
	    }
	  },
	  _getInputNode: function () {
	    return this.$view.querySelectorAll(".webix_item_tab");
	  },
	  _getTabHTML: function (tab, width) {
	    var html,
	        className = "",
	        tooltip = "",
	        config = this.config;
	    if (tab.id == config.value) className = " webix_selected";
	    if (tab.css) className += " " + tab.css;
	    if (config.tooltip) tooltip = " webix_t_id='" + tab.id + "'";
	    width = tab.width || width;
	    html = "<div class=\"webix_item_tab" + className + "\" " +
	    /*@attr*/
	    "button_id=\"" + tab.id + "\" role=\"tab\" aria-selected=\"" + (tab.id == config.value ? "true" : "false") + "\" tabindex=\"" + (tab.id == config.value ? "0" : "-1") + "\" style=\"width:" + width + "px;\"" + tooltip + ">"; // a tab title

	    if (this._tabTemplate) {
	      var calcHeight = this._content_height - config.inputPadding * 2 - 2;
	      var height = this._content_height - 2;
	      var temp = exports.extend({
	        cheight: calcHeight,
	        aheight: height
	      }, tab);
	      html += this._tabTemplate(temp);
	    } else {
	      var icon = tab.icon ? "<span class='webix_icon " + tab.icon + "'></span> " : "";
	      html += icon + tab.value;
	    }

	    if (tab.close || config.close) html += "<span role='button' tabindex='0' aria-label='" + i18n.aria.closeTab + "' class='webix_tab_close webix_icon wxi-close'></span>";
	    html += "</div>";
	    return html;
	  },
	  _getBox: function () {
	    return this._dataobj.firstChild;
	  },
	  _types: {
	    image: "<div class='webix_img_btn_top' style='height:#cheight#px;background-image:url(#image#);'><div class='webix_img_btn_text'>#value#</div></div>",
	    icon: "<div class='webix_img_btn' style='line-height:#cheight#px;height:#cheight#px;'><span class='webix_icon_btn #icon#' style='max-width:#cheight#px;max-height:#cheight#px;'></span>#value#</div>",
	    iconTop: "<div class='webix_img_btn_top' style='height:#cheight#px;width:100%;top:0px;text-align:center;'><span class='webix_icon #icon#'></span><div class='webix_img_btn_text'>#value#</div></div>"
	  },
	  type_setter: function (value) {
	    this._settings.tabOffset = 0;
	    if (this._types[value]) this._tabTemplate = template(this._types[value]);
	    return value;
	  }
	};
	var view$N = exports.protoUI(api$N, segmented.view);

	var api$O = {
	  name: "richtext",
	  defaults: {
	    label: "",
	    labelWidth: 80,
	    labelPosition: "left"
	  },
	  $init: function () {
	    this._viewobj.className += " webix_richtext";
	    this.$ready.unshift(this._setLayout);
	  },
	  getInputNode: function () {
	    return this.$view.querySelector(".webix_richtext_editor");
	  },
	  _button: function (name) {
	    return {
	      view: "toggle",
	      type: "iconButton",
	      icon: "wxi-" + name,
	      name: name,
	      id: name,
	      label: i18n.richtext[name],
	      autowidth: true,
	      action: name,
	      click: this._add_data
	    };
	  },
	  _setLayout: function () {
	    var top = this;
	    var editField = {
	      view: "template",
	      css: "webix_richtext_container",
	      borderless: true,
	      template: "<div class='webix_richtext_editor' contenteditable='true'>" + this.getValue() + "</div>",
	      on: {
	        onAfterRender: function () {
	          top._rendered_input = true;
	          top.refresh();

	          _event(top.getInputNode(), "blur", function () {
	            top._updateValue(this.innerHTML);
	          });

	          _event(top.getInputNode(), "keyup", function () {
	            top._getselection();
	          });
	        }
	      },
	      onClick: {
	        webix_richtext_editor: function () {
	          top._getselection();
	        }
	      }
	    };
	    var editorToolbar = {
	      view: "toolbar",
	      id: "toolbar",
	      elements: [this._button("underline"), this._button("bold"), this._button("italic"), {}]
	    };
	    var rows = [editorToolbar, editField];

	    if (this.config.labelPosition === "top" || !this.config.labelWidth) {
	      editorToolbar.elements.concat([{
	        view: "label",
	        label: this.config.label,
	        align: "right"
	      }, {
	        width: 4
	      }]);
	      this.rows_setter(rows);
	    } else {
	      this.config.borderless = true;
	      this.cols_setter([{
	        template: this.config.label || " ",
	        css: "webix_richtext_inp_label" + (this.config.required ? " webix_required" : ""),
	        borderless: true,
	        width: this.config.labelWidth
	      }, {
	        rows: rows
	      }]);
	    }
	  },
	  _getselection: function () {
	    var top = this;
	    var bar = top.$$("toolbar");
	    var sel;
	    bar.setValues({
	      italic: false,
	      underline: false,
	      bold: false
	    });

	    if (window.getSelection) {
	      sel = window.getSelection();
	    } else {
	      sel = document.selection.createRange();
	    }

	    for (var i = 0; i < sel.rangeCount; ++i) {
	      if (top.$view.contains(this.getInputNode())) {
	        if (document.queryCommandState("bold")) {
	          top.$$("bold").setValue(true);
	        }

	        if (document.queryCommandState("underline")) {
	          top.$$("underline").setValue(true);
	        }

	        if (document.queryCommandState("italic")) {
	          top.$$("italic").setValue(true);
	        }
	      }
	    }
	  },
	  refresh: function () {
	    if (this._rendered_input) this.getInputNode().innerHTML = this.config.value || "";
	  },
	  _execCommandOnElement: function (el, commandName) {
	    var sel, selText;

	    if (window.getSelection()) {
	      sel = window.getSelection();
	      selText = sel.toString().length;
	    } else {
	      sel = document.selection.createRange();
	      selText = sel.text.length;
	    }

	    if (selText > 0) {
	      for (var i = 0; i < sel.rangeCount; ++i) {
	        var range = sel.getRangeAt(i);

	        if (!sel.isCollapsed) {
	          document.execCommand(commandName, false, "");
	        } else {
	          var textValue = sel.focusNode.textContent;
	          var focusEl = sel.focusNode;
	          var focustext = sel.anchorOffset;
	          var wordBegining = textValue.substring(0, focustext).match(/[A-Za-z]*$/)[0];
	          var wordEnd = textValue.substring(focustext).match(/^[A-Za-z]*/)[0];
	          var startWord = focustext - wordBegining.length;
	          var endWord = focustext + wordEnd.length;
	          range.setStart(focusEl, startWord);
	          range.setEnd(focusEl, endWord);
	          sel.removeAllRanges();
	          window.getSelection().addRange(range);
	          document.execCommand(commandName, false, "");
	        }
	      }
	    }
	  },
	  _add_data: function () {
	    var top = this.getTopParentView();
	    var editableElement = top.getInputNode();

	    if (this.$view.contains(this.getInputNode())) {
	      top._execCommandOnElement(editableElement, this.config.action);
	    }
	  },
	  focus: function () {
	    if (!UIManager.canFocus(this)) return false;
	    var editableElement = this.$view.querySelector(".webix_richtext_editor");
	    editableElement.focus();
	  },
	  _updateValue: function (value) {
	    var old = this.config.value;
	    this.config.value = value || "";
	    if (old !== value) this.callEvent("onChange", [value, old]);
	  },
	  setValue: function (value) {
	    this._updateValue(value);

	    this.refresh();
	  },
	  getValue: function () {
	    var input = this.getInputNode();
	    if (input) this.config.value = this.getInputNode().innerHTML;
	    var value = this.config.value;
	    return value || (value === 0 ? "0" : "");
	  }
	};
	var view$O = exports.protoUI(api$O, IdSpace, layout.view);

	var api$P = {
	  name: "proto",
	  $init: function () {
	    this.data.provideApi(this, true);
	    this._dataobj = this._dataobj || this._contentobj; //render self , each time when data is updated

	    this.data.attachEvent("onStoreUpdated", bind(function () {
	      this.render.apply(this, arguments);
	    }, this));
	  },
	  $setSize: function () {
	    if (base.api.$setSize.apply(this, arguments)) this.render();
	  },
	  _id:
	  /*@attr*/
	  "webix_item",
	  on_mouse_move: {},
	  type: {}
	};
	var view$P = exports.protoUI(api$P, PagingAbility, DataMarks, AutoTooltip, ValidateCollection, RenderStack, DataLoader, base.view, EventSystem, Settings);
	var proto = {
	  api: api$P,
	  view: view$P
	};

	var api$Q = {
	  name: "list",
	  _listClassName: "webix_list",
	  _itemClassName: "webix_list_item",
	  $init: function (config) {
	    addCss(this._viewobj, this._listClassName + ((config.layout || this.defaults.layout) == "x" ? "-x" : ""));
	    this.data.provideApi(this, true);
	    this._auto_resize = bind(this._auto_resize, this);
	    this.data.attachEvent("onStoreUpdated", this._auto_resize);
	    this.data.attachEvent("onSyncApply", this._auto_resize);
	    this.attachEvent("onAfterRender", this._correct_width_scroll);

	    this._viewobj.setAttribute("role", "listbox");
	  },
	  dynamic_setter: function (value) {
	    if (value) exports.extend(this, VRenderStack, true);
	    return value;
	  },
	  $dragHTML: function (obj, e, context) {
	    var html;

	    if (this._settings.layout == "y" && this.type.width == "auto") {
	      this.type.width = this._content_width;
	      html = this._toHTML(obj);
	      this.type.width = "auto";
	    } else html = this._toHTML(obj);

	    if (isArray(context.source) && context.source.length > 1) html = this._toMultipleHTML(html, context.source.length);
	    return html;
	  },
	  defaults: {
	    select: false,
	    scroll: true,
	    layout: "y",
	    navigation: true,
	    datafetch: 50
	  },
	  _id:
	  /*@attr*/
	  "webix_l_id",
	  on_click: {
	    webix_list_item: function (e, id) {
	      if (this._settings.select) {
	        this._no_animation = true;
	        if (this._settings.select == "multiselect" || this._settings.multiselect) this.select(id, false, e.ctrlKey || e.metaKey || this._settings.multiselect == "touch", e.shiftKey); //multiselection
	        else this.select(id);
	        this._no_animation = false;
	      }
	    }
	  },
	  on_dblclick: {},
	  getVisibleCount: function () {
	    return Math.floor(this._content_height / this._one_height());
	  },
	  _auto_resize: function () {
	    if (this._settings.autoheight || this._settings.autowidth) this.resize();
	  },
	  _auto_height_calc: function (count) {
	    var value = this.data.$pagesize || this.count();

	    this._onoff_scroll(count && count < value, "y");

	    if (this._settings.autoheight && value < (count || Infinity)) count = value;
	    var height = this._one_height() * count + (this.type.margin || 0); //unitlist

	    if (this.getUnits) height += this.getUnits().length * this.type.headerHeight;
	    return Math.max(height, this._settings.minHeight || 0);
	  },
	  _one_height: function () {
	    return this.type.height + (this.type.margin || 0);
	  },
	  _auto_width_calc: function (count) {
	    var value = this.data.$pagesize || this.count();

	    this._onoff_scroll(count && count < value, "x");

	    if (this._settings.autowidth && value < (count || Infinity)) count = value;
	    return this.type.width * count;
	  },
	  _correct_width_scroll: function () {
	    if (this._settings.layout == "x") this._dataobj.style.width = this.type.width != "auto" ? this.type.width * this.count() + "px" : "auto";
	  },
	  $getSize: function (dx, dy) {
	    if (this._settings.layout == "y") {
	      if (this.type.width != "auto") this._settings.width = this.type.width + (this._scroll_y ? env.scrollSize : 0);
	      if (this._settings.yCount || this._settings.autoheight) this._settings.height = this._auto_height_calc(this._settings.yCount) || 1;
	    } else {
	      if (this.type.height != "auto") this._settings.height = this._one_height() + (this._scroll_x ? env.scrollSize : 0);
	      if (this._settings.xCount || this._settings.autowidth) this._settings.width = this._auto_width_calc(this._settings.xCount) || 1;
	    }

	    return base.api.$getSize.call(this, dx, dy);
	  },
	  $setSize: function () {
	    base.api.$setSize.apply(this, arguments);
	  },
	  type: {
	    css: "",
	    widthSize: function (obj, common) {
	      return common.width + (common.width > -1 ? "px" : "");
	    },
	    heightSize: function (obj, common) {
	      return common.height + (common.height > -1 ? "px" : "");
	    },
	    classname: function (obj, common, marks) {
	      var css = "webix_list_item";
	      if (common.css) css += " " + common.css;

	      if (obj.$css) {
	        if (_typeof(obj.$css) == "object") obj.$css = createCss(obj.$css);
	        css += " " + obj.$css;
	      }

	      if (marks && marks.$css) css += " " + marks.$css;
	      return css;
	    },
	    aria: function (obj, common, marks) {
	      return "role=\"option\"" + (marks && marks.webix_selected ? " aria-selected=\"true\" tabindex=\"0\"" : " tabindex=\"-1\"") + (obj.$count && obj.$template ? "aria-expanded=\"true\"" : "");
	    },
	    template: function (obj) {
	      return (obj.icon ? "<span class='webix_list_icon webix_icon " + obj.icon + "'></span>" : "") + obj.value + (obj.badge ? "<div class='webix_badge'>" + obj.badge + "</div>" : "");
	    },
	    width: "auto",
	    templateStart: template("<div " +
	    /*@attr*/
	    "webix_l_id" + "=\"#id#\" class=\"{common.classname()}\" style=\"width:{common.widthSize()}; height:{common.heightSize()}; overflow:hidden;\" {common.aria()}>"),
	    templateEnd: template("</div>")
	  },
	  $skin: function () {
	    this.type.height = $active.listItemHeight;
	  }
	};
	var view$Q = exports.protoUI(api$Q, CustomPrint, KeysNavigation, DataMove, DragItem, MouseEvents, SelectionModel, Scrollable, proto.view, CopyPaste);
	var list = {
	  api: api$Q,
	  view: view$Q
	};

	/*
		UI:Uploader
	*/
	// #include ui/view.js
	// #include ui/list.js

	type(list.view, {
	  name: "uploader",
	  template: "{common.removeIcon()}{common.percent()}<div style='float:right'>#sizetext#</div>{common.fileName()}",
	  percent: function (obj) {
	    if (obj.status == "transfer") return "<div style='width:60px; text-align:center; float:right'>" + obj.percent + "%</div>";
	    return "<div class='webix_upload_" + obj.status + "'><span class='" + (obj.status == "error" ? "error_icon" : "webix_icon wxi-check") + "'></span></div>";
	  },
	  removeIcon: function () {
	    return "<div class='webix_remove_upload'><span class='cancel_icon'></span></div>";
	  },
	  fileName: function (obj) {
	    return "<div style='text-overflow:ellipsis; white-space:nowrap; overflow:hidden; padding-right:8px;'>" + obj.name + "</div>";
	  },
	  on_click: {
	    "webix_remove_upload": function (ev, id) {
	      $$(this.config.uploader).files.remove(id);
	    }
	  }
	});
	var api$R = {
	  name: "uploader",
	  defaults: {
	    autosend: true,
	    multiple: true,
	    inputName: "upload"
	  },
	  $cssName: "button webix_uploader",
	  $allowsClear: true,
	  on_click: {
	    //don't fire extra onItemClick events, visible button will do it
	    "webix_hidden_upload": function () {
	      return false;
	    }
	  },
	  //will be redefined by upload driver
	  send: function () {},
	  fileDialog: function () {},
	  stopUpload: function () {},
	  $init: function () {
	    var driver = UploadDriver.html5;
	    this.files = new DataCollection();
	    this._destroy_with_me = [this.files]; // browser doesn't support XMLHttpRequest2

	    if (isUndefined(XMLHttpRequest) || isUndefined(new XMLHttpRequest().upload)) driver = UploadDriver.flash;
	    assert(driver, "incorrect driver");
	    exports.extend(this, driver, true);
	  },
	  $setSize: function (x, y) {
	    if (base.api.$setSize.call(this, x, y)) {
	      this.render();
	    }
	  },
	  apiOnly_setter: function (value) {
	    delay(this.render, this);
	    return this.$apiOnly = value;
	  },
	  _add_files: function (files) {
	    for (var i = 0; i < files.length; i++) {
	      this.addFile(files[i]);
	    }
	  },
	  link_setter: function (value) {
	    if (value) delay(function () {
	      var view = $$(this._settings.link);

	      if (!view) {
	        var top = this.getTopParentView();
	        if (top.$$) view = top.$$(this._settings.link);
	      }

	      if (view.sync && view.filter) view.sync(this.files);else if (view.setValues) this.files.data.attachEvent("onStoreUpdated", function () {
	        view.setValues(this);
	      });
	      view._settings.uploader = this._settings.id;
	    }, this);
	    return value;
	  },
	  addFile: function (name, size, type$$1, extra) {
	    var file = null;

	    if (_typeof(name) == "object") {
	      file = name;
	      name = file.name;
	      size = file.size;
	    }

	    var format = this._format_size(size);

	    type$$1 = type$$1 || name.split(".").pop();
	    var file_struct = {
	      file: file,
	      name: name,
	      id: uid(),
	      size: size,
	      sizetext: format,
	      type: type$$1,
	      context: this._last_file_context,
	      status: "client"
	    };
	    if (file && file.webkitRelativePath) file_struct.name = file.webkitRelativePath;
	    if (extra) exports.extend(file_struct, extra, true);

	    if (this.callEvent("onBeforeFileAdd", [file_struct])) {
	      if (!this._settings.multiple) this.files.clearAll();
	      var id = this.files.add(file_struct);
	      this.callEvent("onAfterFileAdd", [file_struct]);
	      if (id && this._settings.autosend) this.send(id);
	    }

	    return file_struct;
	  },
	  _get_active_url: function (item) {
	    var url = this._settings.upload;
	    var urldata = exports.extend(item.urlData || {}, this._settings.urlData || {});

	    if (url && urldata) {
	      var subline = [];

	      for (var key in urldata) {
	        subline.push(encodeURIComponent(key) + "=" + encodeURIComponent(urldata[key]));
	      }

	      if (subline.length) url += (url.indexOf("?") == -1 ? "?" : "&") + subline.join("&");
	    }

	    return url;
	  },
	  addDropZone: function (id, hover_text) {
	    var node = toNode(id);
	    var extra_css = "";
	    if (hover_text) extra_css = " " + createCss({
	      content: "\"" + hover_text + "\""
	    }, ":before");
	    var fullcss = "webix_drop_file" + extra_css;
	    var timer = null; //web

	    _event(node, "dragover", preventEvent);

	    _event(node, "dragover", function () {
	      addCss(node, fullcss, true);

	      if (timer) {
	        clearTimeout(timer);
	        timer = null;
	      }
	    });

	    _event(node, "dragleave", function () {
	      //when moving over html child elements
	      //browser will issue dragleave and dragover events
	      //ignore first one
	      timer = setTimeout(function () {
	        removeCss(node, fullcss);
	      }, 150);
	    });

	    _event(node, "drop", bind(function (e) {
	      removeCss(node, fullcss);

	      this._drop(e);

	      return preventEvent(e);
	    }, this));
	  },
	  _format_size: function (size) {
	    var index$$1 = 0;

	    while (size > 1024) {
	      index$$1++;
	      size = size / 1024;
	    }

	    return Math.round(size * 100) / 100 + " " + i18n.fileSize[index$$1];
	  },
	  _complete: function (id, response) {
	    var item = this.files.getItem(id);
	    exports.extend(item, response, true);
	    item.status = "server";
	    item.progress = 100;
	    this.callEvent("onFileUpload", [item, response]);
	    this.callEvent("onChange", []);
	    this.files.updateItem(id);
	    if (this.isUploaded()) this._upload_complete(response);
	  },
	  _upload_complete: function (response) {
	    this.callEvent("onUploadComplete", [response]);

	    if (this._last_assigned_upload_callback) {
	      this._last_assigned_upload_callback.call(this, response);

	      this._last_assigned_upload_callback = 0;
	    }
	  },
	  isUploaded: function () {
	    var order = this.files.data.order;

	    for (var i = 0; i < order.length; i++) {
	      if (this.files.getItem(order[i]).status != "server") return false;
	    }

	    return true;
	  },
	  $onUploadComplete: function () {},
	  $updateProgress: function (id, percent) {
	    var item = this.files.getItem(id);
	    item.percent = Math.round(percent);
	    this.files.updateItem(id);
	  },
	  setValue: function (value) {
	    if (typeof value == "string" && value) value = {
	      value: value,
	      status: "server"
	    };
	    this.files.clearAll();
	    if (value) this.files.parse(value);
	    this.callEvent("onChange", []);
	  },
	  getValue: function () {
	    var data = [];
	    this.files.data.each(function (obj) {
	      if (obj.status == "server") data.push(obj.value || obj.name);
	    });
	    return data.join(",");
	  }
	};
	var view$R = exports.protoUI(api$R, button$1.view);

	var api$S = {
	  name: "suggest",
	  defaults: {
	    autofocus: false,
	    type: "list",
	    keyPressTimeout: 1,
	    body: {
	      yCount: 10,
	      autoheight: true,
	      body: true,
	      select: true,
	      borderless: true,
	      navigation: true
	    },
	    filter: function (item, value) {
	      if (item.value.toString().toLowerCase().indexOf(value.toLowerCase()) === 0) return true;
	      return false;
	    }
	  },
	  template_setter: template,
	  filter_setter: function (value) {
	    return toFunctor(value, this.$scope);
	  },
	  _show_on_key_press: true,
	  $init: function (obj) {
	    var temp = {};
	    exports.extend(temp, copy(this.defaults.body));
	    temp.view = obj.type || this.defaults.type;

	    var etemp = this._get_extendable_cell(temp);

	    if (obj.body) exports.extend(etemp, obj.body, true);
	    if (obj.data) etemp.data = obj.data;
	    if (obj.url) etemp.url = obj.url;
	    if (obj.datatype) etemp.datatype = obj.datatype;
	    if (obj.id) temp.id = temp.id || obj.id + "_" + temp.view;
	    obj.body = temp;
	    this.$ready.push(this._set_on_popup_click);
	    this.attachEvent("onShow", function () {
	      if (this._settings.master) {
	        var master = $$(this._settings.master);

	        if (master) {
	          var node = master._getInputDiv ? master._getInputDiv() : master.getInputNode();
	          node.setAttribute("aria-expanded", "true");
	        }
	      }

	      this._show_selection();
	    });
	    this.attachEvent("onHide", function () {
	      if (this._settings.master) {
	        var master = $$(this._settings.master);

	        if (master) {
	          var node = master._getInputDiv ? master._getInputDiv() : master.getInputNode();
	          node.setAttribute("aria-expanded", "false");
	        }
	      }
	    });
	    this._old_text = {};
	  },
	  _get_extendable_cell: function (obj) {
	    return obj;
	  },
	  _preselectMasterOption: function (data) {
	    var master,
	        node,
	        text = data.id ? this.getItemText(data.id) : data.text || data.value;

	    if (this._settings.master) {
	      master = $$(this._settings.master);
	      node = master.getInputNode();

	      if (node && master.$setValueHere) {
	        master.$setValueHere(text);
	      } else if (node) {
	        if (master.options_setter) text = this.getItemText(data.id);else if (data.value) text = master._get_visible_text ? master._get_visible_text(data.value) : data.value.toString();
	        if (isUndefined(node.value)) node.innerHTML = text;else node.value = text.replace(/<[^>]*>/g, "");
	      }
	    }

	    node = node || this._last_input_target;
	    if (node) node.focus();
	  },
	  setMasterValue: function (data, refresh) {
	    var text = data.id ? this.getItemText(data.id) : data.text || data.value;

	    if (this._settings.master) {
	      var master = $$(this._settings.master);
	      if (refresh && data.id) master.refresh();else if (master.options_setter) master.setValue(data.$empty ? "" : data.id);else if (master.setValueHere) master.setValueHere(text);else master.setValue(text);
	    } else if (this._last_input_target) {
	      this._last_input_target.value = text;
	    }

	    if (!refresh) {
	      this.hide(true);
	      if (this._last_input_target) this._last_input_target.focus();
	    }

	    this.callEvent("onValueSuggest", [data, text]);
	    delay(function () {
	      callEvent("onEditEnd", []);
	    });
	  },
	  getMasterValue: function () {
	    if (this._settings.master) return $$(this._settings.master).getValue();
	    return null;
	  },
	  getItemId: function (text) {
	    var list = this.getList();

	    for (var key in list.data.pull) {
	      var obj = list.getItem(key);
	      if (this._settings.filter.call(this, obj, text)) return obj.id;
	    }
	  },
	  getItemText: function (id) {
	    var item = this.getList().getItem(id);
	    if (!item) return this._old_text[id] || "";
	    if (this._settings.template) return this._settings.template.call(this, item, this.type);
	    if (this._settings.textValue) return "" + item[this._settings.textValue] + "";
	    var type = this.getList().type;
	    var text = type.template.call(type, item, type);
	    return this._old_text[id] = text;
	  },
	  getSuggestion: function (text) {
	    var id,
	        list = this.getList(),
	        order = list.data.order;
	    if (list.getSelectedId) id = list.getSelectedId();

	    if (text && order.length && (!id || order.find(id) < 0)) {
	      id = order[0]; //ensure that option really does match client-side filtering rules

	      if (!list.config.dataFeed && !this.config.filter.call(this, list.data.pull[id], text)) return null;
	    } //complex id in datatable


	    if (id && _typeof(id) == "object") id = id + "";
	    return id;
	  },
	  getList: function () {
	    return this._body_cell;
	  },
	  _set_on_popup_click: function () {
	    var list = this.getList();
	    var type = this._settings.type;

	    if (list.count) {
	      list.attachEvent("onItemClick", bind(function (item) {
	        this.setMasterValue(list.getItem(item));
	      }, this));
	      list.data.attachEvent("onstoreupdated", bind(function (id, obj, mode) {
	        if (mode == "delete" && id == this.getMasterValue()) this.setMasterValue({
	          id: "",
	          text: ""
	        }, 1);else if (mode == "update" && id == this.getMasterValue()) {
	          this.setMasterValue(obj, 1);
	        }
	      }, this));
	      list.data.attachEvent("onAfterFilter", bind(this._suggest_after_filter, this));
	      list.data.attachEvent("onStoreLoad", bind(this._suggest_after_filter, this));
	      if (isUndefined(this._settings.fitMaster)) this._settings.fitMaster = true;
	    } else if (type == "calendar") {
	      list.attachEvent("onDateSelect", function () {
	        this.getParentView().setMasterValue({
	          value: list.getSelectedDate()
	        }, list.config.multiselect);
	      });
	      list.attachEvent("onTodaySet", function (date) {
	        this.getParentView().setMasterValue({
	          value: date
	        });
	      });
	      list.attachEvent("onDateClear", function (date) {
	        this.getParentView().setMasterValue({
	          value: date
	        });
	      });
	    } else if (type == "colorboard") {
	      list.attachEvent("onItemClick", function (value) {
	        this.getParentView().setMasterValue({
	          value: value
	        });
	      });
	    }
	  },
	  input_setter: function (value) {
	    this.linkInput(value);
	    return 0;
	  },
	  linkInput: function (input) {
	    var node;

	    if (input.getInputNode) {
	      node = input.getInputNode();
	      node.webix_master_id = input._settings.id;
	    } else node = toNode(input);

	    _event(node, "keydown", function (e) {
	      if ((node != document.body || this.isVisible()) && (input.config ? !input.config.readonly : !node.getAttribute("readonly"))) this._suggestions(e);
	    }, {
	      bind: this
	    });

	    if (input._getInputDiv) node = input._getInputDiv();
	    node.setAttribute("aria-autocomplete", "list");
	    node.setAttribute("aria-expanded", "false");

	    if (node.tagName === "DIV") {
	      node.setAttribute("aria-live", "assertive");
	      node.setAttribute("aria-atomic", "true");
	    }

	    this._non_ui_mode = true;
	  },
	  _suggestions: function (e) {
	    e = e || event;
	    var list = this.getList();
	    var trg = e.target || e.srcElement;
	    if (trg == document.body && !this.isVisible() || trg.className == "webix_clipbuffer") return;
	    this._last_input_target = trg;
	    this._settings.master = trg.webix_master_id;
	    window.clearTimeout(this._key_timer);
	    var code = e.keyCode; //shift and ctrl

	    if (code == 16 || code == 17) return; // tab - hide popup and do nothing

	    if (code == 9) return this._tab_key(this, list); // escape - hide popup

	    if (code == 27) return this._escape_key(this, list); // enter

	    if (code == 13) {
	      if (this.isVisible()) preventEvent(e);
	      return this.$enterKey(this, list);
	    } // up/down/right/left are used for navigation


	    if (this._navigate(e) && this.isVisible()) {
	      preventEvent(e);
	      return false;
	    }

	    if (isUndefined(trg.value)) return;
	    clearTimeout(this._last_delay);
	    this._last_delay = delay(function () {
	      //focus moved to the different control, suggest is not necessary
	      if (!this._non_ui_mode && UIManager.getFocus() != $$(this._settings.master)) return;
	      this._resolve_popup = true; //for multicombo

	      var val = trg.value; // used to prevent showing popup when it was initialized

	      if (list.config.dataFeed) list.filter("value", val);else if (list.filter) {
	        list.filter(bind(function (item) {
	          return this._settings.filter.call(this, item, val);
	        }, this));
	      }
	    }, this, [], this._settings.keyPressTimeout);
	  },
	  _suggest_after_filter: function () {
	    if (!this._resolve_popup) return true;
	    this._resolve_popup = false;
	    var list = this.getList(); // filtering is complete
	    // if there are as min 1 variant it must be shown, hidden otherwise

	    if (list.count() > 0) {
	      this.adjust();
	      if (!this.isVisible()) this._dont_unfilter = true;
	      this.show(this._last_input_target, null, true);
	      this._dont_unfilter = false;
	    } else {
	      this.hide(true);
	      this._last_input_target = null;
	    }
	  },
	  show: function (node) {
	    if (!this.isVisible()) {
	      var list = this.getList();

	      if (list.filter && !this._dont_unfilter) {
	        list.filter("");
	      }

	      if (this.$customWidth) {
	        this.$customWidth(node);
	      } else if (node.tagName && this._settings.fitMaster) {
	        this._settings.width = node.offsetWidth - 2; //2 - borders
	      }

	      if (list._zoom_level) list.render();
	      this.adjust(); // needed to return focus

	      if (node.tagName == "INPUT") this._last_input_target = node;
	    }

	    popup.api.show.apply(this, arguments);
	  },
	  _show_selection: function (list) {
	    list = list || this.getList();
	    var value = this.getMasterValue();

	    if (list.select && list.showItem) {
	      if (value && list.exists && list.exists(value)) {
	        list.select(value);
	        list.showItem(value);
	      } else {
	        list.unselect();
	        list.showItem(list.getFirstId());
	      }
	    } else if (list.setValue) {
	      if (this._settings.master) value = $$(this._settings.master).$prepareValue(value);
	      list.setValue(value);
	    }
	  },
	  $enterKey: function (popup$$1, list) {
	    var value;

	    if (popup$$1.isVisible()) {
	      if (list.count && list.count()) {
	        value = list.getSelectedId(false, true);
	        if (list.count() == 1 && list.getFirstId() != value) value = list.getFirstId();
	        if (value) value = list.getItem(value);
	      } else if (list.getSelectedDate && list.getSelectedDate()) value = {
	        value: list.getSelectedDate()
	      };else if (list.getValue && list.getValue()) value = {
	        value: list.getValue()
	      };

	      if (value) this.setMasterValue(value);
	      popup$$1.hide(true);
	    } else if (this._show_on_key_press) popup$$1.show(this._last_input_target);
	  },
	  _escape_key: function (popup$$1) {
	    return popup$$1.hide(true);
	  },
	  _tab_key: function (popup$$1) {
	    return popup$$1.hide(true);
	  },

	  /*! suggestions navigation: up/down buttons move selection
	   *	@param e
	   *		event object
	   **/
	  _navigate: function (e) {
	    var list = this.getList();
	    var code = e.keyCode;
	    var data;

	    if (list.moveSelection && code < 41 && code > 32 && !e.ctrlKey && !e.metaKey && !e.shiftKey && !e.altKey) {
	      // down and up arrows
	      if (code === 40 || code === 38) {
	        if (this._show_on_key_press && !this.isVisible()) this.show(this._last_input_target);
	        var dir = code === 38 ? "up" : "down";
	        list.moveSelection(dir, false, false);
	      } // other arrows
	      else {
	          if (list.count || !list.count && !list.isVisible()) return false;

	          var _dir;

	          if (code == 33) _dir = "pgup";
	          if (code == 34) _dir = "pgdown";
	          if (code == 35) _dir = "bottom";
	          if (code == 36) _dir = "top";
	          if (code == 37) _dir = "left";
	          if (code == 39) _dir = "right";
	          list.moveSelection(_dir, false, false);
	        }

	      if (list.count) data = list.getSelectedItem();else if (list.getSelectedDate) data = {
	        value: list.getVisibleDate()
	      };else if (list.getValue) data = {
	        value: list.getValue()
	      };
	      if (this.isVisible()) this._preselectMasterOption(data);
	      return true;
	    }

	    return false;
	  },
	  getValue: function () {
	    var list = this.getList();
	    var value = (list.getValue ? list.getValue() : list.getSelectedId()) || "";
	    value = value.id || value; // check empty

	    if (list.getItem) {
	      var item = list.getItem(value);
	      if (item && item.$empty) return "";
	    }

	    return value;
	  },
	  setValue: function (value) {
	    var list = this.getList();

	    if (value) {
	      if (list.exists(value)) {
	        list.select(value);
	        list.showItem(value);
	      }
	    } else {
	      list.unselect();
	      list.showItem(list.getFirstId());
	    }
	  }
	};
	var view$S = exports.protoUI(api$S, popup.view);
	var suggest = {
	  api: api$S,
	  view: view$S
	};

	exports.protoUI({
	  name: "multisuggest",
	  defaults: {
	    template: "GPL version doesn't support multisuggest <br> You need Webix PRO"
	  }
	}, template$1.view);

	exports.protoUI({
	  name: "checksuggest",
	  defaults: {
	    template: "GPL version doesn't support checksuggest <br> You need Webix PRO"
	  }
	}, template$1.view);

	exports.protoUI({
	  name: "datasuggest",
	  defaults: {
	    template: "GPL version doesn't support datasuggest <br> You need Webix PRO"
	  }
	}, template$1.view);

	exports.protoUI({
	  name: "gridsuggest",
	  defaults: {
	    template: "GPL version doesn't support gridsuggest <br> You need Webix PRO"
	  }
	}, template$1.view);

	exports.protoUI({
	  name: "daterange",
	  defaults: {
	    template: "GPL version doesn't support daterange <br> You need Webix PRO"
	  }
	}, template$1.view);

	var api$T = {
	  name: "daterangesuggest",
	  defaults: {
	    type: "daterange",
	    body: {
	      view: "daterange",
	      icons: true,
	      button: true,
	      borderless: true
	    }
	  },
	  getValue: function () {
	    return this.getRange().getValue();
	  },
	  setValue: function (value) {
	    this.getRange().setValue(copy(value));
	  },
	  getRange: function () {
	    return this.getBody();
	  },
	  getButton: function () {
	    return this.getBody().getChildViews()[1].getChildViews()[1];
	  },
	  _setValue: function (value, hide) {
	    var master = $$(this._settings.master);

	    if (master) {
	      master.setValue(value);
	      if (hide) this.hide();
	    } else this.setValue(value);
	  },
	  _set_on_popup_click: function () {
	    var range = this.getRange();
	    range.attachEvent("onAfterDateSelect", bind(function (value) {
	      this._setValue(value);
	    }, this));
	    range.attachEvent("onDateClear", bind(function (value) {
	      this._setValue(value);
	    }, this));
	    range.attachEvent("onTodaySet", bind(function (value) {
	      this._setValue(value);
	    }, this));
	  }
	};
	var view$T = exports.protoUI(api$T, suggest.view);

	var api$U = {
	  $cssName: "datepicker",
	  name: "daterangepicker",
	  $init: function (config) {
	    //set non-empty initial value
	    this._settings.value = {}; // other types are not implemented

	    delete config.type;
	  },
	  _init_popup: function () {
	    var obj = this._settings;
	    if (obj.suggest) obj.popup = obj.suggest;else if (!obj.popup) {
	      obj.popup = obj.suggest = this.suggest_setter({
	        view: "daterangesuggest",
	        body: {
	          timepicker: obj.timepicker,
	          calendarCount: obj.calendarCount,
	          height: 250 + (obj.button || obj.icons ? 30 : 0)
	        }
	      });
	    }

	    this._init_once = function () {};
	  },
	  $prepareValue: function (value) {
	    value = value || {};
	    value.start = datepicker.api.$prepareValue.call(this, value.start ? value.start : null);
	    value.end = datepicker.api.$prepareValue.call(this, value.end ? value.end : null);
	    var daterange = $$(this._settings.popup).getRange();
	    return copy(daterange._correct_value(value));
	  },
	  $compareValue: function (oldvalue, value) {
	    var compare = datepicker.api.$compareValue;
	    var start = compare.call(this, oldvalue.start, value.start);
	    var end = compare.call(this, oldvalue.end, value.end);
	    return start && end;
	  },
	  $setValue: function (value) {
	    value = value || {};
	    this._settings.text = (value.start ? this._get_visible_text(value.start) : "") + (value.end ? " - " + this._get_visible_text(value.end) : "");

	    this._set_visible_text();
	  },
	  $render: function (obj) {
	    obj.value = this.$prepareValue(obj.value);
	    this.$setValue(obj.value);
	  },
	  getValue: function () {
	    var value = this._settings.value;

	    if (this._settings.stringResult) {
	      var formatStr = i18n.parseFormatStr;
	      return this._formatValue(formatStr, value);
	    }

	    return value || null;
	  },
	  _formatValue: function (format, value) {
	    if (value.start) value.start = format(value.start);
	    if (value.end) value.end = format(value.end);
	    return value;
	  }
	};
	var view$U = exports.protoUI(api$U, datepicker.view);

	exports.protoUI({
	  name: "excelbar",
	  defaults: {
	    template: "GPL version doesn't support excelbar <br> You need Webix PRO"
	  }
	}, template$1.view);

	exports.protoUI({
	  name: "excelviewer",
	  defaults: {
	    template: "GPL version doesn't support excelviewer <br> You need Webix PRO"
	  }
	}, template$1.view);

	exports.protoUI({
	  name: "pdfbar",
	  defaults: {
	    template: "GPL version doesn't support pdfbar <br> You need Webix PRO"
	  }
	}, template$1.view);

	exports.protoUI({
	  name: "pdfviewer",
	  defaults: {
	    template: "GPL version doesn't support pdfviewer <br> You need Webix PRO"
	  }
	}, template$1.view);

	/*
		UI:Video
	*/
	// #include ui/view.js

	var api$V = {
	  name: "video",
	  $init: function (config) {
	    if (!config.id) config.id = uid();
	    this.$ready.push(this._init_video);
	  },
	  _init_video: function () {
	    var c = this._settings;
	    this._contentobj = create("video", {
	      "class": "webix_view_video",
	      "style": "width:100%;height:100%;",
	      "autobuffer": "autobuffer"
	    }, "");
	    if (c.poster) this._contentobj.poster = c.poster;

	    if (c.src) {
	      if (_typeof(c.src) != "object") c.src = [c.src];

	      for (var i = 0; i < c.src.length; i++) {
	        this._contentobj.innerHTML += " <source src=\"" + c.src[i] + "\">";
	      }
	    }

	    if (c.controls) this._contentobj.controls = true;
	    if (c.autoplay) this._contentobj.autoplay = true;

	    this._viewobj.appendChild(this._contentobj);
	  },
	  getVideo: function () {
	    return this._contentobj;
	  },
	  defaults: {
	    src: "",
	    controls: true
	  }
	};
	var view$V = exports.protoUI(api$V, base.view);

	exports.protoUI({
	  name: "gage",
	  defaults: {
	    template: "GPL version doesn't support gage <br> You need Webix PRO"
	  }
	}, template$1.view);

	exports.protoUI({
	  name: "barcode",
	  defaults: {
	    template: "GPL version doesn't support barcode <br> You need Webix PRO"
	  }
	}, template$1.view);

	exports.protoUI({
	  name: "bulletgraph",
	  defaults: {
	    template: "GPL version doesn't support bulletgraph <br> You need Webix PRO"
	  }
	}, template$1.view);

	var google, script;
	var api$W = {
	  name: "geochart",
	  defaults: {
	    chart: {
	      displayMode: "auto",
	      region: "world",
	      resolution: "countries"
	    }
	  },
	  $init: function (config) {
	    this.$view.innerHTML = "<div class='webix_map_content' style='width:100%;height:100%'></div>";
	    this._contentobj = this.$view.firstChild;
	    this._waitMap = Deferred.defer();
	    config.chart = exports.extend(config.chart || {}, this.defaults.chart);
	    this.data.provideApi(this, true);
	    this.$ready.push(this.render);
	    this.data.attachEvent("onClearAll", bind(this._refreshColumns, this));
	    this.data.attachEvent("onStoreUpdated", bind(this._drawData, this));
	  },
	  getMap: function (waitMap) {
	    return waitMap ? this._waitMap : this._map;
	  },
	  _getCallBack: function (prev) {
	    return bind(function () {
	      if (typeof prev === "function") prev();
	      google = google || window.google;

	      this._initMap();
	    }, this);
	  },
	  render: function () {
	    if (typeof window.google == "undefined" || typeof window.google.charts == "undefined") {
	      if (!script) {
	        script = document.createElement("script");
	        script.type = "text/javascript";
	        script.src = "//www.gstatic.com/charts/loader.js";
	        document.getElementsByTagName("head")[0].appendChild(script);
	      }

	      script.onload = this._getCallBack(script.onload);
	    } else //there's a custom link to google api in document head
	      this._getCallBack()();
	  },
	  _initMap: function () {
	    if (!google.visualization || !google.visualization.GeoChart) {
	      google.charts.load("current", {
	        "packages": ["geochart"],
	        "mapsApiKey": this._settings.key
	      });
	      google.charts.setOnLoadCallback(bind(function () {
	        this._initMap();
	      }, this));
	    } else {
	      this._map = new google.visualization.GeoChart(this._contentobj);

	      this._mapEvents();

	      this._waitMap.resolve(this._map);
	    }
	  },
	  $onLoad: function (obj) {
	    if (!this._map) {
	      this._waitMap.then(bind(function () {
	        this.parse(obj, this._settings.datatype);
	      }, this));

	      return true;
	    }

	    return false;
	  },
	  _drawData: function () {
	    if (!this._map) {
	      if (!this._map) this._waitMap.then(bind(this._drawData, this));
	      return;
	    }

	    var columns = this._columns && this._columns.length ? this._columns : this._defineColumns();
	    var data = [];
	    this.data.each(function (obj) {
	      var line = [];

	      for (var c = 0; c < columns.length; c++) {
	        var value = obj[columns[c].label];
	        if (columns[c].type == "number") value = value * 1;else if (columns[c].role == "tooltip") value = this._settings.tooltip(obj);
	        line.push(value);
	      }

	      data.push(line);
	    }, this);

	    if (columns.length) {
	      var table = new google.visualization.DataTable();

	      for (var i = 0; i < columns.length; i++) {
	        table.addColumn(columns[i]);
	      }

	      table.addRows(data);
	      var view = new google.visualization.DataView(table);

	      this._map.draw(view, this._settings.chart);
	    } else //draw clean chart
	      this._map.draw(google.visualization.arrayToDataTable([["", ""]]), {});
	  },
	  setDisplayMode: function (value) {
	    this._settings.chart.displayMode = value;
	    this.refresh();
	  },
	  setRegion: function (value) {
	    this._settings.chart.region = value;
	    this.refresh();
	  },
	  refresh: function () {
	    this._map.clearChart();

	    this._drawData();
	  },
	  tooltip_setter: function (value) {
	    var tooltip = this._settings.chart.tooltip;
	    this._settings.chart.tooltip = exports.extend(tooltip || {}, {
	      isHtml: true
	    });
	    return template(value);
	  },
	  $setSize: function (w, h) {
	    if (base.api.$setSize.apply(this, arguments) && this._map) {
	      exports.extend(this._settings, {
	        width: w,
	        height: h
	      });
	      this.refresh();
	    }
	  },
	  _refreshColumns: function () {
	    this._columns = null;

	    this._drawData();
	  },
	  _getColumnType: function (item, key) {
	    if (!item || isUndefined(item[key])) return "string";

	    var type = _typeof(item[key]);

	    if (type == "string" && !isNaN(item[key] * 1)) type = "number";
	    return type;
	  },
	  _defineColumns: function () {
	    var columns = this._settings.columns || [];
	    var item = this.data.pull[this.data.order[0]]; //auto columns

	    if (!columns.length && item) {
	      for (var key in item) {
	        if (key !== "id") columns.push(key);
	      }
	    } //["title", "area"]


	    for (var i = 0; i < columns.length; i++) {
	      if (_typeof(columns[i]) !== "object") {
	        columns[i] = {
	          type: this._getColumnType(item, columns[i]),
	          label: columns[i]
	        };
	      }
	    }

	    if (this._settings.tooltip) columns.push({
	      type: "string",
	      role: "tooltip",
	      p: {
	        "html": true
	      }
	    });
	    this._columns = columns;
	    return columns;
	  },
	  _mapEvents: function () {
	    google.visualization.events.addListener(this._map, "error", bind(function () {
	      this.callEvent("onMapError", arguments);
	    }, this));
	    google.visualization.events.addListener(this._map, "ready", bind(function () {
	      this.callEvent("onMapReady", arguments);
	    }, this));
	    google.visualization.events.addListener(this._map, "regionClick", bind(function () {
	      this.callEvent("onRegionClick", arguments);
	    }, this));
	    google.visualization.events.addListener(this._map, "select", bind(function () {
	      var selnow = this._map.getSelection()[0];

	      var sel = selnow || this._selprev;

	      if (sel) {
	        var id = this.data.order[sel.row];
	        this._selprev = sel;
	        this.callEvent("onItemClick", [id, !!selnow]);
	      }
	    }, this));
	  }
	};
	var view$W = exports.protoUI(api$W, DataLoader, EventSystem, base.view);

	var google$1, script$1;
	var api$X = {
	  name: "google-map",
	  $init: function () {
	    this.$view.innerHTML = "<div class='webix_map_content' style='width:100%;height:100%'></div>";
	    this._contentobj = this.$view.firstChild;
	    this._waitMap = Deferred.defer();
	    this.data.provideApi(this, true);
	    this.$ready.push(this.render);
	  },
	  getMap: function (waitMap) {
	    return waitMap ? this._waitMap : this._map;
	  },
	  _getCallBack: function (prev) {
	    return bind(function () {
	      if (typeof prev === "function") prev();
	      google$1 = google$1 || window.google;

	      this._initMap.call(this);
	    }, this);
	  },
	  render: function () {
	    if (typeof window.google == "undefined" || typeof window.google.maps == "undefined") {
	      if (!script$1) {
	        script$1 = document.createElement("script");
	        script$1.type = "text/javascript";
	        var config = this._settings;
	        var src = config.src || "//maps.google.com/maps/api/js";
	        src += src.indexOf("?") === -1 ? "?" : "&";
	        if (config.key) src += "&key=" + config.key;
	        if (config.libraries) src += "&libraries=" + config.libraries;
	        script$1.src = src;
	        document.getElementsByTagName("head")[0].appendChild(script$1);
	      }

	      script$1.onload = this._getCallBack(script$1.onload);
	    } else //there's a custom link to google api in document head
	      this._getCallBack()();
	  },
	  _initMap: function () {
	    var c = this.config;

	    if (this.isVisible(c.id)) {
	      this._map = new google$1.maps.Map(this._contentobj, {
	        zoom: c.zoom,
	        center: new google$1.maps.LatLng(c.center[0], c.center[1]),
	        mapTypeId: google$1.maps.MapTypeId[c.mapType]
	      });

	      this._waitMap.resolve(this._map);

	      this._contentobj.firstChild.setAttribute(
	      /*@attr*/
	      "webix_disable_drag", "true");
	    }
	  },
	  center_setter: function (config) {
	    if (this._map) this._map.setCenter(new google$1.maps.LatLng(config[0], config[1]));
	    return config;
	  },
	  mapType_setter: function (config) {
	    /*ROADMAP,SATELLITE,HYBRID,TERRAIN*/
	    if (this._map) this._map.setMapTypeId(google$1.maps.MapTypeId[config]);
	    return config;
	  },
	  zoom_setter: function (config) {
	    if (this._map) this._map.setZoom(config);
	    return config;
	  },
	  layerType_setter: function (config) {
	    if (config == "heatmap") this.config.libraries = "visualization";

	    if (this._layerApi[config]) {
	      exports.extend(this, this._layerApi[config], true);
	      this.data.attachEvent("onStoreUpdated", bind(function () {
	        this._waitMap.then(bind(function () {
	          this.drawData.call(this, arguments);
	        }, this));
	      }, this));
	    }

	    return config;
	  },
	  defaults: {
	    zoom: 5,
	    center: [39.5, -98.5],
	    mapType: "ROADMAP",
	    layerType: "marker"
	  },
	  $setSize: function () {
	    base.api.$setSize.apply(this, arguments);
	    if (this._map) google$1.maps.event.trigger(this._map, "resize");
	  },
	  $onLoad: function (data) {
	    if (!this._map) {
	      this._waitMap.then(bind(function () {
	        this.parse(data);
	      }, this));

	      return true;
	    }

	    return false;
	  },
	  _layerApi: {
	    marker: {
	      drawData: function (id, item, operation) {
	        switch (operation) {
	          case "add":
	            item.$marker = this._getItemConfig(item);
	            break;

	          case "update":
	            item.$marker = this._getItemConfig(item);
	            break;

	          case "delete":
	            item.$marker.setMap(null);
	            break;

	          default:
	            this.data.each(function (item) {
	              item.$marker = this._getItemConfig(item);
	            }, this);
	            break;
	        }
	      },
	      clearAll: function (soft) {
	        this.data.each(function (obj) {
	          obj.$marker.setMap(null);
	        });
	        this.data.clearAll(soft);
	      },
	      showItem: function (id) {
	        var item = this.getItem(id);

	        this._map.setCenter(new google$1.maps.LatLng(item.lat, item.lng));
	      },
	      _getItemConfig: function (item) {
	        var obj = {};

	        for (var i in item) {
	          obj[i] = item[i];
	        }

	        obj.position = new google$1.maps.LatLng(item.lat, item.lng);
	        obj.map = item.hidden ? null : this._map;
	        var marker = item.$marker;

	        if (!marker) {
	          marker = new google$1.maps.Marker(obj);

	          this._events(marker);
	        } else {
	          item.$marker.setMap(obj.map);
	        }

	        this.callEvent("onItemRender", [item]);
	        return marker;
	      },
	      _events: function (marker) {
	        var map = this;
	        marker.addListener("click", function () {
	          map.callEvent("onItemClick", [this.id, this]);
	        });

	        if (marker.getDraggable()) {
	          marker.addListener("dragend", function () {
	            map._onDrag(this, true);
	          });
	          marker.addListener("drag", function () {
	            map._onDrag(this);
	          });
	        }
	      },
	      _onDrag: function (marker, end) {
	        var item = this.getItem(marker.id);
	        var pos = marker.getPosition();
	        var ev = end ? "onAfterDrop" : "onDrag";
	        item.lat = pos.lat();
	        item.lng = pos.lng();
	        this.callEvent(ev, [item.id, item]);
	      }
	    },
	    heatmap: {
	      heatmapConfig_setter: function (value) {
	        value = value || {};
	        return value;
	      },
	      drawData: function () {
	        if (this._heatmap) {
	          this._heatmap.setMap(null);

	          this._heatmap = null;
	        }

	        var hdata = [];
	        this.data.each(function (item) {
	          hdata.push(this._getConfig(item));
	        }, this);

	        if (hdata.length) {
	          var data = exports.extend(this.config.heatmapConfig, {
	            data: hdata,
	            map: this._map
	          }, true);
	          this._heatmap = new google$1.maps.visualization.HeatmapLayer(data);
	          this.callEvent("onHeatMapRender", [this._heatmap]);
	        }
	      },
	      getHeatmap: function () {
	        return this._heatmap;
	      },
	      _getConfig: function (item) {
	        var obj = {};

	        for (var i in item) {
	          obj[i] = item[i];
	        }

	        obj.location = new google$1.maps.LatLng(item.lat, item.lng);
	        return obj;
	      }
	    }
	  }
	};
	var view$X = exports.protoUI(api$X, DataLoader, EventSystem, base.view);

	exports.protoUI({
	  name: "organogram",
	  defaults: {
	    template: "GPL version doesn't support organogram <br> You need Webix PRO"
	  }
	}, template$1.view);

	var Pie$1 = {
	  $render_pie: function (ctx, data, x, y, sIndex, map) {
	    this._renderPie(ctx, data, x, y, 1, map, sIndex);
	  },

	  /**
	   *   renders a pie chart
	   *   @param: ctx - canvas object
	   *   @param: data - object those need to be displayed
	   *   @param: x - the width of the container
	   *   @param: y - the height of the container
	   *   @param: ky - value from 0 to 1 that defines an angle of inclination (0<ky<1 - 3D chart)
	   */
	  _renderPie: function (ctx, data, point0, point1, ky, map, sIndex) {
	    if (!data.length) return;

	    var coord = this._getPieParameters(point0, point1);
	    /*pie radius*/


	    var radius = this._settings.radius ? this._settings.radius : coord.radius;
	    if (radius < 0) return;
	    /*real values*/

	    var values = this._getValues(data);

	    var totalValue = this._getTotalValue(values);
	    /*weighed values (the ratio of object value to total value)*/


	    var ratios = this._getRatios(values, totalValue);
	    /*pie center*/


	    var x0 = this._settings.x ? this._settings.x : coord.x;
	    var y0 = this._settings.y ? this._settings.y : coord.y;
	    /*adds shadow to the 2D pie*/

	    if (ky == 1 && this._settings.shadow) this._addShadow(ctx, x0, y0, radius);
	    /*changes vertical position of the center according to 3Dpie cant*/

	    y0 = y0 / ky;
	    /*the angle defines the 1st edge of the sector*/

	    var alpha0 = -Math.PI / 2;
	    var angles = [];
	    /*changes Canvas vertical scale*/

	    ctx.scale(1, ky);
	    /*adds radial gradient to a pie*/

	    if (this._settings.gradient) {
	      var x1 = ky != 1 ? x0 + radius / 3 : x0;
	      var y1 = ky != 1 ? y0 + radius / 3 : y0;

	      this._showRadialGradient(ctx, x0, y0, radius, x1, y1);
	    }

	    for (var i = 0; i < data.length; i++) {
	      if (!values[i]) continue;
	      /*drawing sector*/
	      //ctx.lineWidth = 2;

	      ctx.strokeStyle = this._settings.lineColor.call(this, data[i]);
	      ctx.beginPath();
	      ctx.moveTo(x0, y0);
	      angles.push(alpha0);
	      /*the angle defines the 2nd edge of the sector*/

	      var alpha1 = -Math.PI / 2 + ratios[i] - 0.0001;
	      ctx.arc(x0, y0, radius, alpha0, alpha1, false);
	      ctx.lineTo(x0, y0);

	      var color = this._settings.color.call(this, data[i]);

	      ctx.fillStyle = color;
	      ctx.fill();
	      /*text that needs being displayed inside the sector*/

	      if (this._settings.pieInnerText) this._drawSectorLabel(x0, y0, 5 * radius / 6, alpha0, alpha1, ky, this._settings.pieInnerText(data[i], totalValue), true);
	      /*label outside the sector*/

	      if (this._settings.label) this._drawSectorLabel(x0, y0, radius + this._settings.labelOffset, alpha0, alpha1, ky, this._settings.label(data[i]));
	      /*drawing lower part for 3D pie*/

	      if (ky != 1) {
	        this._createLowerSector(ctx, x0, y0, alpha0, alpha1, radius, true);

	        ctx.fillStyle = "#000000";
	        ctx.globalAlpha = 0.2;

	        this._createLowerSector(ctx, x0, y0, alpha0, alpha1, radius, false);

	        ctx.globalAlpha = 1;
	        ctx.fillStyle = color;
	      }
	      /*creats map area (needed for events)*/


	      map.addSector(data[i].id, alpha0, alpha1, x0 - point0.x, y0 - point0.y / ky, radius, ky, sIndex);
	      alpha0 = alpha1;
	    }
	    /*renders radius lines and labels*/


	    ctx.globalAlpha = 0.8;
	    var p;

	    for (i = 0; i < angles.length; i++) {
	      p = this._getPositionByAngle(angles[i], x0, y0, radius);

	      this._drawLine(ctx, x0, y0, p.x, p.y, this._settings.lineColor.call(this, data[i]), 2);
	    }

	    ctx.globalAlpha = 1;

	    if (ky == 1 && this._settings.border) {
	      ctx.lineWidth = this._settings.borderWidth || 2;
	      ctx.strokeStyle = this._settings.borderColor ? this._settings.borderColor.call(this) : "#ffffff";
	      ctx.beginPath();
	      ctx.arc(x0, y0, radius + 1, 0, 2 * Math.PI, false);
	      ctx.stroke();
	    }

	    ctx.scale(1, 1 / ky);
	  },

	  /**
	   *   returns list of values
	   *   @param: data array
	   */
	  _getValues: function (data) {
	    var v = [];

	    for (var i = 0; i < data.length; i++) {
	      v.push(Math.abs(parseFloat(this._settings.value(data[i]) || 0)));
	    }

	    return v;
	  },

	  /**
	   *   returns total value
	   *   @param: the array of values
	   */
	  _getTotalValue: function (values) {
	    var t = 0;

	    for (var i = 0; i < values.length; i++) {
	      t += values[i];
	    }

	    return t;
	  },

	  /**
	   *   gets angles for all values
	   *   @param: the array of values
	   *   @param: total value (optional)
	   */
	  _getRatios: function (values, totalValue) {
	    var value;
	    var ratios = [];
	    var prevSum = 0;
	    totalValue = totalValue || this._getTotalValue(values);

	    for (var i = 0; i < values.length; i++) {
	      value = values[i];
	      ratios[i] = Math.PI * 2 * (totalValue ? (value + prevSum) / totalValue : 1 / values.length);
	      prevSum += value;
	    }

	    return ratios;
	  },

	  /**
	   *   returns calculated pie parameters: center position and radius
	   *   @param: x - the width of a container
	   *   @param: y - the height of a container
	   */
	  _getPieParameters: function (point0, point1) {
	    /*var offsetX = 0;
	     var offsetY = 0;
	     if(this._settings.legend &&this._settings.legend.layout!="x")
	     offsetX = this._settings.legend.width*(this._settings.legend.align=="right"?-1:1);
	     var x0 = (x + offsetX)/2;
	     if(this._settings.legend &&this._settings.legend.layout=="x")
	     offsetY = this._settings.legend.height*(this._settings.legend.valign=="bottom"?-1:1);
	     var y0 = (y+offsetY)/2;*/
	    var width = point1.x - point0.x;
	    var height = point1.y - point0.y;
	    var x0 = point0.x + width / 2;
	    var y0 = point0.y + height / 2;
	    var radius = Math.min(width / 2, height / 2);
	    return {
	      "x": x0,
	      "y": y0,
	      "radius": radius
	    };
	  },

	  /**
	   *   creates lower part of sector in 3Dpie
	   *   @param: ctx - canvas object
	   *   @param: x0 - the horizontal position of the pie center
	   *   @param: y0 - the vertical position of the pie center
	   *   @param: a0 - the angle that defines the first edge of a sector
	   *   @param: a1 - the angle that defines the second edge of a sector
	   *   @param: R - pie radius
	   *   @param: line (boolean) - if the sector needs a border
	   */
	  _createLowerSector: function (ctx, x0, y0, a1, a2, R, line) {
	    ctx.lineWidth = 1;
	    /*checks if the lower sector needs being displayed*/

	    if (!(a1 <= 0 && a2 >= 0 || a1 >= 0 && a2 <= Math.PI || Math.abs(a1 - Math.PI) > 0.003 && a1 <= Math.PI && a2 >= Math.PI)) return;

	    if (a1 <= 0 && a2 >= 0) {
	      a1 = 0;
	      line = false;

	      this._drawSectorLine(ctx, x0, y0, R, a1, a2);
	    }

	    if (a1 <= Math.PI && a2 >= Math.PI) {
	      a2 = Math.PI;
	      line = false;

	      this._drawSectorLine(ctx, x0, y0, R, a1, a2);
	    }
	    /*the height of 3D pie*/


	    var offset = (this._settings.pieHeight || Math.floor(R / 4)) / this._settings.cant;

	    ctx.beginPath();
	    ctx.arc(x0, y0, R, a1, a2, false);
	    ctx.lineTo(x0 + R * Math.cos(a2), y0 + R * Math.sin(a2) + offset);
	    ctx.arc(x0, y0 + offset, R, a2, a1, true);
	    ctx.lineTo(x0 + R * Math.cos(a1), y0 + R * Math.sin(a1));
	    ctx.fill();
	    if (line) ctx.stroke();
	  },

	  /**
	   *   draws a serctor arc
	   */
	  _drawSectorLine: function (ctx, x0, y0, R, a1, a2) {
	    ctx.beginPath();
	    ctx.arc(x0, y0, R, a1, a2, false);
	    ctx.stroke();
	  },

	  /**
	   *   adds a shadow to pie
	   *   @param: ctx - canvas object
	   *   @param: x - the horizontal position of the pie center
	   *   @param: y - the vertical position of the pie center
	   *   @param: R - pie radius
	   */
	  _addShadow: function (ctx, x, y, R) {
	    ctx.globalAlpha = 0.5;
	    var shadows = ["#c4c4c4", "#c6c6c6", "#cacaca", "#dcdcdc", "#dddddd", "#e0e0e0", "#eeeeee", "#f5f5f5", "#f8f8f8"];

	    for (var i = shadows.length - 1; i > -1; i--) {
	      ctx.beginPath();
	      ctx.fillStyle = shadows[i];
	      ctx.arc(x + 1, y + 1, R + i, 0, Math.PI * 2, true);
	      ctx.fill();
	    }

	    ctx.globalAlpha = 1;
	  },

	  /**
	   *   returns a gray gradient
	   *   @param: gradient - gradient object
	   */
	  _getGrayGradient: function (gradient) {
	    gradient.addColorStop(0.0, "#ffffff");
	    gradient.addColorStop(0.7, "#7a7a7a");
	    gradient.addColorStop(1.0, "#000000");
	    return gradient;
	  },

	  /**
	   *   adds gray radial gradient
	   *   @param: ctx - canvas object
	   *   @param: x - the horizontal position of the pie center
	   *   @param: y - the vertical position of the pie center
	   *   @param: radius - pie radius
	   *   @param: x0 - the horizontal position of a gradient center
	   *   @param: y0 - the vertical position of a gradient center
	   */
	  _showRadialGradient: function (ctx, x, y, radius, x0, y0) {
	    //ctx.globalAlpha = 0.3;
	    ctx.beginPath();
	    var gradient;

	    if (typeof this._settings.gradient != "function") {
	      gradient = ctx.createRadialGradient(x0, y0, radius / 4, x, y, radius);
	      gradient = this._getGrayGradient(gradient);
	    } else gradient = this._settings.gradient(gradient);

	    ctx.fillStyle = gradient;
	    ctx.arc(x, y, radius, 0, Math.PI * 2, true);
	    ctx.fill(); //ctx.globalAlpha = 1;

	    ctx.globalAlpha = 0.7;
	  },

	  /**
	   *   returns the calculates pie parameters: center position and radius
	   *   @param: ctx - canvas object
	   *   @param: x0 - the horizontal position of the pie center
	   *   @param: y0 - the vertical position of the pie center
	   *   @param: R - pie radius
	   *   @param: alpha1 - the angle that defines the 1st edge of a sector
	   *   @param: alpha2 - the angle that defines the 2nd edge of a sector
	   *   @param: ky - the value that defines an angle of inclination
	   *   @param: text - label text
	   *   @param: in_width (boolean) - if label needs being displayed inside a pie
	   */
	  _drawSectorLabel: function (x0, y0, R, alpha1, alpha2, ky, text, in_width) {
	    var t = this.canvases[0].renderText(0, 0, text, 0, 1);
	    if (!t) return; //get existing width of text

	    var labelWidth = t.scrollWidth;
	    t.style.width = labelWidth + "px"; //adjust text label to fit all text

	    if (labelWidth > x0) labelWidth = x0; //the text can't be greater than half of view
	    //calculate expected correction based on default font metrics

	    var width = alpha2 - alpha1 < 0.2 ? 4 : 8;
	    if (in_width) width = labelWidth / 1.8;
	    var alpha = alpha1 + (alpha2 - alpha1) / 2; //position and its correction

	    R = R - (width - 8) / 2;
	    var corr_x = -width;
	    var corr_y = -8;
	    var align = "right"; //for items in left upper and lower sector

	    if (alpha >= Math.PI / 2 && alpha < Math.PI || alpha <= 3 * Math.PI / 2 && alpha >= Math.PI) {
	      corr_x = -labelWidth - corr_x + 1;
	      /*correction for label width*/

	      align = "left";
	    }
	    /*
	       calculate position of text
	       basically get point at center of pie sector
	    */


	    var offset = 0;
	    if (!in_width && ky < 1 && alpha > 0 && alpha < Math.PI) offset = (this._settings.height || Math.floor(R / 4)) / ky;
	    var y = (y0 + Math.floor((R + offset) * Math.sin(alpha))) * ky + corr_y;
	    var x = x0 + Math.floor((R + width / 2) * Math.cos(alpha)) + corr_x;
	    /*
	       if pie sector starts in left of right part pie,
	       related text	must be placed to the left of to the right of pie as well
	    */

	    var left_end = alpha2 < Math.PI / 2 + 0.01;
	    var left_start = alpha1 < Math.PI / 2;

	    if (left_start && left_end) {
	      x = Math.max(x, x0 + 3); //right part of pie

	      /*if(alpha2-alpha1<0.2)
	      	x = x0;*/
	    } else if (!left_start && !left_end) x = Math.min(x, x0 - labelWidth); //left part of pie
	    else if (!in_width && (alpha >= Math.PI / 2 && alpha < Math.PI || alpha <= 3 * Math.PI / 2 && alpha >= Math.PI)) {
	        x += labelWidth / 3;
	      } //we need to set position of text manually, based on above calculations


	    t.style.top = y + "px";
	    t.style.left = x + "px";
	    t.style.width = labelWidth + "px";
	    t.style.textAlign = align;
	    t.style.whiteSpace = "nowrap";
	  },
	  $render_pie3D: function (ctx, data, x, y, sIndex, map) {
	    this._renderPie(ctx, data, x, y, this._settings.cant, map);
	  },
	  $render_donut: function (ctx, data, point0, point1, sIndex, map) {
	    if (!data.length) return;

	    this._renderPie(ctx, data, point0, point1, 1, map, sIndex);

	    var config = this._settings;

	    var coord = this._getPieParameters(point0, point1);

	    var pieRadius = config.radius ? config.radius : coord.radius;
	    var innerRadius = config.innerRadius && config.innerRadius < pieRadius ? config.innerRadius : pieRadius / 3;
	    var x0 = config.x ? config.x : coord.x;
	    var y0 = config.y ? config.y : coord.y;
	    ctx.fillStyle = "#ffffff";
	    ctx.beginPath();
	    ctx.arc(x0, y0, innerRadius, 0, Math.PI * 2, true);
	    ctx.fill();
	  }
	};

	var BarChart = {
	  /**
	  *   renders a bar chart
	  *   @param: ctx - canvas object
	  *   @param: data - object those need to be displayed
	  *   @param: x - the width of the container
	  *   @param: y - the height of the container
	  *   @param: sIndex - index of drawing chart
	  */
	  $render_bar: function (ctx, data, point0, point1, sIndex, map) {
	    var barWidth,
	        cellWidth,
	        i,
	        limits,
	        maxValue,
	        minValue,
	        relValue,
	        valueFactor,
	        relativeValues,
	        startValue,
	        unit,
	        xax,
	        yax,
	        totalHeight = point1.y - point0.y;
	    yax = !!this._settings.yAxis;
	    xax = !!this._settings.xAxis;
	    limits = this._getLimits();
	    maxValue = limits.max;
	    minValue = limits.min;
	    /*an available width for one bar*/

	    cellWidth = (point1.x - point0.x) / data.length;
	    /*draws x and y scales*/

	    if (!sIndex && !(this._settings.origin != "auto" && !yax)) {
	      this._drawScales(data, point0, point1, minValue, maxValue, cellWidth);
	    }
	    /*necessary for automatic scale*/


	    if (yax) {
	      maxValue = parseFloat(this._settings.yAxis.end);
	      minValue = parseFloat(this._settings.yAxis.start);
	    }
	    /*unit calculation (bar_height = value*unit)*/


	    relativeValues = this._getRelativeValue(minValue, maxValue);
	    relValue = relativeValues[0];
	    valueFactor = relativeValues[1];
	    unit = relValue ? totalHeight / relValue : relValue;

	    if (!yax && !(this._settings.origin != "auto" && xax)) {
	      /*defines start value for better representation of small values*/
	      startValue = 10;
	      unit = relValue ? (totalHeight - startValue) / relValue : startValue;
	    }
	    /*if yAxis isn't set, but with custom origin */


	    if (!sIndex && this._settings.origin != "auto" && !yax && this._settings.origin > minValue) {
	      this._drawXAxis(ctx, data, point0, point1, cellWidth, point1.y - unit * (this._settings.origin - minValue));
	    }
	    /*a real bar width */


	    barWidth = parseInt(this._settings.barWidth, 10);
	    var seriesNumber = 0;
	    var seriesIndex = 0;

	    for (i = 0; i < this._series.length; i++) {
	      if (i == sIndex) {
	        seriesIndex = seriesNumber;
	      }

	      if (this._series[i].type == "bar") seriesNumber++;
	    }

	    if (this._series && barWidth * seriesNumber + 4 > cellWidth) barWidth = parseInt(cellWidth / seriesNumber - 4, 10);
	    /*the half of distance between bars*/

	    var barOffset = (cellWidth - barWidth * seriesNumber) / 2;
	    /*the radius of rounding in the top part of each bar*/

	    var radius = typeof this._settings.radius != "undefined" ? parseInt(this._settings.radius, 10) : Math.round(barWidth / 5);
	    var inner_gradient = false;
	    var gradient = this._settings.gradient;

	    if (gradient && typeof gradient != "function") {
	      inner_gradient = gradient;
	      gradient = false;
	    } else if (gradient) {
	      gradient = ctx.createLinearGradient(0, point1.y, 0, point0.y);

	      this._settings.gradient(gradient);
	    }
	    /*draws a black line if the horizontal scale isn't defined*/


	    if (!xax) {
	      this._drawLine(ctx, point0.x, point1.y + 0.5, point1.x, point1.y + 0.5, "#edeff0", 1); //hardcoded color!

	    }

	    for (i = 0; i < data.length; i++) {
	      var value = parseFloat(this._settings.value(data[i]) || 0);
	      if (this._logScaleCalc) value = this._log10(value);
	      if (value > maxValue) value = maxValue;
	      value -= minValue;
	      value *= valueFactor;
	      /*start point (bottom left)*/

	      var x0 = point0.x + barOffset + i * cellWidth + (barWidth + 1) * seriesIndex;
	      var y0 = point1.y;

	      var color$$1 = gradient || this._settings.color.call(this, data[i]);

	      var border = this._settings.border ? 1 : 0;

	      var label = this._settings.label(data[i]);
	      /* don't draw borders and labels for not painted values (on y-Axis or lower) */


	      if (value == this._settings.origin || this._settings.origin == "auto" && this._settings.value(data[i]) == minValue) {
	        border = 0;
	        label = "";
	      } else if (value < 0 || this._settings.yAxis && value === 0 && !(this._settings.origin != "auto" && this._settings.origin > minValue)) {
	        value = border = 0;
	        label = "";
	      }
	      /*takes start value into consideration */
	      else if (!yax && !(this._settings.origin != "auto" && xax)) value += startValue / unit;
	      /*drawing bar body*/


	      ctx.globalAlpha = this._settings.alpha.call(this, data[i]);

	      var points = this._drawBar(ctx, point0, x0, y0, barWidth, minValue, radius, unit, value, color$$1, gradient, inner_gradient, border);

	      if (inner_gradient) {
	        this._drawBarGradient(ctx, x0, y0, barWidth, minValue, radius, unit, value, color$$1, inner_gradient, border);
	      }
	      /*drawing the gradient border of a bar*/


	      if (border) this._drawBarBorder(ctx, x0, y0, barWidth, minValue, radius, unit, value, color$$1);
	      ctx.globalAlpha = 1;
	      /*sets a bar label*/

	      if (points[0] != x0) this.canvases[sIndex].renderTextAt(false, true, x0 + Math.floor(barWidth / 2), points[1], label);else this.canvases[sIndex].renderTextAt(true, true, x0 + Math.floor(barWidth / 2), points[3], label);
	      /*defines a map area for a bar*/

	      map.addRect(data[i].id, [x0 - point0.x, points[3] - point0.y, points[2] - point0.x, points[1] - point0.y], sIndex); //this._addMapRect(map,data[i].id,[{x:x0,y:points[3]},{x:points[2],y:points[1]}],point0,sIndex);
	    }
	  },
	  _correctBarParams: function (ctx, x, y, value, unit, barWidth, minValue) {
	    var xax = this._settings.xAxis;
	    var axisStart = y;

	    if (!!xax && this._settings.origin != "auto" && this._settings.origin > minValue) {
	      y -= (this._settings.origin - minValue) * unit;
	      axisStart = y;
	      value = value - (this._settings.origin - minValue);

	      if (value < 0) {
	        value *= -1;
	        ctx.translate(x + barWidth, y);
	        ctx.rotate(Math.PI);
	        x = 0;
	        y = 0;
	      }

	      y -= 0.5;
	    }

	    return {
	      value: value,
	      x0: x,
	      y0: y,
	      start: axisStart
	    };
	  },
	  _drawBar: function (ctx, point0, x0, y0, barWidth, minValue, radius, unit, value, color$$1, gradient, inner_gradient, border) {
	    ctx.save();
	    ctx.fillStyle = color$$1;

	    var p = this._correctBarParams(ctx, x0, y0, value, unit, barWidth, minValue);

	    var points = this._setBarPoints(ctx, p.x0, p.y0, barWidth, radius, unit, p.value, border);

	    if (gradient && !inner_gradient) ctx.lineTo(p.x0 + border, point0.y); //fix gradient sphreading

	    ctx.fill();
	    ctx.restore();
	    var x1 = p.x0;
	    var x2 = p.x0 != x0 ? x0 + points[0] : points[0];
	    var y1 = p.x0 != x0 ? p.start - points[1] - p.y0 : p.y0;
	    var y2 = p.x0 != x0 ? p.start - p.y0 : points[1];
	    return [x1, y1, x2, y2];
	  },
	  _setBorderStyles: function (ctx, rawColor) {
	    var hsv, rgb;
	    rgb = color.toRgb(rawColor);
	    hsv = color.rgbToHsv(rgb[0], rgb[1], rgb[2]);
	    hsv[2] /= 1.4;
	    var rgbColor = "rgb(" + color.hsvToRgb(hsv[0], hsv[1], hsv[2]) + ")";
	    ctx.strokeStyle = rgbColor;
	    if (ctx.globalAlpha == 1) ctx.globalAlpha = 0.9;
	  },
	  _drawBarBorder: function (ctx, x0, y0, barWidth, minValue, radius, unit, value, color$$1) {
	    var p;
	    ctx.save();
	    p = this._correctBarParams(ctx, x0, y0, value, unit, barWidth, minValue);

	    this._setBorderStyles(ctx, color$$1);

	    this._setBarPoints(ctx, p.x0, p.y0, barWidth, radius, unit, p.value, ctx.lineWidth / 2, 1);

	    ctx.stroke();
	    /*ctx.fillStyle = color;
	     this._setBarPoints(ctx,p.x0,p.y0,barWidth,radius,unit,p.value,0);
	     ctx.lineTo(p.x0,0);
	     ctx.fill()
	    		 ctx.fillStyle = "#000000";
	     ctx.globalAlpha = 0.37;
	    	 this._setBarPoints(ctx,p.x0,p.y0,barWidth,radius,unit,p.value,0);
	     ctx.fill()
	     */

	    ctx.restore();
	  },
	  _drawBarGradient: function (ctx, x0, y0, barWidth, minValue, radius, unit, value, color$$1, inner_gradient, border) {
	    ctx.save();

	    var p = this._correctBarParams(ctx, x0, y0, value, unit, barWidth, minValue);

	    var gradParam = this._setBarGradient(ctx, p.x0, p.y0, p.x0 + barWidth, p.y0 - unit * p.value + 2, inner_gradient, color$$1, "y");

	    ctx.fillStyle = gradParam.gradient;

	    this._setBarPoints(ctx, p.x0 + gradParam.offset, p.y0, barWidth - gradParam.offset * 2, radius, unit, p.value, gradParam.offset + border);

	    ctx.fill();
	    ctx.restore();
	  },

	  /**
	   *   sets points for bar and returns the position of the bottom right point
	   *   @param: ctx - canvas object
	   *   @param: x0 - the x position of start point
	   *   @param: y0 - the y position of start point
	   *   @param: barWidth - bar width
	   *   @param: radius - the rounding radius of the top
	   *   @param: unit - the value defines the correspondence between item value and bar height
	   *   @param: value - item value
	   *   @param: offset - the offset from expected bar edge (necessary for drawing border)
	   */
	  _setBarPoints: function (ctx, x0, y0, barWidth, radius, unit, value, offset, skipBottom) {
	    /*correction for displaing small values (when rounding radius is bigger than bar height)*/
	    ctx.beginPath(); //y0 = 0.5;

	    var angle_corr = 0;

	    if (radius > unit * value) {
	      var cosA = (radius - unit * value) / radius;
	      if (cosA <= 1 && cosA >= -1) angle_corr = -Math.acos(cosA) + Math.PI / 2;
	    }
	    /*start*/


	    ctx.moveTo(x0 + offset, y0);
	    /*start of left rounding*/

	    var y1 = y0 - Math.floor(unit * value) + radius + (radius ? 0 : offset);
	    if (radius < unit * value) ctx.lineTo(x0 + offset, y1);
	    /*left rounding*/

	    var x2 = x0 + radius;
	    if (radius && radius > 0) ctx.arc(x2, y1, Math.max(radius - offset, 0), -Math.PI + angle_corr, -Math.PI / 2, false);
	    /*start of right rounding*/

	    var x3 = x0 + barWidth - radius - offset;
	    var y3 = y1 - radius + (radius ? offset : 0);
	    ctx.lineTo(x3, y3);
	    /*right rounding*/

	    if (radius && radius > 0) ctx.arc(x3 + offset, y1, Math.max(radius - offset, 0), -Math.PI / 2, 0 - angle_corr, false);
	    /*bottom right point*/

	    var x5 = x0 + barWidth - offset;
	    ctx.lineTo(x5, y0);
	    /*line to the start point*/

	    if (!skipBottom) {
	      ctx.lineTo(x0 + offset, y0);
	    } //	ctx.lineTo(x0,0); //IE fix!


	    return [x5, y3];
	  }
	};

	var LineChart = {
	  /**
	  *   renders a graphic
	  *   @param: ctx - canvas object
	  *   @param: data - object those need to be displayed
	  *   @param: width - the width of the container
	  *   @param: height - the height of the container
	  *   @param: sIndex - index of drawing chart
	  */
	  $render_line: function (ctx, data, point0, point1, sIndex, map) {
	    var config, i, items, params, x0, x1, x2, y1, y2, y0, res1, res2;
	    params = this._calculateLineParams(ctx, data, point0, point1, sIndex);
	    config = this._settings;

	    if (data.length) {
	      x0 = config.offset ? point0.x + params.cellWidth * 0.5 : point0.x; //finds items with data (excludes scale units)

	      items = [];

	      for (i = 0; i < data.length; i++) {
	        res2 = this._getPointY(data[i], point0, point1, params);

	        if (res2 || res2 == "0") {
	          x2 = !i ? x0 : params.cellWidth * i - 0.5 + x0;
	          y2 = _typeof(res2) == "object" ? res2.y0 : res2;

	          if (i && this._settings.fixOverflow) {
	            res1 = this._getPointY(data[i - 1], point0, point1, params);

	            if (res1.out && res1.out == res2.out) {
	              continue;
	            }

	            x1 = params.cellWidth * (i - 1) - 0.5 + x0;
	            y1 = _typeof(res1) == "object" ? res1.y0 : res1;

	            if (res1.out) {
	              y0 = res1.out == "min" ? point1.y : point0.y;
	              items.push({
	                x: this._calcOverflowX(x1, x2, y1, y2, y0),
	                y: y0
	              });
	            }

	            if (res2.out) {
	              y0 = res2.out == "min" ? point1.y : point0.y;
	              items.push({
	                x: this._calcOverflowX(x1, x2, y1, y2, y0),
	                y: y0
	              });
	            }
	          }

	          if (!res2.out) items.push({
	            x: x2,
	            y: res2,
	            index: i
	          });
	        }
	      }

	      this._mapStart = point0;

	      for (i = 1; i <= items.length; i++) {
	        //line start position
	        x1 = items[i - 1].x;
	        y1 = items[i - 1].y;

	        if (i < items.length) {
	          //line end position
	          x2 = items[i].x;
	          y2 = items[i].y; //line

	          this._drawLine(ctx, x1, y1, x2, y2, config.line.color.call(this, data[i - 1]), config.line.width); //line shadow


	          if (config.line && config.line.shadow) {
	            ctx.globalAlpha = 0.3;

	            this._drawLine(ctx, x1 + 2, y1 + config.line.width + 8, x2 + 2, y2 + config.line.width + 8, "#eeeeee", config.line.width + 3);

	            ctx.globalAlpha = 1;
	          }
	        } //item


	        if (typeof items[i - 1].index != "undefined") {
	          this._drawItem(ctx, x1, y1, data[items[i - 1].index], config.label(data[items[i - 1].index]), sIndex, map, point0);
	        }
	      }
	    }
	  },
	  _calcOverflowX: function (x1, x2, y1, y2, y) {
	    return x1 + (y - y1) * (x2 - x1) / (y2 - y1);
	  },

	  /**
	  *   draws an item and its label
	  *   @param: ctx - canvas object
	  *   @param: x0 - the x position of a circle
	  *   @param: y0 - the y position of a circle
	  *   @param: obj - data object
	  *   @param: label - (boolean) defines wherether label needs being drawn
	  */
	  _drawItem: function (ctx, x0, y0, obj, label, sIndex, map) {
	    var config = this._settings.item;
	    var R = parseInt(config.radius.call(this, obj), 10) || 0;
	    var mapStart = this._mapStart;
	    var item = config.type.call(this, obj);

	    if (R) {
	      ctx.save();

	      if (config.shadow) {
	        ctx.lineWidth = 1;
	        ctx.strokeStyle = "#bdbdbd";
	        ctx.fillStyle = "#bdbdbd";
	        var alphas = [0.1, 0.2, 0.3];

	        for (var i = alphas.length - 1; i >= 0; i--) {
	          ctx.globalAlpha = alphas[i];
	          ctx.strokeStyle = "#d0d0d0";
	          ctx.beginPath();

	          this._strokeChartItem(ctx, x0, y0 + 2 * R / 3, R + i + 1, item);

	          ctx.stroke();
	        }

	        ctx.beginPath();
	        ctx.globalAlpha = 0.3;
	        ctx.fillStyle = "#bdbdbd";

	        this._strokeChartItem(ctx, x0, y0 + 2 * R / 3, R + 1, item);

	        ctx.fill();
	      }

	      ctx.restore();
	      ctx.lineWidth = config.borderWidth;
	      ctx.fillStyle = config.color.call(this, obj);
	      ctx.strokeStyle = config.borderColor.call(this, obj);
	      ctx.globalAlpha = config.alpha.call(this, obj);
	      ctx.beginPath();

	      this._strokeChartItem(ctx, x0, y0, R + 1, item);

	      ctx.fill();
	      ctx.stroke();
	      ctx.globalAlpha = 1;
	    }
	    /*item label*/


	    if (label) {
	      this.canvases[sIndex].renderTextAt(false, true, x0, y0 - R - this._settings.labelOffset, this._settings.label.call(this, obj));
	    }

	    if (map) {
	      var areaPos = this._settings.eventRadius || R + 1; //this._addMapRect(map,obj.id,[{x:x0-areaPos,y:y0-areaPos},{x0+areaPos,y:y0+areaPos}],point0,sIndex);

	      map.addRect(obj.id, [x0 - areaPos - mapStart.x, y0 - areaPos - mapStart.y, x0 + areaPos - mapStart.x, y0 + areaPos - mapStart.y], sIndex);
	    }
	  },
	  _strokeChartItem: function (ctx, x0, y0, R, type) {
	    var p = [];

	    if (type && (type == "square" || type == "s")) {
	      R *= Math.sqrt(2) / 2;
	      p = [[x0 - R - ctx.lineWidth / 2, y0 - R], [x0 + R, y0 - R], [x0 + R, y0 + R], [x0 - R, y0 + R], [x0 - R, y0 - R]];
	    } else if (type && (type == "diamond" || type == "d")) {
	      var corr = ctx.lineWidth > 1 ? ctx.lineWidth * Math.sqrt(2) / 4 : 0;
	      p = [[x0, y0 - R], [x0 + R, y0], [x0, y0 + R], [x0 - R, y0], [x0 + corr, y0 - R - corr]];
	    } else if (type && (type == "triangle" || type == "t")) {
	      p = [[x0, y0 - R], [x0 + Math.sqrt(3) * R / 2, y0 + R / 2], [x0 - Math.sqrt(3) * R / 2, y0 + R / 2], [x0, y0 - R]];
	    } else p = [[x0, y0, R, 0, Math.PI * 2, true]];

	    this._path(ctx, p);
	  },

	  /**
	  *   gets the vertical position of the item
	  *   @param: data - data object
	  *   @param: y0 - the y position of chart start
	  *   @param: y1 - the y position of chart end
	  *   @param: params - the object with elements: minValue, maxValue, unit, valueFactor (the value multiple of 10)
	  */
	  _getPointY: function (data, point0, point1, params) {
	    var minValue = params.minValue;
	    var maxValue = params.maxValue;
	    var unit = params.unit;
	    var valueFactor = params.valueFactor;
	    /*the real value of an object*/

	    var value = this._settings.value(data);

	    if (this._logScaleCalc) {
	      value = this._log10(value);
	    }
	    /*a relative value*/


	    var v = (parseFloat(value || 0) - minValue) * valueFactor;
	    if (!this._settings.yAxis) v += params.startValue / unit;
	    /*a vertical coordinate*/

	    var y = point1.y - unit * v;
	    /*the limit of the max and min values*/

	    if (this._settings.fixOverflow && (this._settings.type == "line" || this._settings.type == "area")) {
	      if (value > maxValue) y = {
	        y: point0.y,
	        y0: y,
	        out: "max"
	      };else if (v < 0 || value < minValue) y = {
	        y: point1.y,
	        y0: y,
	        out: "min"
	      };
	    } else {
	      if (value > maxValue) y = point0.y;
	      if (v < 0 || value < minValue) y = point1.y;
	    }

	    return y;
	  },
	  _calculateLineParams: function (ctx, data, point0, point1, sIndex) {
	    var params = {};
	    /*maxValue - minValue*/

	    var relValue;
	    /*available height*/

	    params.totalHeight = point1.y - point0.y;
	    /*a space available for a single item*/
	    //params.cellWidth = Math.round((point1.x-point0.x)/((!this._settings.offset&&this._settings.yAxis)?(data.length-1):data.length));

	    if (this._settings.cellWidth) params.cellWidth = Math.min(point1.x - point0.x, this._settings.cellWidth);else params.cellWidth = (point1.x - point0.x) / (!this._settings.offset ? data.length - 1 : data.length);
	    /*scales*/

	    var yax = !!this._settings.yAxis;
	    var limits = this._settings.type.indexOf("stacked") != -1 ? this._getStackedLimits(data) : this._getLimits();
	    params.maxValue = limits.max;
	    params.minValue = limits.min;
	    /*draws x and y scales*/

	    if (!sIndex) this._drawScales(data, point0, point1, params.minValue, params.maxValue, params.cellWidth);
	    /*necessary for automatic scale*/

	    if (yax) {
	      params.maxValue = parseFloat(this._settings.yAxis.end);
	      params.minValue = parseFloat(this._settings.yAxis.start);
	    }
	    /*unit calculation (y_position = value*unit)*/


	    var relativeValues = this._getRelativeValue(params.minValue, params.maxValue);

	    relValue = relativeValues[0];
	    params.valueFactor = relativeValues[1];
	    params.unit = relValue ? params.totalHeight / relValue : 10;
	    params.startValue = 0;

	    if (!yax) {
	      /*defines start value for better representation of small values*/
	      params.startValue = 10;
	      if (params.unit != params.totalHeight) params.unit = relValue ? (params.totalHeight - params.startValue) / relValue : 10;
	    }

	    return params;
	  }
	};

	var BarHChart = {
	  /**
	  *   renders a bar chart
	  *   @param: ctx - canvas object
	  *   @param: data - object those need to be displayed
	  *   @param: x - the width of the container
	  *   @param: y - the height of the container
	  *   @param: sIndex - index of drawing chart
	  */
	  $render_barH: function (ctx, data, point0, point1, sIndex, map) {
	    var barOffset, barWidth, cellWidth, color, gradient, i, limits, maxValue, minValue, innerGradient, valueFactor, relValue, radius, relativeValues, startValue, totalWidth, value, unit, x0, y0, xax;
	    /*an available width for one bar*/

	    cellWidth = (point1.y - point0.y) / data.length;
	    limits = this._getLimits("h");
	    maxValue = limits.max;
	    minValue = limits.min;
	    totalWidth = point1.x - point0.x;
	    xax = !!this._settings.xAxis;
	    /*draws x and y scales*/

	    if (!sIndex) this._drawHScales(ctx, data, point0, point1, minValue, maxValue, cellWidth);
	    /*necessary for automatic scale*/

	    if (xax) {
	      maxValue = parseFloat(this._settings.xAxis.end);
	      minValue = parseFloat(this._settings.xAxis.start);
	    }
	    /*unit calculation (bar_height = value*unit)*/


	    relativeValues = this._getRelativeValue(minValue, maxValue);
	    relValue = relativeValues[0];
	    valueFactor = relativeValues[1];
	    unit = relValue ? totalWidth / relValue : 10;

	    if (!xax) {
	      /*defines start value for better representation of small values*/
	      startValue = 10;
	      unit = relValue ? (totalWidth - startValue) / relValue : 10;
	    }
	    /*a real bar width */


	    barWidth = parseInt(this._settings.barWidth, 10);
	    if (barWidth * this._series.length + 4 > cellWidth) barWidth = cellWidth / this._series.length - 4;
	    /*the half of distance between bars*/

	    barOffset = Math.floor((cellWidth - barWidth * this._series.length) / 2);
	    /*the radius of rounding in the top part of each bar*/

	    radius = typeof this._settings.radius != "undefined" ? parseInt(this._settings.radius, 10) : Math.round(barWidth / 5);
	    innerGradient = false;
	    gradient = this._settings.gradient;

	    if (gradient && typeof gradient != "function") {
	      innerGradient = gradient;
	      gradient = false;
	    } else if (gradient) {
	      gradient = ctx.createLinearGradient(point0.x, point0.y, point1.x, point0.y);

	      this._settings.gradient(gradient);
	    }
	    /*draws a black line if the horizontal scale isn't defined*/


	    if (!xax) {
	      this._drawLine(ctx, point0.x - 0.5, point0.y, point0.x - 0.5, point1.y, "#edeff0", 1); //hardcoded color!

	    }

	    for (i = 0; i < data.length; i++) {
	      value = parseFloat(this._settings.value(data[i] || 0));
	      if (this._logScaleCalc) value = this._log10(value);
	      if (!value || isNaN(value)) continue;
	      if (value > maxValue) value = maxValue;
	      value -= minValue;
	      value *= valueFactor;
	      /*start point (bottom left)*/

	      x0 = point0.x;
	      y0 = point0.y + barOffset + i * cellWidth + (barWidth + 1) * sIndex;

	      if (value < 0 && this._settings.origin == "auto" || this._settings.xAxis && value === 0 && !(this._settings.origin != "auto" && this._settings.origin > minValue)) {
	        this.canvases[sIndex].renderTextAt("middle", "right", x0 + 10, y0 + barWidth / 2 + barOffset, this._settings.label(data[i]));
	        continue;
	      }

	      if (value < 0 && this._settings.origin != "auto" && this._settings.origin > minValue) {
	        value = 0;
	      }
	      /*takes start value into consideration*/


	      if (!xax) value += startValue / unit;
	      color = gradient || this._settings.color.call(this, data[i]);
	      /*drawing the gradient border of a bar*/

	      if (this._settings.border) {
	        this._drawBarHBorder(ctx, x0, y0, barWidth, minValue, radius, unit, value, color);
	      }
	      /*drawing bar body*/


	      ctx.globalAlpha = this._settings.alpha.call(this, data[i]);

	      var points = this._drawBarH(ctx, point1, x0, y0, barWidth, minValue, radius, unit, value, color, gradient, innerGradient);

	      if (innerGradient) {
	        this._drawBarHGradient(ctx, x0, y0, barWidth, minValue, radius, unit, value, color, innerGradient);
	      }

	      ctx.globalAlpha = 1;
	      /*sets a bar label and map area*/

	      if (points[3] == y0) {
	        this.canvases[sIndex].renderTextAt("middle", "left", points[0] - 5, points[3] + Math.floor(barWidth / 2), this._settings.label(data[i]));
	        map.addRect(data[i].id, [points[0] - point0.x, points[3] - point0.y, points[2] - point0.x, points[3] + barWidth - point0.y], sIndex);
	      } else {
	        this.canvases[sIndex].renderTextAt("middle", false, points[2] + 5, points[1] + Math.floor(barWidth / 2), this._settings.label(data[i]));
	        map.addRect(data[i].id, [points[0] - point0.x, y0 - point0.y, points[2] - point0.x, points[3] - point0.y], sIndex);
	      }
	    }
	  },

	  /**
	   *   sets points for bar and returns the position of the bottom right point
	   *   @param: ctx - canvas object
	   *   @param: x0 - the x position of start point
	   *   @param: y0 - the y position of start point
	   *   @param: barWidth - bar width
	   *   @param: radius - the rounding radius of the top
	   *   @param: unit - the value defines the correspondence between item value and bar height
	   *   @param: value - item value
	   *   @param: offset - the offset from expected bar edge (necessary for drawing border)
	   */
	  _setBarHPoints: function (ctx, x0, y0, barWidth, radius, unit, value, offset, skipLeft) {
	    /*correction for displaing small values (when rounding radius is bigger than bar height)*/
	    var angle_corr = 0;

	    if (radius > unit * value) {
	      var sinA = (radius - unit * value) / radius;
	      angle_corr = -Math.asin(sinA) + Math.PI / 2;
	    }
	    /*start*/


	    ctx.moveTo(x0, y0 + offset);
	    /*start of left rounding*/

	    var x1 = x0 + unit * value - radius - (radius ? 0 : offset);
	    x1 = Math.max(x0, x1);
	    if (radius < unit * value) ctx.lineTo(x1, y0 + offset);
	    /*left rounding*/

	    var y2 = y0 + radius;
	    if (radius && radius > 0) ctx.arc(x1, y2, radius - offset, -Math.PI / 2 + angle_corr, 0, false);
	    /*start of right rounding*/

	    var y3 = y0 + barWidth - radius - (radius ? 0 : offset);
	    var x3 = x1 + radius - (radius ? offset : 0);
	    ctx.lineTo(x3, y3);
	    /*right rounding*/

	    if (radius && radius > 0) ctx.arc(x1, y3, radius - offset, 0, Math.PI / 2 - angle_corr, false);
	    /*bottom right point*/

	    var y5 = y0 + barWidth - offset;
	    ctx.lineTo(x0, y5);
	    /*line to the start point*/

	    if (!skipLeft) {
	      ctx.lineTo(x0, y0 + offset);
	    } //	ctx.lineTo(x0,0); //IE fix!


	    return [x3, y5];
	  },
	  _drawHScales: function (ctx, data, point0, point1, start, end, cellWidth) {
	    var x = 0;

	    if (this._settings.xAxis) {
	      if (!this.canvases["x"]) this.canvases["x"] = this._createCanvas("axis_x");
	      x = this._drawHXAxis(this.canvases["x"].getCanvas(), data, point0, point1, start, end);
	    }

	    if (this._settings.yAxis) {
	      if (!this.canvases["y"]) this.canvases["y"] = this._createCanvas("axis_y");

	      this._drawHYAxis(this.canvases["y"].getCanvas(), data, point0, point1, cellWidth, x);
	    }
	  },
	  _drawHYAxis: function (ctx, data, point0, point1, cellWidth, yAxisX) {
	    if (!this._settings.yAxis) return;
	    var unitPos;
	    var x0 = parseInt(yAxisX ? yAxisX : point0.x, 10) - 0.5;
	    var y0 = point1.y + 0.5;
	    var y1 = point0.y;

	    this._drawLine(ctx, x0, y0, x0, y1, this._settings.yAxis.color, 1);

	    for (var i = 0; i < data.length; i++) {
	      /*scale labels*/
	      var right = this._settings.origin != "auto" && this._settings.type == "barH" && parseFloat(this._settings.value(data[i])) < this._settings.origin;

	      unitPos = y1 + cellWidth / 2 + i * cellWidth;
	      this.canvases["y"].renderTextAt("middle", right ? false : "left", right ? x0 + 5 : x0 - 5, unitPos, this._settings.yAxis.template(data[i]), "webix_axis_item_y", right ? 0 : x0 - 10);
	      if (this._settings.yAxis.lines.call(this, data[i])) this._drawLine(ctx, point0.x, unitPos, point1.x, unitPos, this._settings.yAxis.lineColor.call(this, data[i]), 1);
	    }

	    if (this._settings.yAxis.lines.call(this, {})) this._drawLine(ctx, point0.x + 0.5, y1 + 0.5, point1.x, y1 + 0.5, this._settings.yAxis.lineColor.call(this, {}), 1);

	    this._setYAxisTitle(point0, point1);
	  },
	  _drawHXAxis: function (ctx, data, point0, point1, start, end) {
	    var step;
	    var scaleParam = {};
	    var axis = this._settings.xAxis;
	    if (!axis) return;
	    var y0 = point1.y + 0.5;
	    var x0 = point0.x - 0.5;
	    var x1 = point1.x - 0.5;
	    var yAxisStart = point0.x;

	    this._drawLine(ctx, x0, y0, x1, y0, axis.color, 1);

	    if (axis.step) step = parseFloat(axis.step);

	    if (typeof this._configXAxis.step == "undefined" || typeof this._configXAxis.start == "undefined" || typeof this._configXAxis.end == "undefined") {
	      scaleParam = this._calculateScale(start, end);
	      start = scaleParam.start;
	      end = scaleParam.end;
	      step = scaleParam.step;
	      this._settings.xAxis.end = end;
	      this._settings.xAxis.start = start;
	      this._settings.xAxis.step = step;
	    }

	    if (step === 0) return;
	    var stepHeight = (x1 - x0) * step / (end - start);
	    var c = 0;

	    for (var i = start; i <= end; i += step) {
	      var value = this._logScaleCalc ? Math.pow(10, i) : i;
	      if (scaleParam.fixNum) value = parseFloat(value).toFixed(scaleParam.fixNum);
	      var xi = Math.floor(x0 + c * stepHeight) + 0.5;
	      /*canvas line fix*/

	      if (!(i == start && this._settings.origin == "auto") && axis.lines.call(this, i)) this._drawLine(ctx, xi, y0, xi, point0.y, this._settings.xAxis.lineColor.call(this, i), 1);
	      if (i == this._settings.origin) yAxisStart = xi + 1;
	      /*correction for JS float calculation*/

	      if (step < 1 && !this._logScaleCalc) {
	        var power = Math.min(Math.floor(this._log10(step)), start <= 0 ? 0 : Math.floor(this._log10(start)));
	        var corr = Math.pow(10, -power);
	        value = Math.round(value * corr) / corr;
	        i = value;
	      }

	      this.canvases["x"].renderTextAt(false, true, xi, y0 + 2, axis.template(value.toString()), "webix_axis_item_x");
	      c++;
	    }

	    this.canvases["x"].renderTextAt(true, false, x0, point1.y + this._settings.padding.bottom - 3, this._settings.xAxis.title, "webix_axis_title_x", point1.x - point0.x);
	    return yAxisStart;
	  },
	  _correctBarHParams: function (ctx, x, y, value, unit, barWidth, minValue) {
	    var yax = this._settings.yAxis;
	    var axisStart = x;

	    if (!!yax && this._settings.origin != "auto" && this._settings.origin > minValue) {
	      x += (this._settings.origin - minValue) * unit;
	      axisStart = x;
	      value = value - (this._settings.origin - minValue);

	      if (value < 0) {
	        value *= -1;
	        ctx.translate(x, y + barWidth);
	        ctx.rotate(Math.PI);
	        x = 0.5;
	        y = 0;
	      }

	      x += 0.5;
	    }

	    return {
	      value: value,
	      x0: x,
	      y0: y,
	      start: axisStart
	    };
	  },
	  _drawBarH: function (ctx, point1, x0, y0, barWidth, minValue, radius, unit, value, color, gradient, inner_gradient) {
	    ctx.save();

	    var p = this._correctBarHParams(ctx, x0, y0, value, unit, barWidth, minValue);

	    ctx.fillStyle = color;
	    ctx.beginPath();

	    var points = this._setBarHPoints(ctx, p.x0, p.y0, barWidth, radius, unit, p.value, this._settings.border ? 1 : 0);

	    if (gradient && !inner_gradient) ctx.lineTo(point1.x, p.y0 + (this._settings.border ? 1 : 0)); //fix gradient sphreading

	    ctx.fill();
	    ctx.restore();
	    var y1 = p.y0;
	    var y2 = p.y0 != y0 ? y0 : points[1];
	    var x1 = p.y0 != y0 ? p.start - points[0] : p.start;
	    var x2 = p.y0 != y0 ? p.start : points[0];
	    return [x1, y1, x2, y2];
	  },
	  _drawBarHBorder: function (ctx, x0, y0, barWidth, minValue, radius, unit, value, color) {
	    ctx.save();

	    var p = this._correctBarHParams(ctx, x0, y0, value, unit, barWidth, minValue);

	    ctx.beginPath();

	    this._setBorderStyles(ctx, color);

	    ctx.globalAlpha = 0.9;

	    this._setBarHPoints(ctx, p.x0, p.y0, barWidth, radius, unit, p.value, ctx.lineWidth / 2, 1);

	    ctx.stroke();
	    ctx.restore();
	  },
	  _drawBarHGradient: function (ctx, x0, y0, barWidth, minValue, radius, unit, value, color, inner_gradient) {
	    ctx.save();

	    var p = this._correctBarHParams(ctx, x0, y0, value, unit, barWidth, minValue);

	    var gradParam = this._setBarGradient(ctx, p.x0, p.y0 + barWidth, p.x0 + unit * p.value, p.y0, inner_gradient, color, "x");

	    ctx.fillStyle = gradParam.gradient;
	    ctx.beginPath();

	    this._setBarHPoints(ctx, p.x0, p.y0 + gradParam.offset, barWidth - gradParam.offset * 2, radius, unit, p.value, gradParam.offset);

	    ctx.fill();
	    ctx.globalAlpha = 1;
	    ctx.restore();
	  }
	};

	var StackedBarChart = {
	  /**
	  *   renders a bar chart
	  *   @param: ctx - canvas object
	  *   @param: data - object those need to be displayed
	  *   @param: x - the width of the container
	  *   @param: y - the height of the container
	  *   @param: sIndex - index of drawing chart
	  */
	  $render_stackedBar: function (ctx, data, point0, point1, sIndex, map) {
	    var maxValue, minValue, xAxisY, x0, y0;
	    /*necessary if maxValue - minValue < 0*/

	    var valueFactor;
	    /*maxValue - minValue*/

	    var relValue;
	    var config = this._settings;
	    var total_height = point1.y - point0.y;
	    var yax = !!config.yAxis;
	    var xax = !!config.xAxis;

	    var limits = this._getStackedLimits(data);

	    var origin = config.origin === 0;
	    maxValue = limits.max;
	    minValue = limits.min;
	    /*an available width for one bar*/

	    var cellWidth = Math.floor((point1.x - point0.x) / data.length);
	    /*draws x and y scales*/

	    if (!sIndex) {
	      xAxisY = this._drawScales(data, point0, point1, minValue, maxValue, cellWidth);
	    }
	    /*necessary for automatic scale*/


	    if (yax) {
	      maxValue = parseFloat(config.yAxis.end);
	      minValue = parseFloat(config.yAxis.start);
	    }
	    /*unit calculation (bar_height = value*unit)*/


	    var relativeValues = this._getRelativeValue(minValue, maxValue);

	    relValue = relativeValues[0];
	    valueFactor = relativeValues[1];
	    var unit = relValue ? total_height / relValue : 10;
	    /*a real bar width */

	    var barWidth = parseInt(config.barWidth, 10);
	    if (barWidth + 4 > cellWidth) barWidth = cellWidth - 4;
	    /*the half of distance between bars*/

	    var barOffset = Math.floor((cellWidth - barWidth) / 2);
	    var inner_gradient = config.gradient ? config.gradient : false;
	    /*draws a black line if the horizontal scale isn't defined*/

	    if (!xax) {
	      //scaleY = y-bottomPadding;
	      this._drawLine(ctx, point0.x, point1.y + 0.5, point1.x, point1.y + 0.5, "#edeff0", 1); //hardcoded color!

	    }

	    for (var i = 0; i < data.length; i++) {
	      var value = Math.abs(parseFloat(config.value(data[i] || 0)));
	      if (this._logScaleCalc) value = this._log10(value);
	      /*start point (bottom left)*/

	      x0 = point0.x + barOffset + i * cellWidth;
	      var negValue = origin && value < 0;

	      if (!sIndex) {
	        y0 = xAxisY - 1;
	        data[i].$startY = y0;

	        if (origin) {
	          if (negValue) y0 = xAxisY + 1;
	          data[i].$startYN = xAxisY + 1;
	        }
	      } else {
	        y0 = negValue ? data[i].$startYN : data[i].$startY;
	      }

	      if (!value || isNaN(value)) continue;
	      /*adjusts the first tab to the scale*/

	      if (!sIndex && !origin) value -= minValue;
	      value *= valueFactor;
	      /*the max height limit*/

	      if (y0 < point0.y + 1) continue;

	      var color = this._settings.color.call(this, data[i]);

	      var firstSector = Math.abs(y0 - (origin ? point1.y + minValue * unit : point1.y)) < 3;
	      /*drawing bar body*/

	      ctx.globalAlpha = config.alpha.call(this, data[i]);
	      ctx.fillStyle = ctx.strokeStyle = config.color.call(this, data[i]);
	      ctx.beginPath();
	      var y1 = y0 - unit * value + (firstSector ? negValue ? -1 : 1 : 0);

	      var points = this._setStakedBarPoints(ctx, x0 - (config.border ? 0.5 : 0), y0, barWidth + (config.border ? 0.5 : 0), y1, 0, point0.y);

	      ctx.fill();
	      ctx.stroke();
	      /*gradient*/

	      if (inner_gradient) {
	        ctx.save();

	        var gradParam = this._setBarGradient(ctx, x0, y0, x0 + barWidth, points[1], inner_gradient, color, "y");

	        ctx.fillStyle = gradParam.gradient;
	        ctx.beginPath();
	        points = this._setStakedBarPoints(ctx, x0 + gradParam.offset, y0, barWidth - gradParam.offset * 2, y1, config.border ? 1 : 0, point0.y);
	        ctx.fill();
	        ctx.restore();
	      }
	      /*drawing the gradient border of a bar*/


	      if (config.border) {
	        ctx.save();
	        if (typeof config.border == "string") ctx.strokeStyle = config.border;else this._setBorderStyles(ctx, color);
	        ctx.beginPath();

	        this._setStakedBarPoints(ctx, x0 - 0.5, parseInt(y0, 10) + 0.5, barWidth + 1, parseInt(y1, 10) + 0.5, 0, point0.y, firstSector);

	        ctx.stroke();
	        ctx.restore();
	      }

	      ctx.globalAlpha = 1;
	      /*sets a bar label*/

	      this.canvases[sIndex].renderTextAt(false, true, x0 + Math.floor(barWidth / 2), points[1] + (y0 - points[1]) / 2 - 7, this._settings.label(data[i]));
	      /*defines a map area for a bar*/

	      map.addRect(data[i].id, [x0 - point0.x, points[1] - point0.y, points[0] - point0.x, data[i][negValue ? "$startYN" : "$startY"] - point0.y], sIndex);
	      /*the start position for the next series*/

	      data[i][negValue ? "$startYN" : "$startY"] = points[1];
	    }
	  },

	  /**
	   *   sets points for bar and returns the position of the bottom right point
	   *   @param: ctx - canvas object
	   *   @param: x0 - the x position of start point
	   *   @param: y0 - the y position of start point
	   *   @param: barWidth - bar width
	   *   @param: radius - the rounding radius of the top
	   *   @param: unit - the value defines the correspondence between item value and bar height
	   *   @param: value - item value
	   *   @param: offset - the offset from expected bar edge (necessary for drawing border)
	   *   @param: minY - the minimum y position for the bars ()
	   */
	  _setStakedBarPoints: function (ctx, x0, y0, barWidth, y1, offset, minY, skipBottom) {
	    /*start*/
	    ctx.moveTo(x0, y0);
	    /*maximum height limit*/

	    if (y1 < minY) y1 = minY;
	    ctx.lineTo(x0, y1);
	    var x3 = x0 + barWidth;
	    var y3 = y1;
	    ctx.lineTo(x3, y3);
	    /*right rounding*/

	    /*bottom right point*/

	    var x5 = x0 + barWidth;
	    ctx.lineTo(x5, y0);
	    /*line to the start point*/

	    if (!skipBottom) {
	      ctx.lineTo(x0, y0);
	    } //	ctx.lineTo(x0,0); //IE fix!


	    return [x5, y3];
	  }
	};

	var StackedBarHChart = {
	  /**
	  	*   renders a bar chart
	  	*   @param: ctx - canvas object
	  	*   @param: data - object those need to be displayed
	  	*   @param: x - the width of the container
	  	*   @param: y - the height of the container
	  	*   @param: sIndex - index of drawing chart
	  	*   @param: map - map object
	  	*/
	  $render_stackedBarH: function (ctx, data, point0, point1, sIndex, map) {
	    var maxValue, minValue;
	    /*necessary if maxValue - minValue < 0*/

	    var valueFactor;
	    /*maxValue - minValue*/

	    var relValue;
	    var total_width = point1.x - point0.x;
	    var yax = !!this._settings.yAxis;

	    var limits = this._getStackedLimits(data);

	    maxValue = limits.max;
	    minValue = limits.min;
	    /*an available width for one bar*/

	    var cellWidth = Math.floor((point1.y - point0.y) / data.length);
	    /*draws x and y scales*/

	    if (!sIndex) this._drawHScales(ctx, data, point0, point1, minValue, maxValue, cellWidth);
	    /*necessary for automatic scale*/

	    if (yax) {
	      maxValue = parseFloat(this._settings.xAxis.end);
	      minValue = parseFloat(this._settings.xAxis.start);
	    }
	    /*unit calculation (bar_height = value*unit)*/


	    var relativeValues = this._getRelativeValue(minValue, maxValue);

	    relValue = relativeValues[0];
	    valueFactor = relativeValues[1];
	    var unit = relValue ? total_width / relValue : 10;
	    var startValue = 0;

	    if (!yax) {
	      /*defines start value for better representation of small values*/
	      startValue = 10;
	      unit = relValue ? (total_width - startValue) / relValue : 10;
	    }
	    /*a real bar width */


	    var barWidth = parseInt(this._settings.barWidth, 10);
	    if (barWidth + 4 > cellWidth) barWidth = cellWidth - 4;
	    /*the half of distance between bars*/

	    var barOffset = (cellWidth - barWidth) / 2;
	    /*the radius of rounding in the top part of each bar*/

	    var radius = 0;
	    var inner_gradient = false;
	    var gradient = this._settings.gradient;

	    if (gradient) {
	      inner_gradient = true;
	    }
	    /*draws a black line if the horizontal scale isn't defined*/


	    if (!yax) {
	      this._drawLine(ctx, point0.x - 0.5, point0.y, point0.x - 0.5, point1.y, "#edeff0", 1); //hardcoded color!

	    }

	    var seriesNumber = 0;
	    var seriesIndex = 0;

	    for (i = 0; i < this._series.length; i++) {
	      if (i == sIndex) {
	        seriesIndex = seriesNumber;
	      }

	      if (this._series[i].type == "stackedBarH") seriesNumber++;
	    }

	    for (var i = 0; i < data.length; i++) {
	      if (!seriesIndex) data[i].$startX = point0.x;
	      var value = Math.abs(parseFloat(this._settings.value(data[i] || 0)));
	      if (value > maxValue) value = maxValue;
	      value -= minValue;
	      value *= valueFactor;
	      /*start point (bottom left)*/

	      var x0 = point0.x;
	      var y0 = point0.y + barOffset + i * cellWidth;
	      if (!seriesIndex) data[i].$startX = x0;else x0 = data[i].$startX;
	      if (!value || isNaN(value)) continue;
	      /*takes start value into consideration*/

	      if (!yax) value += startValue / unit;

	      var color = this._settings.color.call(this, data[i]);
	      /*drawing bar body*/


	      ctx.globalAlpha = this._settings.alpha.call(this, data[i]);
	      ctx.fillStyle = this._settings.color.call(this, data[i]);
	      ctx.beginPath();

	      var points = this._setBarHPoints(ctx, x0, y0, barWidth, radius, unit, value, 0);

	      if (gradient && !inner_gradient) ctx.lineTo(point0.x + total_width, y0 + (this._settings.border ? 1 : 0)); //fix gradient sphreading

	      ctx.fill();

	      if (inner_gradient) {
	        var gradParam = this._setBarGradient(ctx, x0, y0 + barWidth, x0, y0, inner_gradient, color, "x");

	        ctx.fillStyle = gradParam.gradient;
	        ctx.beginPath();
	        points = this._setBarHPoints(ctx, x0, y0, barWidth, radius, unit, value, 0);
	        ctx.fill();
	      }
	      /*drawing the gradient border of a bar*/


	      if (this._settings.border) {
	        this._drawBarHBorder(ctx, x0, y0, barWidth, minValue, radius, unit, value, color);
	      }

	      ctx.globalAlpha = 1;
	      /*sets a bar label*/

	      this.canvases[sIndex].renderTextAt("middle", true, data[i].$startX + (points[0] - data[i].$startX) / 2 - 1, y0 + (points[1] - y0) / 2, this._settings.label(data[i]));
	      /*defines a map area for a bar*/

	      map.addRect(data[i].id, [data[i].$startX - point0.x, y0 - point0.y, points[0] - point0.x, points[1] - point0.y], sIndex);
	      /*the start position for the next series*/

	      data[i].$startX = points[0];
	    }
	  }
	};

	var Spline$1 = {
	  /**
	  *   renders a spline chart
	  *   @param: ctx - canvas object
	  *   @param: data - object those need to be displayed
	  *   @param: width - the width of the container
	  *   @param: height - the height of the container
	  *   @param: sIndex - index of drawing chart
	  */
	  $render_spline: function (ctx, data, point0, point1, sIndex, map) {
	    var config, i, items, j, params, sparam, x, x0, x1, x2, y, y1, y2;
	    params = this._calculateLineParams(ctx, data, point0, point1, sIndex);
	    config = this._settings;
	    this._mapStart = point0;
	    /*array of all points*/

	    items = [];
	    /*drawing all items*/

	    if (data.length) {
	      /*getting all points*/
	      x0 = config.offset ? point0.x + params.cellWidth * 0.5 : point0.x;

	      for (i = 0; i < data.length; i++) {
	        y = this._getPointY(data[i], point0, point1, params);

	        if (y || y == "0") {
	          x = !i ? x0 : params.cellWidth * i - 0.5 + x0;
	          items.push({
	            x: x,
	            y: y,
	            v: this._settings.value(data[i]),
	            index: i
	          });
	        }
	      }

	      sparam = this._getSplineParameters(items);

	      for (i = 0; i < items.length; i++) {
	        x1 = items[i].x;
	        y1 = items[i].y;

	        if (i < items.length - 1) {
	          x2 = items[i + 1].x;
	          y2 = items[i + 1].y;

	          for (j = x1; j < x2; j++) {
	            var sY1 = this._getSplineYPoint(j, x1, i, sparam.a, sparam.b, sparam.c, sparam.d);

	            if (sY1 < point0.y) sY1 = point0.y;
	            if (sY1 > point1.y) sY1 = point1.y;

	            var sY2 = this._getSplineYPoint(j + 1, x1, i, sparam.a, sparam.b, sparam.c, sparam.d);

	            if (sY2 < point0.y) sY2 = point0.y;
	            if (sY2 > point1.y) sY2 = point1.y;

	            this._drawLine(ctx, j, sY1, j + 1, sY2, config.line.color(data[i]), config.line.width);
	          }

	          this._drawLine(ctx, x2 - 1, this._getSplineYPoint(j, x1, i, sparam.a, sparam.b, sparam.c, sparam.d), x2, y2, config.line.color(data[i]), config.line.width);
	        }

	        this._drawItem(ctx, x1, y1, data[items[i].index], config.label(data[items[i].index]), sIndex, map);
	      }
	    }
	  },

	  /*gets spline parameter*/
	  _getSplineParameters: function (points) {
	    var a,
	        b,
	        c,
	        d,
	        i,
	        s,
	        u,
	        v,
	        h = [],
	        m = [],
	        n = points.length;

	    for (i = 0; i < n - 1; i++) {
	      h[i] = points[i + 1].x - points[i].x;
	      m[i] = (points[i + 1].y - points[i].y) / h[i];
	    }

	    u = [];
	    v = [];
	    u[0] = 0;
	    u[1] = 2 * (h[0] + h[1]);
	    v[0] = 0;
	    v[1] = 6 * (m[1] - m[0]);

	    for (i = 2; i < n - 1; i++) {
	      u[i] = 2 * (h[i - 1] + h[i]) - h[i - 1] * h[i - 1] / u[i - 1];
	      v[i] = 6 * (m[i] - m[i - 1]) - h[i - 1] * v[i - 1] / u[i - 1];
	    }

	    s = [];
	    s[n - 1] = s[0] = 0;

	    for (i = n - 2; i >= 1; i--) {
	      s[i] = (v[i] - h[i] * s[i + 1]) / u[i];
	    }

	    a = [];
	    b = [];
	    c = [];
	    d = [];

	    for (i = 0; i < n - 1; i++) {
	      a[i] = points[i].y;
	      b[i] = -h[i] * s[i + 1] / 6 - h[i] * s[i] / 3 + (points[i + 1].y - points[i].y) / h[i];
	      c[i] = s[i] / 2;
	      d[i] = (s[i + 1] - s[i]) / (6 * h[i]);
	    }

	    for (i = 0; i < points.length - 1; i++) {
	      if (points[i].v === 0 && points[i + 1].v === 0) {
	        a[i] = points[i].y;
	        d[i] = c[i] = b[i] = 0;
	      }
	    }

	    return {
	      a: a,
	      b: b,
	      c: c,
	      d: d
	    };
	  },

	  /*returns the y position of the spline point */
	  _getSplineYPoint: function (x, xi, i, a, b, c, d) {
	    return a[i] + (x - xi) * (b[i] + (x - xi) * (c[i] + (x - xi) * d[i]));
	  }
	};

	var AreaChart = {
	  /**
	  *   renders an area chart
	  *   @param: ctx - canvas object
	  *   @param: data - object those need to be displayed
	  *   @param: width - the width of the container
	  *   @param: height - the height of the container
	  *   @param: sIndex - index of drawing chart
	  */
	  $render_area: function (ctx, data, point0, point1, sIndex, map) {
	    var align, config, i, mapRect, obj, params, path, res1, res2, x0, x1, y1, x2, y2, y0;
	    params = this._calculateLineParams(ctx, data, point0, point1, sIndex);
	    config = this._settings; //the size of map area

	    mapRect = config.eventRadius || Math.floor(params.cellWidth / 2);

	    if (data.length) {
	      // area points
	      path = []; //the x position of the first item

	      x0 = !config.offset ? point0.x : point0.x + params.cellWidth * 0.5;
	      /*
	       iterates over all data items:
	       calculates [x,y] for area path, adds rect to chart map and renders labels
	       */

	      for (i = 0; i < data.length; i++) {
	        obj = data[i];
	        res2 = this._getPointY(obj, point0, point1, params);
	        x2 = x0 + params.cellWidth * i;

	        if (res2) {
	          y2 = _typeof(res2) == "object" ? res2.y0 : res2;

	          if (i && this._settings.fixOverflow) {
	            res1 = this._getPointY(data[i - 1], point0, point1, params);

	            if (res1.out && res1.out == res2.out) {
	              continue;
	            }

	            x1 = params.cellWidth * (i - 1) - 0.5 + x0;
	            y1 = _typeof(res1) == "object" ? res1.y0 : res1;

	            if (res1.out) {
	              y0 = res1.out == "min" ? point1.y : point0.y;
	              path.push([this._calcOverflowX(x1, x2, y1, y2, y0), y0]);
	            }

	            if (res2.out) {
	              y0 = res2.out == "min" ? point1.y : point0.y;
	              path.push([this._calcOverflowX(x1, x2, y1, y2, y0), y0]);
	              if (i == data.length - 1 && y0 == point0.y) path.push([x2, point0.y]);
	            }
	          }

	          if (!res2.out) {
	            path.push([x2, y2]); //map

	            map.addRect(obj.id, [x2 - mapRect - point0.x, y2 - mapRect - point0.y, x2 + mapRect - point0.x, y2 + mapRect - point0.y], sIndex);
	          } //labels


	          if (!config.yAxis) {
	            align = !config.offset && i == data.length - 1 ? "left" : "center";
	            this.canvases[sIndex].renderTextAt(false, align, x2, y2 - config.labelOffset, config.label(obj));
	          }
	        }
	      }

	      if (path.length) {
	        path.push([x2, point1.y]);
	        path.push([path[0][0], point1.y]);
	      } //filling area


	      ctx.globalAlpha = this._settings.alpha.call(this, data[0]);
	      ctx.fillStyle = this._settings.color.call(this, data[0]);
	      ctx.beginPath();

	      this._path(ctx, path);

	      ctx.fill();
	      ctx.lineWidth = 1;
	      ctx.globalAlpha = 1; //border

	      if (config.border) {
	        ctx.lineWidth = config.borderWidth || 1;
	        if (config.borderColor) ctx.strokeStyle = config.borderColor.call(this, data[0]);else this._setBorderStyles(ctx, ctx.fillStyle);
	        ctx.beginPath();

	        this._path(ctx, path);

	        ctx.stroke();
	      }
	    }
	  },

	  /**
	  *   renders an area chart
	  *   @param: ctx - canvas object
	  *   @param: data - object those need to be displayed
	  *   @param: width - the width of the container
	  *   @param: height - the height of the container
	  *   @param: sIndex - index of drawing chart
	  */
	  $render_stackedArea: function (ctx, data, point0, point1, sIndex, map) {
	    var a0, a1, align, config, i, j, lastItem, mapRect, obj, params, path, x, y, yPos;
	    params = this._calculateLineParams(ctx, data, point0, point1, sIndex);
	    config = this._settings;
	    /*the value that defines the map area position*/

	    mapRect = config.eventRadius || Math.floor(params.cellWidth / 2);
	    /*drawing all items*/

	    if (data.length) {
	      // area points
	      path = []; // y item positions

	      yPos = []; //the x position of the first item

	      x = !config.offset ? point0.x : point0.x + params.cellWidth * 0.5;

	      var setOffset = function (i, y) {
	        return sIndex ? data[i].$startY ? y - point1.y + data[i].$startY : 0 : y;
	      };

	      var solveEquation = function (x, p0, p1) {
	        var k = (p1.y - p0.y) / (p1.x - p0.x);
	        return k * x + p0.y - k * p0.x;
	      };
	      /*
	       iterates over all data items:
	       calculates [x,y] for area path, adds rect to chart map and renders labels
	       */


	      for (i = 0; i < data.length; i++) {
	        obj = data[i];

	        if (!i) {
	          y = setOffset(i, point1.y);
	          path.push([x, y]);
	        } else {
	          x += params.cellWidth;
	        }

	        y = setOffset(i, this._getPointY(obj, point0, point1, params));
	        yPos.push(isNaN(y) && !i ? data[i].$startY || point1.y : y);

	        if (y) {
	          path.push([x, y]); //map

	          map.addRect(obj.id, [x - mapRect - point0.x, y - mapRect - point0.y, x + mapRect - point0.x, y + mapRect - point0.y], sIndex); //labels

	          if (!config.yAxis) {
	            align = !config.offset && lastItem ? "left" : "center";
	            this.canvases[sIndex].renderTextAt(false, align, x, y - config.labelOffset, config.label(obj));
	          }
	        }
	      } // bottom right point


	      path.push([x, setOffset(i - 1, point1.y)]); // lower border from the end to start

	      if (sIndex) {
	        for (i = data.length - 2; i > 0; i--) {
	          x -= params.cellWidth;
	          y = data[i].$startY;
	          if (y) path.push([x, y]);
	        }
	      } // go to start point


	      path.push([path[0][0], path[0][1]]); // filling path

	      ctx.globalAlpha = this._settings.alpha.call(this, data[0]);
	      ctx.fillStyle = this._settings.color.call(this, data[0]);
	      ctx.beginPath();

	      this._path(ctx, path);

	      ctx.fill(); // set y positions of the next series

	      for (i = 0; i < data.length; i++) {
	        y = yPos[i];

	        if (!y) {
	          if (i == data.length - 1) {
	            y = data[i].$startY;
	          }

	          for (j = i + 1; j < data.length; j++) {
	            if (yPos[j]) {
	              a0 = {
	                x: point0.x,
	                y: yPos[0]
	              };
	              a1 = {
	                x: point0.x + params.cellWidth * j,
	                y: yPos[j]
	              };
	              y = solveEquation(point0.x + params.cellWidth * i, a0, a1);
	              break;
	            }
	          }
	        }

	        data[i].$startY = y;
	      }
	    }
	  }
	};

	var Radar = {
	  $render_radar: function (ctx, data, x, y, sIndex, map) {
	    this._renderRadarChart(ctx, data, x, y, sIndex, map);
	  },

	  /**
	  *   renders a pie chart
	  *   @param: ctx - canvas object
	  *   @param: data - object those need to be displayed
	  *   @param: x - the width of the container
	  *   @param: y - the height of the container
	  *   @param: ky - value from 0 to 1 that defines an angle of inclination (0<ky<1 - 3D chart)
	  */
	  _renderRadarChart: function (ctx, data, point0, point1, sIndex, map) {
	    if (!data.length) return;

	    var coord = this._getPieParameters(point0, point1);
	    /*scale radius*/


	    var radius = this._settings.radius ? this._settings.radius : coord.radius;
	    /*scale center*/

	    var x0 = this._settings.x ? this._settings.x : coord.x;
	    var y0 = this._settings.y ? this._settings.y : coord.y;
	    /*angles for each unit*/

	    var ratioUnits = [];

	    for (var i = 0; i < data.length; i++) {
	      ratioUnits.push(1);
	    }

	    var ratios = this._getRatios(ratioUnits, data.length);

	    this._mapStart = point0;
	    if (!sIndex) this._drawRadarAxises(ratios, x0, y0, radius, data);

	    this._drawRadarData(ctx, ratios, x0, y0, radius, data, sIndex, map);
	  },
	  _drawRadarData: function (ctx, ratios, x, y, radius, data, sIndex, map) {
	    var alpha0, alpha1, config, i, min, max, pos0, pos1, posArr, r0, r1, relValue, startAlpha, value, value0, value1, valueFactor, unit, unitArr;
	    config = this._settings;
	    /*unit calculation (item_radius_pos = value*unit)*/

	    min = config.yAxis.start;
	    max = config.yAxis.end;
	    unitArr = this._getRelativeValue(min, max);
	    relValue = unitArr[0];
	    unit = relValue ? radius / relValue : radius / 2;
	    valueFactor = unitArr[1];
	    startAlpha = -Math.PI / 2;
	    alpha0 = alpha1 = startAlpha;
	    posArr = [];
	    pos1 = 0;

	    for (i = 0; i < data.length; i++) {
	      if (!value1) {
	        value = config.value(data[i]);
	        if (this._logScaleCalc) value = this._log10(value);
	        /*a relative value*/

	        value0 = (parseFloat(value || 0) - min) * valueFactor;
	      } else value0 = value1;

	      r0 = Math.floor(unit * value0);
	      value = config.value(i != data.length - 1 ? data[i + 1] : data[0]);
	      if (this._logScaleCalc) value = this._log10(value);
	      value1 = (parseFloat(value || 0) - min) * valueFactor;
	      r1 = Math.floor(unit * value1);
	      alpha0 = alpha1;
	      alpha1 = i != data.length - 1 ? startAlpha + ratios[i] - 0.0001 : startAlpha;
	      pos0 = pos1 || this._getPositionByAngle(alpha0, x, y, r0);
	      pos1 = this._getPositionByAngle(alpha1, x, y, r1);
	      /*creates map area*/

	      /*areaWidth  = (config.eventRadius||(parseInt(config.item.radius.call(this,data[i]),10)+config.item.borderWidth));
	       map.addRect(data[i].id,[pos0.x-areaWidth,pos0.y-areaWidth,pos0.x+areaWidth,pos0.y+areaWidth],sIndex);*/
	      //this._drawLine(ctx,pos0.x,pos0.y,pos1.x,pos1.y,config.line.color.call(this,data[i]),config.line.width)

	      posArr.push(pos0);
	    }

	    if (config.fill) this._fillRadarChart(ctx, posArr, data);
	    if (!config.disableLines && data.length > 2) this._strokeRadarChart(ctx, posArr, data);
	    if (!config.disableItems || data.length < 3) this._drawRadarItemMarkers(ctx, posArr, data, sIndex, map);
	    posArr = null;
	  },
	  _drawRadarItemMarkers: function (ctx, points, data, sIndex, map) {
	    for (var i = 0; i < points.length; i++) {
	      this._drawItem(ctx, points[i].x, points[i].y, data[i], this._settings.label.call(this, data), sIndex, map);
	    }
	  },
	  _fillRadarChart: function (ctx, points, data) {
	    var pos0, pos1;
	    ctx.globalAlpha = this._settings.alpha.call(this, {});
	    ctx.beginPath();

	    for (var i = 0; i < points.length; i++) {
	      ctx.fillStyle = this._settings.fill.call(this, data[i]);
	      pos0 = points[i];
	      pos1 = points[i + 1] || points[0];

	      if (!i) {
	        ctx.moveTo(pos0.x, pos0.y);
	      }

	      ctx.lineTo(pos1.x, pos1.y);
	    }

	    ctx.fill();
	    ctx.globalAlpha = 1;
	  },
	  _strokeRadarChart: function (ctx, points, data) {
	    var pos0, pos1;

	    for (var i = 0; i < points.length; i++) {
	      pos0 = points[i];
	      pos1 = points[i + 1] || points[0];

	      this._drawLine(ctx, pos0.x, pos0.y, pos1.x, pos1.y, this._settings.line.color.call(this, data[i]), this._settings.line.width);
	    }
	  },
	  _drawRadarAxises: function (ratios, x, y, radius, data) {
	    var configY = this._settings.yAxis;
	    var configX = this._settings.xAxis;
	    var start = configY.start;
	    var end = configY.end;
	    var step = configY.step;
	    var scaleParam = {};
	    var config = this._configYAxis;

	    if (typeof config.step == "undefined" || typeof config.start == "undefined" || typeof config.end == "undefined") {
	      var limits = this._getLimits();

	      scaleParam = this._calculateScale(limits.min, limits.max);
	      start = scaleParam.start;
	      end = scaleParam.end;
	      step = scaleParam.step;
	      configY.end = end;
	      configY.start = start;
	    }

	    var units = [];
	    var i, j, p;
	    var c = 0;
	    var stepHeight = radius * step / (end - start);
	    /*correction for small step*/

	    var power, corr;

	    if (step < 1) {
	      power = Math.min(this._log10(step), start <= 0 ? 0 : this._log10(start));
	      corr = Math.pow(10, -power);
	    }

	    var angles = [];
	    if (!this.canvases["scale"]) this.canvases["scale"] = this._createCanvas("radar_scale");
	    var ctx = this.canvases["scale"].getCanvas();

	    for (i = end; i >= start; i -= step) {
	      var value = this._logScaleCalc ? Math.pow(10, i) : i;
	      if (scaleParam.fixNum) value = parseFloat(i).toFixed(scaleParam.fixNum);
	      units.push(Math.floor(c * stepHeight) + 0.5);

	      if (corr && !this._logScaleCalc) {
	        value = Math.round(value * corr) / corr;
	        i = value;
	      }

	      var unitY = y - radius + units[units.length - 1];
	      this.canvases["scale"].renderTextAt("middle", "left", x, unitY, configY.template(value.toString()), "webix_axis_item_y webix_radar");

	      if (ratios.length < 2) {
	        this._drawScaleSector(ctx, "arc", x, y, radius - units[units.length - 1], -Math.PI / 2, 3 * Math.PI / 2, i);

	        return;
	      }

	      var startAlpha = -Math.PI / 2;
	      /*possibly need  to moved in config*/

	      var alpha0 = startAlpha;
	      var alpha1;

	      for (j = 0; j < ratios.length; j++) {
	        if (!c) angles.push(alpha0);
	        alpha1 = startAlpha + ratios[j] - 0.0001;

	        this._drawScaleSector(ctx, ratios.length > 2 ? config.lineShape || "line" : "arc", x, y, radius - units[units.length - 1], alpha0, alpha1, i, j, data[i]);

	        alpha0 = alpha1;
	      }

	      c++;
	    }
	    /*renders radius lines and labels*/


	    for (i = 0; i < angles.length; i++) {
	      p = this._getPositionByAngle(angles[i], x, y, radius);
	      if (configX.lines.call(this, data[i], i)) this._drawLine(ctx, x, y, p.x, p.y, configX ? configX.lineColor.call(this, data[i]) : "#cfcfcf", 1);

	      this._drawRadarScaleLabel(ctx, x, y, radius, angles[i], configX ? configX.template.call(this, data[i]) : "&nbsp;");
	    }
	  },
	  _drawScaleSector: function (ctx, shape, x, y, radius, a1, a2, i, j) {
	    var pos1, pos2;
	    if (radius < 0) return false;
	    pos1 = this._getPositionByAngle(a1, x, y, radius);
	    pos2 = this._getPositionByAngle(a2, x, y, radius);
	    var configY = this._settings.yAxis;

	    if (configY.bg) {
	      ctx.beginPath();
	      ctx.moveTo(x, y);
	      if (shape == "arc") ctx.arc(x, y, radius, a1, a2, false);else {
	        ctx.lineTo(pos1.x, pos1.y);
	        ctx.lineTo(pos2.x, pos2.y);
	      }
	      ctx.fillStyle = configY.bg(i, j);
	      ctx.moveTo(x, y);
	      ctx.fill();
	      ctx.closePath();
	    }

	    if (configY.lines.call(this, i)) {
	      ctx.lineWidth = 1;
	      ctx.beginPath();
	      if (shape == "arc") ctx.arc(x, y, radius, a1, a2, false);else {
	        ctx.moveTo(pos1.x, pos1.y);
	        ctx.lineTo(pos2.x, pos2.y);
	      }
	      ctx.strokeStyle = configY.lineColor.call(this, i);
	      ctx.stroke();
	    }
	  },
	  _drawRadarScaleLabel: function (ctx, x, y, r, a, text) {
	    if (!text) return false;
	    var t = this.canvases["scale"].renderText(0, 0, text, "webix_axis_radar_title", 1);
	    var width = t.scrollWidth;
	    var height = t.offsetHeight;
	    var delta = 0.001;

	    var pos = this._getPositionByAngle(a, x, y, r + 5);

	    var corr_x = 0,
	        corr_y = 0;

	    if (a < 0 || a > Math.PI) {
	      corr_y = -height;
	    }

	    if (a > Math.PI / 2) {
	      corr_x = -width;
	    }

	    if (Math.abs(a + Math.PI / 2) < delta || Math.abs(a - Math.PI / 2) < delta) {
	      corr_x = -width / 2;
	    } else if (Math.abs(a) < delta || Math.abs(a - Math.PI) < delta) {
	      corr_y = -height / 2;
	    }

	    t.style.top = pos.y + corr_y + "px";
	    t.style.left = pos.x + corr_x + "px";
	    t.style.width = width + "px";
	    t.style.whiteSpace = "nowrap";
	  }
	};

	var Scatter = {
	  /**
	  *   renders a graphic
	  *   @param: ctx - canvas object
	  *   @param: data - object those need to be displayed
	  *   @param: point0  - top left point of a chart
	  *   @param: point1  - right bottom point of a chart
	  *   @param: sIndex - index of drawing chart
	     *   @param: map - map object
	  */
	  $render_scatter: function (ctx, data, point0, point1, sIndex, map) {
	    if (!this._settings.xValue) return;
	    var config = this._settings;
	    var lines = !(config.disableLines || typeof config.disableLines == "undefined");
	    /*max in min values*/

	    var limitsY = this._getLimits();

	    var limitsX = this._getLimits("h", "xValue");
	    /*render scale*/


	    if (!sIndex) {
	      if (!this.canvases["x"]) this.canvases["x"] = this._createCanvas("axis_x");
	      if (!this.canvases["y"]) this.canvases["y"] = this._createCanvas("axis_y");

	      this._drawYAxis(this.canvases["y"].getCanvas(), data, point0, point1, limitsY.min, limitsY.max);

	      this._drawHXAxis(this.canvases["x"].getCanvas(), data, point0, point1, limitsX.min, limitsX.max);
	    }

	    limitsY = {
	      min: config.yAxis.start,
	      max: config.yAxis.end
	    };
	    limitsX = {
	      min: config.xAxis.start,
	      max: config.xAxis.end
	    };

	    var params = this._getScatterParams(ctx, data, point0, point1, limitsX, limitsY);

	    this._mapStart = point0;
	    var items = [];

	    for (var i = 0; i < data.length; i++) {
	      var x = this._calculateScatterItemPosition(params, point1, point0, limitsX, data[i], "X");

	      var y = this._calculateScatterItemPosition(params, point0, point1, limitsY, data[i], "Y");

	      if (isNaN(x) || isNaN(y)) continue;
	      items.push({
	        x: x,
	        y: y,
	        index: i
	      });
	    }

	    var x1, y1, x2, y2, di;

	    for (var _i = 0; _i < items.length; _i++) {
	      di = items[_i].index;

	      if (lines) {
	        var color = config.line.color.call(this, data[di]); //line start position

	        x1 = items[_i].x;
	        y1 = items[_i].y;

	        if (_i == items.length - 1) {
	          //connecting last and first items
	          if (config.shape && items.length > 2) {
	            this._drawLine(ctx, x2, y2, items[0].x, items[0].y, color, config.line.width); //render shape on top of the line


	            if (!config.disableItems) this._drawScatterItem(ctx, map, items[0], data[0], sIndex);
	            if (config.fill) this._fillScatterChart(ctx, items, data);
	          }
	        } else {
	          // line between two points
	          x2 = items[_i + 1].x;
	          y2 = items[_i + 1].y;

	          this._drawLine(ctx, x1, y1, x2, y2, color, config.line.width);
	        }
	      } //item


	      if (!config.disableItems && items[_i]) {
	        this._drawScatterItem(ctx, map, items[_i], data[di], sIndex);
	      }
	    }
	  },
	  _fillScatterChart: function (ctx, points, data) {
	    var pos0, pos1;
	    ctx.globalAlpha = this._settings.alpha.call(this, {});
	    ctx.beginPath();

	    for (var i = 0; i < points.length; i++) {
	      ctx.fillStyle = this._settings.fill.call(this, data[i]);
	      pos0 = points[i];
	      pos1 = points[i + 1] || points[0];

	      if (!i) {
	        ctx.moveTo(pos0.x, pos0.y);
	      }

	      ctx.lineTo(pos1.x, pos1.y);
	    }

	    ctx.fill();
	    ctx.globalAlpha = 1;
	  },
	  _getScatterParams: function (ctx, data, point0, point1, limitsX, limitsY) {
	    var params = {};
	    /*available space*/

	    params.totalHeight = point1.y - point0.y;
	    /*available width*/

	    params.totalWidth = point1.x - point0.x;
	    /*unit calculation (y_position = value*unit)*/

	    this._calcScatterUnit(params, limitsX.min, limitsX.max, params.totalWidth, "X");

	    this._calcScatterUnit(params, limitsY.min, limitsY.max, params.totalHeight, "Y");

	    return params;
	  },
	  _drawScatterItem: function (ctx, map, item, obj, sIndex) {
	    this._drawItem(ctx, item.x, item.y, obj, this._settings.label.call(this, obj), sIndex, map);
	  },
	  _calculateScatterItemPosition: function (params, point0, point1, limits, obj, axis) {
	    /*the real value of an object*/
	    var value = this._settings[axis == "X" ? "xValue" : "value"].call(this, obj);
	    /*a relative value*/


	    var valueFactor = params["valueFactor" + axis];
	    var v = (parseFloat(value || 0) - limits.min) * valueFactor;
	    /*a vertical coordinate*/

	    var unit = params["unit" + axis];
	    var pos = point1[axis.toLowerCase()] - (axis == "X" ? -1 : 1) * Math.floor(unit * v);
	    /*the limit of the minimum value is  the minimum visible value*/

	    if (v < 0) pos = point1[axis.toLowerCase()];
	    /*the limit of the maximum value*/

	    if (value > limits.max) pos = point0[axis.toLowerCase()];
	    /*the limit of the minimum value*/

	    if (value < limits.min) pos = point1[axis.toLowerCase()];
	    return pos;
	  },
	  _calcScatterUnit: function (p, min, max, size, axis) {
	    var relativeValues = this._getRelativeValue(min, max);

	    axis = axis || "";
	    p["relValue" + axis] = relativeValues[0];
	    p["valueFactor" + axis] = relativeValues[1];
	    p["unit" + axis] = p["relValue" + axis] ? size / p["relValue" + axis] : 10;
	  }
	};

	var Presets = {
	  presets: {
	    "simple": {
	      item: {
	        borderColor: "#ffffff",
	        color: "#2b7100",
	        shadow: false,
	        borderWidth: 2
	      },
	      line: {
	        color: "#8ecf03",
	        width: 2
	      }
	    },
	    "plot": {
	      color: "#8664C6",
	      item: {
	        borderColor: "#8664C6",
	        borderWidth: 1,
	        color: "#ffffff",
	        type: "r",
	        shadow: false
	      },
	      line: {
	        color: "#8664C6",
	        width: 2
	      }
	    },
	    "diamond": {
	      color: "#FF5C4C",
	      item: {
	        borderColor: "#FF5C4C",
	        color: "#FF5C4C",
	        type: "d",
	        radius: 3,
	        shadow: true
	      },
	      line: {
	        color: "#FF5C4C",
	        width: 2
	      }
	    },
	    "point": {
	      color: "#1ca1c1",
	      disableLines: true,
	      fill: false,
	      disableItems: false,
	      item: {
	        color: "#1ca1c1",
	        borderColor: "#1ca1c1",
	        radius: 2,
	        borderWidth: 2,
	        type: "r"
	      },
	      alpha: 1
	    },
	    "line": {
	      line: {
	        color: "#1ca1c1",
	        width: 2
	      },
	      item: {
	        color: "#ffffff",
	        borderColor: "#1ca1c1",
	        radius: 2,
	        borderWidth: 2,
	        type: "d"
	      },
	      fill: false,
	      disableItems: false,
	      disableLines: false,
	      alpha: 1
	    },
	    "area": {
	      fill: "#1ca1c1",
	      line: {
	        color: "#1ca1c1",
	        width: 1
	      },
	      disableItems: true,
	      alpha: 0.2,
	      disableLines: false
	    },
	    "round": {
	      item: {
	        radius: 3,
	        borderColor: "#1ca1c1",
	        borderWidth: 1,
	        color: "#1ca1c1",
	        type: "r",
	        shadow: false,
	        alpha: 0.6
	      }
	    },
	    "square": {
	      item: {
	        radius: 3,
	        borderColor: "#00a497",
	        borderWidth: 2,
	        color: "#ffffff",
	        type: "s",
	        shadow: false,
	        alpha: 1
	      },
	      line: {
	        color: "#00a497"
	      }
	    },

	    /*bar*/
	    "column": {
	      color: "RAINBOW",
	      gradient: false,
	      barWidth: 45,
	      radius: 0,
	      alpha: 1,
	      border: true
	    },
	    "stick": {
	      barWidth: 5,
	      gradient: false,
	      color: "#1ca1c1",
	      radius: 2,
	      alpha: 1,
	      border: false
	    },
	    "alpha": {
	      color: "#b9a8f9",
	      barWidth: 70,
	      gradient: "falling",
	      radius: 0,
	      alpha: 0.5,
	      border: true
	    }
	  }
	};

	var SplineArea$1 = {
	  /**
	   *   renders an splineArea chart
	   *   @param: ctx - canvas object
	   *   @param: data - object those need to be displayed
	   *   @param: width - the width of the container
	   *   @param: height - the height of the container
	   *   @param: sIndex - index of drawing chart
	   */
	  $render_splineArea: function (ctx, data, point0, point1, sIndex, map) {
	    var color,
	        i,
	        items,
	        j,
	        mapRect,
	        params,
	        sParams,
	        x,
	        x0,
	        x1,
	        x2,
	        y,
	        y2,
	        config = this._settings,
	        path = [];
	    params = this._calculateLineParams(ctx, data, point0, point1, sIndex);
	    mapRect = config.eventRadius || Math.floor(params.cellWidth / 2);
	    /*array of all points*/

	    items = [];

	    if (data.length) {
	      /*getting all points*/
	      x0 = point0.x;

	      for (i = 0; i < data.length; i++) {
	        y = this._getPointY(data[i], point0, point1, params);

	        if (y || y == "0") {
	          x = !i ? x0 : params.cellWidth * i - 0.5 + x0;
	          items.push({
	            x: x,
	            y: y,
	            index: i
	          });
	          map.addRect(data[i].id, [x - mapRect - point0.x, y - mapRect - point0.y, x + mapRect - point0.x, y + mapRect - point0.y], sIndex);
	        }
	      }

	      sParams = this._getSplineParameters(items);

	      for (i = 0; i < items.length; i++) {
	        x1 = items[i].x;

	        if (i < items.length - 1) {
	          x2 = items[i + 1].x;
	          y2 = items[i + 1].y;

	          for (j = x1; j < x2; j++) {
	            var sY1 = this._getSplineYPoint(j, x1, i, sParams.a, sParams.b, sParams.c, sParams.d);

	            if (sY1 < point0.y) sY1 = point0.y;
	            if (sY1 > point1.y) sY1 = point1.y;

	            var sY2 = this._getSplineYPoint(j + 1, x1, i, sParams.a, sParams.b, sParams.c, sParams.d);

	            if (sY2 < point0.y) sY2 = point0.y;
	            if (sY2 > point1.y) sY2 = point1.y;
	            path.push([j, sY1]);
	            path.push([j + 1, sY2]);
	          }

	          path.push([x2, y2]);
	        }
	      }

	      color = this._settings.color.call(this, data[0]);

	      if (path.length) {
	        path.push([x2, point1.y]);
	        path.push([path[0][0], point1.y]);
	      } //filling area


	      ctx.globalAlpha = this._settings.alpha.call(this, data[0]);
	      ctx.fillStyle = color;
	      ctx.beginPath();

	      this._path(ctx, path);

	      ctx.fill();
	      ctx.lineWidth = 1;
	      ctx.globalAlpha = 1; // draw line

	      if (config.border) {
	        ctx.lineWidth = config.borderWidth || 1;
	        if (config.borderColor) ctx.strokeStyle = config.borderColor.call(this, data[0]);else this._setBorderStyles(ctx, color);
	        ctx.beginPath();
	        path.splice(path.length - 3);

	        this._path(ctx, path);

	        ctx.stroke();
	      }
	    }
	  }
	};

	var animateDuration = 400,
	    cellWidth = 30;
	var DynamicChart = {
	  dynamic_setter: function (value) {
	    if (value) init(this);
	    return value;
	  }
	};
	/**
	 * Sets event handlers and properties for a stock chart
	 * @param {object} chart - chart view
	 */

	function init(chart) {
	  if (chart._stockRenderHandler) return;
	  var config = chart._settings;
	  if (!config.cellWidth) config.cellWidth = cellWidth;
	  if (!config.animateDuration) config.animateDuration = animateDuration;
	  config.offset = false;
	  chart._stockRenderHandler = chart.attachEvent("onBeforeRender", function (data, type) {
	    var bounds = chart._getChartBounds(chart._content_width, chart._content_height);

	    resizeStockCanvases(chart);
	    filterStockData(data, bounds.start, bounds.end, config.cellWidth);
	    if (type == "add") startAnimation(chart);
	  });
	  chart._stockXAxisHandler = chart.attachEvent("onBeforeXAxis", function (ctx, data, point0, point1, cellWidth, y) {
	    drawXAxis(chart, ctx, data, point0, point1, cellWidth, y);
	    return false;
	  });
	}
	/**
	 * Starts stock animation
	 * @param {object} chart - chart view
	 */


	function startAnimation(chart) {
	  var cellWidth = chart._settings.cellWidth;

	  if (chart._stockAnimationOffset != cellWidth) {
	    chart._stockAnimationOffset = cellWidth;
	    chart.render();
	  }

	  chart._stockAnimationOffset = 0;
	  chart._stockAnimationStart = null;
	  if (window.requestAnimationFrame && !document.hidden) window.requestAnimationFrame(function (t) {
	    animate$2(chart, t);
	  });
	  if (!chart._stockAnimateHandler) chart._stockAnimateHandler = chart.attachEvent("onAfterRender", function (data) {
	    applyStockOffset(chart, data);
	  });
	}
	/**
	 * Animates a chart
	 * @param {object} chart - chart view
	 * @param {number} timestamp - timestamp
	 */


	function animate$2(chart, timestamp) {
	  var progress,
	      duration = chart._settings.animateDuration,
	      cellWidth = chart._settings.cellWidth;

	  if (cellWidth && chart.count() > 1) {
	    if (!chart._stockAnimationStart) chart._stockAnimationStart = timestamp;
	    progress = timestamp - chart._stockAnimationStart;
	    chart._stockAnimationOffset = Math.min(Math.max(progress / duration * cellWidth, 1), cellWidth);
	    chart.render();
	    if (progress < duration) window.requestAnimationFrame(function (t) {
	      animate$2(chart, t);
	    });
	  }
	}
	/**
	 * Applies animation offset to "series" and "x-axis" canvases
	 * @param {object} chart - chart view
	 * @param {object} data - data array
	 */


	function applyStockOffset(chart, data) {
	  var count = chart.count(),
	      bounds = chart._getChartBounds(chart._content_width, chart._content_height),
	      cellWidth = chart._settings.cellWidth,
	      offset$$1 = chart._stockAnimationOffset || 0,
	      isScroll = data.length < count || (data.length - 1) * cellWidth > bounds.end.x - bounds.start.x;

	  function setCanvasOffset(canvas, x0, x1, skipRight) {
	    var ctx = canvas.getCanvas(),
	        elem = canvas._canvas,
	        labels = canvas._canvas_labels,
	        series = canvas._canvas_series; // if we need to display less values than they are

	    if (offset$$1 && (data.length < count || (data.length - 1) * cellWidth > x1 - x0)) {
	      // move canvas to the left
	      elem.style.left = -offset$$1 + "px";

	      if (data.length > 1) {
	        setLabelsOffset(labels, offset$$1, series); // clear out of the scale parts

	        ctx.clearRect(0, 0, x0 + offset$$1, elem.offsetHeight);
	        ctx.clearRect(x1 + offset$$1, 0, elem.offsetWidth, elem.offsetHeight);
	      }
	    } // animation for the right part (added item)
	    else {
	        elem.style.left = "0px";
	        if (!skipRight && offset$$1 != cellWidth) ctx.clearRect(x0 + (data.length - 1) * cellWidth - cellWidth + offset$$1, 0, elem.offsetWidth, elem.offsetHeight);
	      } // show label for the last label after finishing animation


	    if (labels.length > 1 && offset$$1 && offset$$1 != cellWidth) {
	      var last = labels.length - 1;
	      if (isAxisTitle(series, labels[last])) last -= 1;
	      labels[last].style.display = "none";
	    }
	  }

	  eachStockCanvas(chart, function (name, canvas) {
	    setCanvasOffset(canvas, bounds.start.x, bounds.end.x, name == "x");
	  });
	  setHtmlMapSizes(chart, bounds, isScroll ? offset$$1 : 0);
	}

	function isAxisTitle(series, label) {
	  return series === "axis_x" && label.className.indexOf("webix_axis_title_x") !== -1;
	}

	function setLabelsOffset(labels, offset$$1, series) {
	  if (labels.length) {
	    remove(labels[0]);

	    for (var i = 1; i < labels.length; i++) {
	      //don't move axis title
	      if (isAxisTitle(series, labels[i])) continue;
	      labels[i].style.left = labels[i].offsetLeft - offset$$1 + "px";
	    }
	  }
	}
	/**
	 * Gets visible chart data
	 * @param {object} data - an array with all chart data
	 * @param {object} point0 - a top left point of a plot
	 * @param {object} point1 - a bottom right point of a plot
	 * @param {number} cellWidth - a unit width
	 */


	function filterStockData(data, point0, point1, cellWidth) {
	  if (cellWidth && data.length) {
	    var limit = Math.ceil((point1.x - point0.x) / cellWidth);
	    if (data.length > limit + 1) data.splice(0, data.length - limit - 1);
	  }
	}
	/**
	 * Calls a function for "series" and "x-axis" canvases
	 * @param {object} chart - chart view
	 * @param {function} func - function to call
	 */


	function eachStockCanvas(chart, func) {
	  if (chart.canvases) {
	    for (var i = 0; i < chart._series.length; i++) {
	      if (chart.canvases[i]) func(i, chart.canvases[i]);
	    }

	    if (chart.canvases["x"]) func("x", chart.canvases["x"]);
	  }
	}
	/**
	 * Set sizes for animated canvases
	 * @param {object} chart - chart view
	 */


	function resizeStockCanvases(chart) {
	  eachStockCanvas(chart, function (name, canvas) {
	    canvas._resizeCanvas(chart._content_width + 2 * chart._settings.cellWidth, chart._content_height);
	  });
	}
	/**
	 * Set sizes for an html map of a chart
	 * @param {object} chart - a chart view
	 * @param {object} bounds - start and end points of a plot
	 * @param {number} offset - an offset to apply
	 */


	function setHtmlMapSizes(chart, bounds, offset$$1) {
	  chart._contentobj._htmlmap.style.left = bounds.start.x - offset$$1 + "px";
	  chart._contentobj._htmlmap.style.width = bounds.end.x - bounds.start.x + offset$$1 + "px";
	}
	/**
	 * Renders lines and labels of an x-axis
	 * @param {object} chart - a chart view
	 * @param {object} ctx - a canvas Context
	 * @param {object} data - a data array
	 * @param {object} point0 - a top left point of a plot
	 * @param {object} point1 - a bottom right point of a plot
	 * @param {number} cellWidth - a width of a unit
	 * @param {number} y - the vertical position of an "x-axis" line
	 */


	function drawXAxis(chart, ctx, data, point0, point1, cellWidth, y) {
	  var center,
	      i,
	      isScroll,
	      unitPos,
	      config = chart._settings,
	      x0 = point0.x - 0.5,
	      y0 = parseInt(y ? y : point1.y, 10) + 0.5,
	      x1 = point1.x;
	  if (!config.dynamic) return false;
	  isScroll = (data.length - 1) * cellWidth > x1 - x0 || data.length < chart.count();

	  for (i = 0; i < data.length; i++) {
	    unitPos = x0 + i * cellWidth;
	    center = isScroll ? i > 1 : !!i;
	    unitPos = Math.ceil(unitPos) - 0.5; //scale labels

	    chart._drawXAxisLabel(unitPos, y0, data[i], center); //draws a vertical line for the horizontal scale


	    if (i && config.xAxis.lines.call(chart, data[i])) chart._drawXAxisLine(ctx, unitPos, point1.y, point0.y, data[i]);
	  }

	  chart.canvases["x"].renderTextAt(true, false, x0, point1.y + config.padding.bottom - 3, config.xAxis.title, "webix_axis_title_x", point1.x - point0.x);

	  chart._drawLine(ctx, x0, y0, x1 + (isScroll ? chart._stockAnimationOffset : 0), y0, config.xAxis.color, 1);
	}

	var api$Y = {
	  name: "chart",
	  $init: function (config) {
	    this._series = [this._settings];
	    this._legend_labels = [];
	    this._contentobj.className += " webix_chart";
	    this.$ready.push(this._after_init_call);
	    /*preset*/

	    if (config.preset) {
	      this._definePreset(config);
	    } // move series to end of configuration properties hash
	    // so it will be parsed after other settings


	    if (config.series) {
	      var series = config.series;
	      delete config.series;
	      config.series = series;
	    }

	    this.data.provideApi(this, true);
	  },
	  _after_init_call: function () {
	    this.data.attachEvent("onStoreUpdated", bind(function () {
	      this.render.apply(this, arguments);
	    }, this));
	  },
	  defaults: {
	    ariaLabel: "chart",
	    color: "default",
	    alpha: "1",
	    radius: 0,
	    label: false,
	    value: "{obj.value}",
	    padding: {},
	    type: "pie",
	    lineColor: "#ffffff",
	    cant: 0.5,
	    barWidth: 30,
	    line: {
	      width: 2,
	      color: "#1ca1c1"
	    },
	    item: {
	      radius: 3,
	      borderColor: "#1ca1c1",
	      borderWidth: 2,
	      color: "#ffffff",
	      alpha: 1,
	      type: "r",
	      shadow: false
	    },
	    shadow: false,
	    gradient: false,
	    border: false,
	    labelOffset: 20,
	    origin: "auto",
	    scale: "linear"
	  },
	  _id:
	  /*@attr*/
	  "webix_area_id",
	  on_click: {
	    webix_chart_legend_item: function (e, id, obj) {
	      var series = obj.getAttribute(
	      /*@attr*/
	      "series_id");

	      if (this.callEvent("onLegendClick", [e, series, obj])) {
	        if (typeof series != "undefined" && this._series.length > 1) {
	          var config = this._settings;
	          var values = config.legend.values;
	          var toggle = values && values[series].toggle || config.legend.toggle; // hide action

	          if (toggle) {
	            if (obj.className.indexOf("hidden") != -1) {
	              this.showSeries(series);
	            } else {
	              this.hideSeries(series);
	            }
	          }
	        }
	      }
	    }
	  },
	  on_dblclick: {},
	  on_mouse_move: {},
	  locate: function (e) {
	    return locate(e, this._id);
	  },
	  $setSize: function (x, y) {
	    var res = base.api.$setSize.call(this, x, y);

	    if (res) {
	      for (var c in this.canvases) {
	        this.canvases[c]._resizeCanvas(this._content_width, this._content_height);
	      }

	      this.render();
	    }

	    return res;
	  },
	  type_setter: function (val) {
	    assert(this["$render_" + val], "Chart type is not supported, or extension is not loaded: " + val);

	    if (typeof this._settings.offset == "undefined") {
	      this._settings.offset = !(val.toLowerCase().indexOf("area") != -1);
	    }

	    if (val == "radar" && !this._settings.yAxis) this.define("yAxis", {});

	    if (val == "scatter") {
	      if (!this._settings.yAxis) this.define("yAxis", {});
	      if (!this._settings.xAxis) this.define("xAxis", {});
	    }

	    return val;
	  },
	  destructor: function () {
	    this.removeAllSeries();
	    Destruction.destructor.apply(this, arguments);
	  },
	  removeAllSeries: function () {
	    this.clearCanvas();

	    if (this._legendObj) {
	      this._legendObj.innerHTML = "";

	      this._legendObj.parentNode.removeChild(this._legendObj);

	      this._legendObj = null;
	    }

	    if (this.canvases) {
	      this.canvases = {};
	    }

	    this._contentobj.innerHTML = "";
	    this._series = [];
	  },
	  clearCanvas: function () {
	    if (this.canvases && _typeof(this.canvases) == "object") for (var c in this.canvases) {
	      this.canvases[c].clearCanvas();
	    }
	  },
	  render: function (id, changes, type) {
	    var bounds, data, map, temp;
	    if (!this.isVisible(this._settings.id)) return;
	    data = this._getChartData();
	    if (!this.callEvent("onBeforeRender", [data, type])) return;

	    if (this.canvases && _typeof(this.canvases) == "object") {
	      for (var i in this.canvases) {
	        this.canvases[i].clearCanvas();
	      }
	    } else this.canvases = {};

	    if (this._settings.legend) {
	      if (!this.canvases["legend"]) this.canvases["legend"] = this._createCanvas("legend");

	      this._drawLegend(this.data.getRange(), this._content_width, this._content_height);
	    }

	    this._map = map = new HtmlMap(this._id);
	    temp = this._settings;
	    bounds = this._getChartBounds(this._content_width, this._content_height);

	    if (this._series) {
	      for (var _i = 0; _i < this._series.length; _i++) {
	        this._settings = this._series[_i];
	        if (!this.canvases[_i]) this.canvases[_i] = this._createCanvas(this.name + " " + _i, "z-index:" + (2 + _i), null, _i, this._settings.ariaLabel);

	        this["$render_" + this._settings.type](this.canvases[_i].getCanvas(), data, bounds.start, bounds.end, _i, map);
	      }
	    }

	    map.render(this._contentobj);
	    this._contentobj.lastChild.style.zIndex = 100;

	    this._applyBounds(this._contentobj.lastChild, bounds);

	    this.callEvent("onAfterRender", [data]);
	    this._settings = temp; // hide hidden series

	    if (this._settings.legend && this._settings.legend.values) {
	      var series = this._settings.legend.values;

	      for (var _i2 = 0; _i2 < series.length; _i2++) {
	        if (series[_i2].$hidden) this.hideSeries(_i2);
	      }
	    }
	  },
	  _applyBounds: function (elem, bounds) {
	    var style = {};
	    style.left = bounds.start.x;
	    style.top = bounds.start.y;
	    style.width = bounds.end.x - bounds.start.x;
	    style.height = bounds.end.y - bounds.start.y;

	    for (var prop in style) {
	      elem.style[prop] = style[prop] + "px";
	    }
	  },
	  _getChartData: function () {
	    var axis, axisConfig, config, data, i, newData, start, units, value, valuesHash;
	    data = this.data.getRange();
	    axis = this._settings.type.toLowerCase().indexOf("barh") != -1 ? "yAxis" : "xAxis";
	    axisConfig = this._settings[axis];

	    if (axisConfig && axisConfig.units && _typeof(axisConfig.units) == "object") {
	      config = axisConfig.units;
	      units = [];

	      if (typeof config.start != "undefined" && typeof config.end != "undefined" && typeof config.next != "undefined") {
	        start = config.start;

	        while (start <= config.end) {
	          units.push(start);
	          start = config.next.call(this, start);
	        }
	      } else if (Object.prototype.toString.call(config) === "[object Array]") {
	        units = config;
	      }

	      newData = [];

	      if (units.length) {
	        value = axisConfig.value;
	        valuesHash = {};

	        for (i = 0; i < data.length; i++) {
	          valuesHash[value(data[i])] = i;
	        }

	        for (i = 0; i < units.length; i++) {
	          if (typeof valuesHash[units[i]] != "undefined") {
	            data[valuesHash[units[i]]].$unit = units[i];
	            newData.push(data[valuesHash[units[i]]]);
	          } else {
	            newData.push({
	              $unit: units[i]
	            });
	          }
	        }
	      }

	      return newData;
	    }

	    return data;
	  },
	  series_setter: function (config) {
	    if (!config || _typeof(config) != "object") {
	      assert(config, "Chart :: Series must be an array or object");
	    } else {
	      this._parseSettings(!config.length ? config : config[0]);

	      this._series = [this._settings];

	      for (var i = 1; i < config.length; i++) {
	        this.addSeries(config[i]);
	      }
	    }

	    return config;
	  },
	  value_setter: template,
	  xValue_setter: template,
	  yValue_setter: function (config) {
	    this.define("value", config);
	  },
	  alpha_setter: template,
	  label_setter: template,
	  lineColor_setter: template,
	  borderColor_setter: template,
	  pieInnerText_setter: template,
	  gradient_setter: function (config) {
	    if (typeof config != "function" && config && config === true) config = "light";
	    return config;
	  },
	  colormap: {
	    "RAINBOW": function (obj) {
	      var pos$$1 = Math.floor(this.getIndexById(obj.id) / this.count() * 1536);
	      if (pos$$1 == 1536) pos$$1 -= 1;
	      return this._rainbow[Math.floor(pos$$1 / 256)](pos$$1 % 256);
	    },
	    "default": function (obj) {
	      var count = this.count();
	      var colorsCount = this._defColors.length;
	      var i = this.getIndexById(obj.id);

	      if (colorsCount > count) {
	        if (i) {
	          if (i < colorsCount - count) i = this._defColorsCursor + 2;else i = this._defColorsCursor + 1;
	        }

	        this._defColorsCursor = i;
	      } else i = i % colorsCount;

	      return this._defColors[i];
	    }
	  },
	  color_setter: function (value) {
	    return this.colormap[value] || template(value);
	  },
	  fill_setter: function (value) {
	    return !value || value == "0" ? false : template(value);
	  },
	  _definePreset: function (obj) {
	    this.define("preset", obj.preset);
	    delete obj.preset;
	  },
	  preset_setter: function (value) {
	    var a, b, preset;
	    this.defaults = exports.extend({}, this.defaults);
	    preset = this.presets[value];

	    if (_typeof(preset) == "object") {
	      for (a in preset) {
	        if (_typeof(preset[a]) == "object") {
	          if (!this.defaults[a] || _typeof(this.defaults[a]) != "object") {
	            this.defaults[a] = exports.extend({}, preset[a]);
	          } else {
	            this.defaults[a] = exports.extend({}, this.defaults[a]);

	            for (b in preset[a]) {
	              this.defaults[a][b] = preset[a][b];
	            }
	          }
	        } else {
	          this.defaults[a] = preset[a];
	        }
	      }

	      return value;
	    }

	    return false;
	  },
	  legend_setter: function (config) {
	    if (!config) {
	      if (this._legendObj) {
	        this._legendObj.innerHTML = "";
	        this._legendObj = null;
	      }

	      return false;
	    }

	    if (_typeof(config) != "object") //allow to use template string instead of object
	      config = {
	        template: config
	      };

	    this._mergeSettings(config, {
	      width: 150,
	      height: 18,
	      layout: "y",
	      align: "left",
	      valign: "bottom",
	      template: "",
	      toggle: this._settings.type.toLowerCase().indexOf("stacked") != -1 ? "" : "hide",
	      marker: {
	        type: "square",
	        width: 15,
	        height: 15,
	        radius: 3
	      },
	      margin: 4,
	      padding: 3
	    });

	    config.template = template(config.template);
	    return config;
	  },
	  item_setter: function (config) {
	    if (_typeof(config) != "object") config = {
	      color: config,
	      borderColor: config
	    };

	    this._mergeSettings(config, exports.extend({}, this.defaults.item));

	    var settings = ["alpha", "borderColor", "color", "radius", "type"];

	    this._converToTemplate(settings, config);

	    return config;
	  },
	  line_setter: function (config) {
	    if (_typeof(config) != "object") config = {
	      color: config
	    };
	    config = exports.extend(config, this.defaults.line);
	    config.color = template(config.color);
	    return config;
	  },
	  padding_setter: function (config) {
	    if (_typeof(config) != "object") config = {
	      left: config,
	      right: config,
	      top: config,
	      bottom: config
	    };

	    this._mergeSettings(config, {
	      left: 50,
	      right: 20,
	      top: 35,
	      bottom: 40
	    });

	    return config;
	  },
	  xAxis_setter: function (config) {
	    if (!config) return false;
	    if (_typeof(config) != "object") config = {
	      template: config
	    };

	    this._mergeSettings(config, {
	      title: "",
	      color: "#edeff0",
	      lineColor: "#edeff0",
	      template: "{obj}",
	      lines: true
	    });

	    var templates = ["lineColor", "template", "lines"];

	    this._converToTemplate(templates, config);

	    this._configXAxis = exports.extend({}, config);
	    return config;
	  },
	  yAxis_setter: function (config) {
	    this._mergeSettings(config, {
	      title: "",
	      color: "#edeff0",
	      lineColor: "#edeff0",
	      template: "{obj}",
	      lines: true,
	      bg: "#ffffff"
	    });

	    var templates = ["lineColor", "template", "lines", "bg"];

	    this._converToTemplate(templates, config);

	    this._configYAxis = exports.extend({}, config);
	    return config;
	  },
	  _converToTemplate: function (arr, config) {
	    for (var i = 0; i < arr.length; i++) {
	      config[arr[i]] = template(config[arr[i]]);
	    }
	  },
	  _createCanvas: function (name, style, container, index$$1, title) {
	    var params = {
	      container: container || this._contentobj,
	      name: name,
	      title: isUndefined(title) ? name : title || "",
	      series: index$$1,
	      style: style || "",
	      width: this._content_width,
	      height: this._content_height
	    };
	    return new Canvas(params);
	  },
	  _drawScales: function (data, point0, point1, start, end, cellWidth) {
	    var ctx,
	        y = 0;

	    if (this._settings.yAxis) {
	      if (!this.canvases["y"]) this.canvases["y"] = this._createCanvas("axis_y");
	      y = this._drawYAxis(this.canvases["y"].getCanvas(), data, point0, point1, start, end);
	    }

	    if (this._settings.xAxis) {
	      if (!this.canvases["x"]) this.canvases["x"] = this._createCanvas("axis_x");
	      ctx = this.canvases["x"].getCanvas();
	      if (this.callEvent("onBeforeXAxis", [ctx, data, point0, point1, cellWidth, y])) this._drawXAxis(ctx, data, point0, point1, cellWidth, y);
	    }

	    return y;
	  },
	  _drawXAxis: function (ctx, data, point0, point1, cellWidth, y) {
	    var i,
	        unitPos,
	        config = this._settings,
	        x0 = point0.x - 0.5,
	        y0 = parseInt(y ? y : point1.y, 10) + 0.5,
	        x1 = point1.x,
	        center = true,
	        labelY = config.type == "stackedBar" ? point1.y + 0.5 : y0;

	    for (i = 0; i < data.length; i++) {
	      if (config.offset === true) unitPos = x0 + cellWidth / 2 + i * cellWidth;else {
	        unitPos = i == data.length - 1 && !config.cellWidth ? point1.x : x0 + i * cellWidth;
	        center = !!i;
	      }
	      unitPos = Math.ceil(unitPos) - 0.5;
	      /*scale labels*/

	      var top = config.origin != "auto" && config.type == "bar" && parseFloat(config.value(data[i])) < config.origin;

	      this._drawXAxisLabel(unitPos, labelY, data[i], center, top);
	      /*draws a vertical line for the horizontal scale*/


	      if ((config.offset || i || config.cellWidth) && config.xAxis.lines.call(this, data[i])) this._drawXAxisLine(ctx, unitPos, point1.y, point0.y, data[i]);
	    }

	    this.canvases["x"].renderTextAt(true, false, x0, point1.y + config.padding.bottom - 3, config.xAxis.title, "webix_axis_title_x", point1.x - point0.x);

	    this._drawLine(ctx, x0, y0, x1, y0, config.xAxis.color, 1);
	    /*the right border in lines in scale are enabled*/


	    if (!config.xAxis.lines.call(this, {}) || !config.offset) return;

	    this._drawLine(ctx, x1 + 0.5, point1.y, x1 + 0.5, point0.y + 0.5, config.xAxis.lineColor.call(this, {}), 1);
	  },
	  _drawYAxis: function (ctx, data, point0, point1, start, end) {
	    var step;
	    var scaleParam = {};
	    if (!this._settings.yAxis) return;
	    var x0 = point0.x - 0.5;
	    var y0 = point1.y;
	    var y1 = point0.y;
	    var lineX = point1.y + 0.5; //this._drawLine(ctx,x0,y0,x0,y1,this._settings.yAxis.color,1);

	    if (this._settings.yAxis.step) step = parseFloat(this._settings.yAxis.step);

	    if (typeof this._configYAxis.step == "undefined" || typeof this._configYAxis.start == "undefined" || typeof this._configYAxis.end == "undefined") {
	      scaleParam = this._calculateScale(start, end);
	      start = scaleParam.start;
	      end = scaleParam.end;
	      step = scaleParam.step;
	      this._settings.yAxis.end = end;
	      this._settings.yAxis.start = start;
	    } else if (this.config.scale == "logarithmic") this._logScaleCalc = true;

	    this._setYAxisTitle(point0, point1);

	    if (step === 0) return;

	    if (end == start) {
	      return y0;
	    }

	    var stepHeight = (y0 - y1) * step / (end - start);
	    var c = 0;

	    for (var i = start; i <= end; i += step) {
	      var value = this._logScaleCalc ? Math.pow(10, i) : i;
	      if (scaleParam.fixNum) value = parseFloat(value).toFixed(scaleParam.fixNum);
	      var yi = Math.floor(y0 - c * stepHeight) + 0.5;
	      /*canvas line fix*/

	      if (!(i == start && this._settings.origin == "auto") && this._settings.yAxis.lines.call(this, i)) this._drawLine(ctx, x0, yi, point1.x, yi, this._settings.yAxis.lineColor.call(this, i), 1);
	      if (i == this._settings.origin) lineX = yi;
	      /*correction for JS float calculation*/

	      if (step < 1 && !this._logScaleCalc) {
	        var power = Math.min(Math.floor(this._log10(step)), start <= 0 ? 0 : Math.floor(this._log10(start)));
	        var corr = Math.pow(10, -power);
	        value = Math.round(value * corr) / corr;
	        i = value;
	      }

	      this.canvases["y"].renderText(0, yi - 5, this._settings.yAxis.template(value.toString()), "webix_axis_item_y", point0.x - 5);
	      c++;
	    }

	    this._drawLine(ctx, x0, y0 + 1, x0, y1, this._settings.yAxis.color, 1);

	    return lineX;
	  },
	  _setYAxisTitle: function (point0, point1) {
	    var className = "webix_axis_title_y";
	    var text = this.canvases["y"].renderTextAt("middle", false, 0, parseInt((point1.y - point0.y) / 2 + point0.y, 10), this._settings.yAxis.title, className);
	    if (text) text.style.left = (env.transform ? (text.offsetHeight - text.offsetWidth) / 2 : 0) + "px";
	  },
	  _calculateLogScale: function (nmin, nmax) {
	    var startPower = Math.floor(this._log10(nmin));
	    var endPower = Math.ceil(this._log10(nmax));
	    return {
	      start: startPower,
	      step: 1,
	      end: endPower
	    };
	  },
	  _normStep: function (step) {
	    var power = Math.floor(this._log10(step));
	    var calculStep = Math.pow(10, power);
	    var stepVal = step / calculStep;
	    stepVal = stepVal > 5 ? 10 : 5;
	    return parseInt(stepVal, 10) * calculStep;
	  },
	  _calculateScale: function (nmin, nmax) {
	    this._logScaleCalc = false;

	    if (this._settings.scale == "logarithmic") {
	      var logMin = Math.floor(this._log10(nmin));
	      var logMax = Math.ceil(this._log10(nmax));

	      if (nmin > 0 && nmax > 0 && logMax - logMin > 1) {
	        this._logScaleCalc = true;
	        return this._calculateLogScale(nmin, nmax);
	      }
	    }

	    if (this._settings.origin != "auto" && this._settings.origin < nmin) nmin = this._settings.origin;
	    var start, end;

	    var step = this._normStep((nmax - nmin) / 8 || 1);

	    var power = Math.floor(this._log10(step));
	    if (step > Math.abs(nmin)) start = nmin < 0 ? -step : 0;else {
	      var absNmin = Math.abs(nmin);
	      var powerStart = Math.floor(this._log10(absNmin));
	      var nminVal = absNmin / Math.pow(10, powerStart);
	      start = Math.ceil(nminVal * 10) / 10 * Math.pow(10, powerStart) - step;

	      if (absNmin > 1 && step > 0.1) {
	        start = Math.ceil(start);
	      }

	      while (nmin < 0 ? start <= nmin : start >= nmin) {
	        start -= step;
	      }

	      if (nmin < 0) start = -start - 2 * step;
	      start = start - start % step;
	      start = parseFloat((start * 1.0).toFixed(Math.abs(power)));
	    }
	    if (nmax - start > 10) step = this._normStep((nmax - start) / 8 || 1);
	    end = start;

	    while (end < nmax) {
	      end += step;
	      end = parseFloat((end * 1.0).toFixed(Math.abs(power)));
	    }

	    return {
	      start: start,
	      end: end,
	      step: step,
	      fixNum: power < 0 ? Math.abs(power) : 0
	    };
	  },
	  _getLimits: function (orientation, value) {
	    var data = this.data._obj_array();

	    var maxValue, minValue;
	    var axis = arguments.length && orientation == "h" ? this._configXAxis : this._configYAxis;
	    value = value || "value";

	    if (axis && typeof axis.end != "undefined" && typeof axis.start != "undefined" && axis.step) {
	      maxValue = parseFloat(axis.end);
	      minValue = parseFloat(axis.start);
	    } else {
	      maxValue = GroupMethods.max(this._series[0][value], data);
	      minValue = axis && typeof axis.start != "undefined" ? parseFloat(axis.start) : GroupMethods.min(this._series[0][value], data);
	      if (this._series.length > 1) for (var i = 1; i < this._series.length; i++) {
	        var maxI = GroupMethods.max(this._series[i][value], data);
	        var minI = GroupMethods.min(this._series[i][value], data);
	        if (maxI > maxValue) maxValue = maxI;
	        if (minI < minValue) minValue = minI;
	      }
	    }

	    return {
	      max: maxValue,
	      min: minValue
	    };
	  },
	  _log10: function (n) {
	    var method_name = "log";
	    return Math[method_name](n) / Math.LN10;
	  },
	  _drawXAxisLabel: function (x, y, obj, center, top) {
	    if (!this._settings.xAxis) return;
	    var elem = this.canvases["x"].renderTextAt(top, center, x, y - (top ? 2 : 0), this._settings.xAxis.template(obj));
	    if (elem) elem.className += " webix_axis_item_x";
	  },
	  _drawXAxisLine: function (ctx, x, y1, y2, obj) {
	    if (!this._settings.xAxis || !this._settings.xAxis.lines) return;

	    this._drawLine(ctx, x, y1, x, y2, this._settings.xAxis.lineColor.call(this, obj), 1);
	  },
	  _drawLine: function (ctx, x1, y1, x2, y2, color$$1, width) {
	    ctx.strokeStyle = color$$1;
	    ctx.lineWidth = width;
	    ctx.beginPath();
	    ctx.moveTo(x1, y1);
	    ctx.lineTo(x2, y2);
	    ctx.stroke();
	    ctx.lineWidth = 1;
	  },
	  _getRelativeValue: function (minValue, maxValue) {
	    var relValue;
	    var valueFactor = 1;

	    if (maxValue != minValue) {
	      relValue = maxValue - minValue;
	    } else relValue = minValue;

	    return [relValue, valueFactor];
	  },
	  _rainbow: [function (pos$$1) {
	    return "#FF" + color.toHex(pos$$1 / 2, 2) + "00";
	  }, function (pos$$1) {
	    return "#FF" + color.toHex(pos$$1 / 2 + 128, 2) + "00";
	  }, function (pos$$1) {
	    return "#" + color.toHex(255 - pos$$1, 2) + "FF00";
	  }, function (pos$$1) {
	    return "#00FF" + color.toHex(pos$$1, 2);
	  }, function (pos$$1) {
	    return "#00" + color.toHex(255 - pos$$1, 2) + "FF";
	  }, function (pos$$1) {
	    return "#" + color.toHex(pos$$1, 2) + "00FF";
	  }],
	  _defColors: ["#f55b50", "#ff6d3f", "#ffa521", "#ffc927", "#ffee54", "#d3e153", "#9acb61", "#63b967", "#21a497", "#21c5da", "#3ea4f5", "#5868bf", "#7b53c0", "#a943ba", "#ec3b77", "#9eb0b8"],
	  _defColorsCursor: 0,

	  /**
	  *   adds series to the chart (value and color properties)
	  *   @param: obj - obj with configuration properties
	  */
	  addSeries: function (obj) {
	    var temp = exports.extend({}, this._settings);
	    this._settings = exports.extend({}, temp);

	    this._parseSettings(obj);

	    this._series.push(this._settings);

	    this._settings = temp;
	  },
	  $tooltipIn: function (t) {
	    return t;
	  },
	  _get_tooltip_data: function (t, e) {
	    var id = this.locate(e);
	    if (!id) return null;

	    var active = this._getActiveSeries(e);

	    var def = exports.extend({
	      dx: 20,
	      dy: 0,
	      template: "{obj.value}",
	      css: ""
	    }, this._series[active].tooltip || {
	      template: ""
	    }, true);

	    TooltipControl._tooltip.define(def);

	    return this.getItem(id);
	  },
	  _getActiveSeries: function (e) {
	    var a, areas, i, offset$$1, pos$$1, selection, x, y;
	    areas = this._map._areas;
	    offset$$1 = offset(this._contentobj._htmlmap);
	    pos$$1 = pos(e);
	    x = pos$$1.x - offset$$1.x;
	    y = pos$$1.y - offset$$1.y;

	    for (i = 0; i < areas.length; i++) {
	      a = areas[i].points;

	      if (x <= a[2] && x >= a[0] && y <= a[3] && y >= a[1]) {
	        if (selection) {
	          if (areas[i].index > selection.index) selection = areas[i];
	        } else selection = areas[i];
	      }
	    }

	    return selection ? selection.index : 0;
	  },
	  hideSeries: function (series) {
	    this.canvases[series].hideCanvas();
	    var legend = this._settings.legend;

	    if (legend && legend.values && legend.values[series]) {
	      legend.values[series].$hidden = true;

	      this._drawLegend();
	    }

	    this._map.hide(this._contentobj, series, true);
	  },
	  showSeries: function (series) {
	    this.canvases[series].showCanvas();
	    var legend = this._settings.legend;

	    if (legend && legend.values && legend.values[series]) {
	      delete legend.values[series].$hidden;

	      this._drawLegend();
	    }

	    this._map.hide(this._contentobj, series, false);
	  },

	  /**
	  *   renders legend block
	  *   @param: ctx - canvas object
	  *   @param: data - object those need to be displayed
	  *   @param: width - the width of the container
	  *   @param: height - the height of the container
	  */
	  _drawLegend: function (data, width) {
	    /*position of the legend block*/
	    var i,
	        legend,
	        legendContainer,
	        legendHeight,
	        legendItems,
	        legendWidth,
	        style,
	        x = 0,
	        y = 0,
	        ctx,
	        itemColor,
	        disabled,
	        item;
	    data = data || [];
	    width = width || this._content_width;
	    ctx = this.canvases["legend"].getCanvas();
	    /*legend config*/

	    legend = this._settings.legend;
	    /*the legend sizes*/

	    style = this._settings.legend.layout != "x" ? "width:" + legend.width + "px" : "";
	    /*creation of legend container*/

	    if (this._legendObj) {
	      this._legendObj.innerHTML = "";

	      this._legendObj.parentNode.removeChild(this._legendObj);
	    }

	    this.canvases["legend"].clearCanvas(true);
	    legendContainer = create("DIV", {
	      "class": "webix_chart_legend",
	      "style": "left:" + x + "px; top:" + y + "px;" + style
	    }, "");

	    if (legend.padding) {
	      legendContainer.style.padding = legend.padding + "px";
	    }

	    this._legendObj = legendContainer;

	    this._contentobj.appendChild(legendContainer);
	    /*rendering legend text items*/


	    legendItems = [];
	    if (!legend.values) for (i = 0; i < data.length; i++) {
	      legendItems.push(this._drawLegendText(legendContainer, legend.template(data[i]), data[i].id));
	    } else for (i = 0; i < legend.values.length; i++) {
	      legendItems.push(this._drawLegendText(legendContainer, legend.values[i].text, typeof legend.values[i].id != "undefined" ? _typeof(legend.values[i].id) : i, legend.values[i].$hidden));
	    }
	    if (legendContainer.offsetWidth === 0) legendContainer.style.width = "auto";
	    legendWidth = legendContainer.offsetWidth;
	    legendHeight = legendContainer.offsetHeight;
	    /*this._settings.legend.width = legendWidth;
	    this._settings.legend.height = legendHeight;*/

	    /*setting legend position*/

	    if (legendWidth < width) {
	      if (legend.layout == "x" && legend.align == "center") {
	        x = (width - legendWidth) / 2;
	      }

	      if (legend.align == "right") {
	        x = width - legendWidth;
	      }

	      if (legend.margin && legend.align != "center") {
	        x += (legend.align == "left" ? 1 : -1) * legend.margin;
	      }
	    }

	    if (legendHeight < this._content_height) {
	      if (legend.valign == "middle" && legend.align != "center" && legend.layout != "x") y = (this._content_height - legendHeight) / 2;else if (legend.valign == "bottom") y = this._content_height - legendHeight;

	      if (legend.margin && legend.valign != "middle") {
	        y += (legend.valign == "top" ? 1 : -1) * legend.margin;
	      }
	    }

	    legendContainer.style.left = x + "px";
	    legendContainer.style.top = y + "px";
	    /*drawing colorful markers*/

	    ctx.save();

	    for (i = 0; i < legendItems.length; i++) {
	      item = legendItems[i];

	      if (legend.values && legend.values[i].$hidden) {
	        disabled = true;
	        itemColor = legend.values[i].disableColor ? legend.values[i].disableColor : "#edeff0";
	      } else {
	        disabled = false;
	        itemColor = legend.values ? legend.values[i].color : this._settings.color.call(this, data[i]);
	      }

	      this._drawLegendMarker(ctx, item.offsetLeft + x, item.offsetTop + y, itemColor, item.offsetHeight, disabled, i);
	    }

	    ctx.restore();
	    legendItems = null;
	  },

	  /**
	  *   appends legend item to legend block
	  *   @param: ctx - canvas object
	  *   @param: obj - data object that needs being represented
	  */
	  _drawLegendText: function (cont, value, series, disabled) {
	    var style = "";
	    if (this._settings.legend.layout == "x") style = "float:left;";
	    /*the text of the legend item*/

	    var text = create("DIV", {
	      "style": style + "padding-left:" + (10 + this._settings.legend.marker.width) + "px",
	      "class": "webix_chart_legend_item" + (disabled ? " hidden" : ""),
	      "role": "button",
	      "tabindex": "0",
	      "aria-label": i18n.aria[(disabled ? "show" : "hide") + "Chart"] + " " + value
	    }, value);
	    if (arguments.length > 2) text.setAttribute(
	    /*@attr*/
	    "series_id", series);
	    cont.appendChild(text);
	    return text;
	  },

	  /**
	  *   draw legend colorful marder
	  *   @param: ctx - canvas object
	  *   @param: x - the horizontal position of the marker
	  *   @param: y - the vertical position of the marker
	  *   @param: obj - data object which color needs being used
	  */
	  _drawLegendMarker: function (ctx, x, y, color$$1, height, disabled, i) {
	    var p = [];
	    var marker = this._settings.legend.marker;
	    var values = this._settings.legend.values;
	    var type = values && values[i].markerType ? values[i].markerType : marker.type;

	    if (color$$1) {
	      ctx.strokeStyle = ctx.fillStyle = color$$1;
	    }

	    if (type == "round" || !marker.radius) {
	      ctx.beginPath();
	      ctx.lineWidth = marker.height;
	      ctx.lineCap = marker.type;
	      /*start of marker*/

	      x += ctx.lineWidth / 2 + 5;
	      y += height / 2;
	      ctx.moveTo(x, y);
	      var x1 = x + marker.width - marker.height + 1;
	      ctx.lineTo(x1, y);
	      ctx.stroke();
	      ctx.fill();
	    } else if (type == "item") {
	      /*copy of line*/
	      if (this._settings.line && this._settings.type != "scatter" && !this._settings.disableLines) {
	        ctx.beginPath();
	        ctx.lineWidth = this._series[i].line.width;
	        ctx.strokeStyle = disabled ? color$$1 : this._series[i].line.color.call(this, {});
	        var x0 = x + 5;
	        var y0 = y + height / 2;
	        ctx.moveTo(x0, y0);

	        var _x = x0 + marker.width;

	        ctx.lineTo(_x, y0);
	        ctx.stroke();
	      }
	      /*item copy*/


	      var config = this._series[i].item;
	      var radius = parseInt(config.radius.call(this, {}), 10) || 0;
	      var markerType = config.type.call(this, {});

	      if (radius) {
	        ctx.beginPath();

	        if (disabled) {
	          ctx.lineWidth = config.borderWidth;
	          ctx.strokeStyle = color$$1;
	          ctx.fillStyle = color$$1;
	        } else {
	          ctx.lineWidth = config.borderWidth;
	          ctx.fillStyle = config.color.call(this, {});
	          ctx.strokeStyle = config.borderColor.call(this, {});
	          ctx.globalAlpha = config.alpha.call(this, {});
	        }

	        ctx.beginPath();
	        x += marker.width / 2 + 5;
	        y += height / 2;

	        this._strokeChartItem(ctx, x, y, radius + 1, markerType);

	        ctx.fill();
	        ctx.stroke();
	      }

	      ctx.globalAlpha = 1;
	    } else {
	      ctx.beginPath();
	      ctx.lineWidth = 1;
	      x += 5;
	      y += height / 2 - marker.height / 2;
	      p = [[x + marker.radius, y + marker.radius, marker.radius, Math.PI, 3 * Math.PI / 2, false], [x + marker.width - marker.radius, y], [x + marker.width - marker.radius, y + marker.radius, marker.radius, -Math.PI / 2, 0, false], [x + marker.width, y + marker.height - marker.radius], [x + marker.width - marker.radius, y + marker.height - marker.radius, marker.radius, 0, Math.PI / 2, false], [x + marker.radius, y + marker.height], [x + marker.radius, y + marker.height - marker.radius, marker.radius, Math.PI / 2, Math.PI, false], [x, y + marker.radius]];

	      this._path(ctx, p);

	      ctx.stroke();
	      ctx.fill();
	    }
	  },

	  /**
	  *   gets the points those represent chart left top and right bottom bounds
	  *   @param: width - the width of the chart container
	  *   @param: height - the height of the chart container
	  */
	  _getChartBounds: function (width, height) {
	    var chartX0, chartY0, chartX1, chartY1;
	    chartX0 = this._settings.padding.left;
	    chartY0 = this._settings.padding.top;
	    chartX1 = width - this._settings.padding.right;
	    chartY1 = height - this._settings.padding.bottom;

	    if (this._settings.legend) {
	      var legend = this._settings.legend;
	      /*legend size*/

	      var legendWidth = this._settings.legend.width;
	      var legendHeight = this._settings.legend.height;
	      /*if legend is horizontal*/

	      if (legend.layout == "x") {
	        if (legend.valign == "center") {
	          if (legend.align == "right") chartX1 -= legendWidth;else if (legend.align == "left") chartX0 += legendWidth;
	        } else if (legend.valign == "bottom") {
	          chartY1 -= legendHeight;
	        } else {
	          chartY0 += legendHeight;
	        }
	      }
	      /*vertical scale*/
	      else {
	          if (legend.align == "right") chartX1 -= legendWidth;else if (legend.align == "left") chartX0 += legendWidth;
	        }
	    }

	    return {
	      start: {
	        x: chartX0,
	        y: chartY0
	      },
	      end: {
	        x: chartX1,
	        y: chartY1
	      }
	    };
	  },

	  /**
	  *   gets the maximum and minimum values for the stacked chart
	  *   @param: data - data set
	  */
	  _getStackedLimits: function (data) {
	    var i, j, maxValue, minValue, value;

	    if (this._settings.yAxis && typeof this._settings.yAxis.end != "undefined" && typeof this._settings.yAxis.start != "undefined" && this._settings.yAxis.step) {
	      maxValue = parseFloat(this._settings.yAxis.end);
	      minValue = parseFloat(this._settings.yAxis.start);
	    } else {
	      for (i = 0; i < data.length; i++) {
	        data[i].$sum = 0;
	        data[i].$min = Infinity;

	        for (j = 0; j < this._series.length; j++) {
	          value = Math.abs(parseFloat(this._series[j].value(data[i]) || 0));
	          if (isNaN(value)) continue;
	          if (this._series[j].type.toLowerCase().indexOf("stacked") != -1) data[i].$sum += value;
	          if (value < data[i].$min) data[i].$min = value;
	        }
	      }

	      maxValue = -Infinity;
	      minValue = Infinity;

	      for (i = 0; i < data.length; i++) {
	        if (data[i].$sum > maxValue) maxValue = data[i].$sum;
	        if (data[i].$min < minValue) minValue = data[i].$min;
	      }

	      if (minValue > 0) minValue = 0;
	    }

	    return {
	      max: maxValue,
	      min: minValue
	    };
	  },

	  /*adds colors to the gradient object*/
	  _setBarGradient: function (ctx, x1, y1, x2, y2, type, rawColor, axis) {
	    var gradient, offset$$1, rgb, hsv, rawColor0, stops;

	    if (type == "light") {
	      if (axis == "x") gradient = ctx.createLinearGradient(x1, y1, x2, y1);else gradient = ctx.createLinearGradient(x1, y1, x1, y2);
	      stops = [[0, "#FFFFFF"], [0.9, rawColor], [1, rawColor]];
	      offset$$1 = 2;
	    } else if (type == "falling" || type == "rising") {
	      if (axis == "x") gradient = ctx.createLinearGradient(x1, y1, x2, y1);else gradient = ctx.createLinearGradient(x1, y1, x1, y2);
	      rgb = color.toRgb(rawColor);
	      hsv = color.rgbToHsv(rgb[0], rgb[1], rgb[2]);
	      hsv[1] *= 1 / 2;
	      rawColor0 = "rgb(" + color.hsvToRgb(hsv[0], hsv[1], hsv[2]) + ")";

	      if (type == "falling") {
	        stops = [[0, rawColor0], [0.7, rawColor], [1, rawColor]];
	      } else if (type == "rising") {
	        stops = [[0, rawColor], [0.3, rawColor], [1, rawColor0]];
	      }

	      offset$$1 = 0;
	    } else {
	      ctx.globalAlpha = 0.37;
	      offset$$1 = 0;
	      if (axis == "x") gradient = ctx.createLinearGradient(x1, y2, x1, y1);else gradient = ctx.createLinearGradient(x1, y1, x2, y1);
	      stops = [[0, "#9d9d9d"], [0.3, "#e8e8e8"], [0.45, "#ffffff"], [0.55, "#ffffff"], [0.7, "#e8e8e8"], [1, "#9d9d9d"]];
	    }

	    this._gradient(gradient, stops);

	    return {
	      gradient: gradient,
	      offset: offset$$1
	    };
	  },

	  /**
	  *   returns the x and y position
	  *   @param: a - angle
	  *   @param: x - start x position
	  *   @param: y - start y position
	  *   @param: r - destination to the point
	  */
	  _getPositionByAngle: function (a, x, y, r) {
	    a *= -1;
	    x = x + Math.cos(a) * r;
	    y = y - Math.sin(a) * r;
	    return {
	      x: x,
	      y: y
	    };
	  },
	  _gradient: function (gradient, stops) {
	    for (var i = 0; i < stops.length; i++) {
	      gradient.addColorStop(stops[i][0], stops[i][1]);
	    }
	  },
	  _path: function (ctx, points) {
	    var i, method;

	    for (i = 0; i < points.length; i++) {
	      method = i ? "lineTo" : "moveTo";
	      if (points[i].length > 2) method = "arc";
	      ctx[method].apply(ctx, points[i]);
	    }
	  },
	  _addMapRect: function (map, id, points, bounds, sIndex) {
	    map.addRect(id, [points[0].x - bounds.x, points[0].y - bounds.y, points[1].x - bounds.x, points[1].y - bounds.y], sIndex);
	  }
	};
	var view$Y = exports.protoUI(api$Y, Pie$1, BarChart, LineChart, BarHChart, StackedBarChart, StackedBarHChart, Spline$1, AreaChart, Radar, Scatter, Presets, SplineArea$1, DynamicChart, Group, AutoTooltip, DataLoader, MouseEvents, EventSystem, base.view);

	exports.protoUI({
	  name: "rangechart",
	  defaults: {
	    template: "GPL version doesn't support rangechart <br> You need Webix PRO"
	  }
	}, template$1.view);

	var api$Z = {
	  name: "grouplist",
	  defaults: {
	    animate: {}
	  },
	  _listClassName: "webix_grouplist",
	  $init: function () {
	    exports.extend(this.data, TreeStore, true); //needed for getRange

	    this.data.count = function () {
	      return this.order.length;
	    };

	    this.data.provideApi(this, true);
	    this.data.attachEvent("onClearAll", bind(this._onClear, this));

	    this._onClear();
	  },
	  _onClear: function () {
	    this._nested_cursor = [];
	    this._nested_chain = [];
	  },
	  $setSize: function () {
	    if (base.api.$setSize.apply(this, arguments)) {
	      //critical for animations in group list
	      this._dataobj.style.width = this._content_width;
	    }
	  },
	  on_click: {
	    webix_list_item: function (e, id) {
	      if (this._in_animation) {
	        return false;
	      }

	      for (var i = 0; i < this._nested_chain.length; i++) {
	        if (this._nested_chain[i] == id) {
	          //one level up
	          for (var j = i; j < this._nested_chain.length; j++) {
	            this.data.getItem(this._nested_chain[j]).$template = "";
	          }

	          if (!i) {
	            //top level
	            this._nested_cursor = this.data.branch[0];
	            this._nested_chain = [];
	          } else {
	            this._nested_cursor = this.data.branch[this._nested_chain[i - 1]];

	            this._nested_chain.splice(i);
	          }

	          this._is_level_down = false;
	          return this.render();
	        }
	      }

	      var obj = this.getItem(id);

	      if (obj.$count) {
	        //one level down
	        this._is_level_down = true;

	        this._nested_chain.push(id);

	        obj.$template = "Back";
	        this._nested_cursor = this.data.branch[obj.id];
	        return this.render();
	      } else {
	        if (this._settings.select) {
	          this._no_animation = true;
	          if (this._settings.select == "multiselect" || this._settings.multiselect) this.select(id, false, this._settings.multiselect == "touch" || e.ctrlKey || e.metaKey, e.shiftKey); //multiselection
	          else this.select(id);
	          this._no_animation = false;
	        }
	      }
	    }
	  },
	  getOpenState: function () {
	    return {
	      parents: this._nested_chain,
	      branch: this._nested_cursor
	    };
	  },
	  render: function () {
	    var i, lastChain; //start filtering processing=>

	    this._nested_chain = copy(this._nested_chain);
	    this._nested_cursor = copy(this._nested_cursor);

	    if (this._nested_chain.length) {
	      for (i = 0; i < this._nested_chain.length; i++) {
	        if (!this.data.branch[this._nested_chain[i]]) {
	          this._nested_chain.splice(i, 1);

	          i--;
	        }
	      }
	    }

	    lastChain = this._nested_chain.length ? this._nested_chain[this._nested_chain.length - 1] : 0;
	    this._nested_cursor = copy(this.data.branch[lastChain]);

	    if (!this._nested_cursor.length && this._nested_chain.length) {
	      this._nested_cursor = [lastChain];

	      this._nested_chain.pop();
	    } //<= end filtering processing


	    if (this._in_animation) {
	      return delay(this.render, this, arguments, 100);
	    }

	    for (i = 0; i < this._nested_cursor.length; i++) {
	      this.data.getItem(this._nested_cursor[i]).$template = "";
	    }

	    if (!this._nested_cursor.length) this._nested_cursor = this.data.branch[0];
	    this.data.order = toArray([].concat(this._nested_chain).concat(this._nested_cursor));

	    if (this.callEvent("onBeforeRender", [this.data])) {
	      if (this._no_animation || !this._dataobj.innerHTML || !(animate.isSupported() && this._settings.animate) || this._prev_nested_chain_length == this._nested_chain.length) {
	        // if dataobj is empty or animation is not supported
	        RenderStack.render.apply(this, arguments);
	      } else {
	        //getRange - returns all elements
	        if (this.callEvent("onBeforeRender", [this.data])) {
	          if (!this._back_scroll_states) this._back_scroll_states = [];

	          var next_div = this._dataobj.cloneNode(false);

	          next_div.innerHTML = this.data.getRange().map(this._toHTML, this).join("");
	          var aniset = exports.extend({}, this._settings.animate);
	          aniset.direction = this._is_level_down ? "left" : "right";
	          /*scroll position restore*/

	          var animArr = [clone(aniset), clone(aniset)];

	          if (this._is_level_down) {
	            this._back_scroll_states.push(this.getScrollState());

	            if (Touch && Touch.$active) {
	              animArr[0].y = 0;
	              animArr[1].y = -this.getScrollState().y;
	            }
	          } else {
	            var getScrollState = this._back_scroll_states.pop();

	            if (Touch && Touch.$active) {
	              animArr[0].y = -getScrollState.y;
	              animArr[1].y = -this.getScrollState().y;
	            }
	          }

	          var line = animate.formLine(next_div, this._dataobj, aniset);
	          /*keeping scroll position*/

	          if (Touch && Touch.$active) Touch._set_matrix(next_div, 0, this._is_level_down ? 0 : animArr[0].y, "0ms");
	          aniset.master = this;

	          aniset.callback = function () {
	            this._dataobj = next_div;
	            /*scroll position restore*/

	            if (!this._is_level_down) {
	              if (Touch && Touch.$active) {
	                delay(function () {
	                  Touch._set_matrix(next_div, 0, animArr[0].y, "0ms");
	                }, this);
	              } else if (getScrollState) this.scrollTo(0, getScrollState.y);
	            } else if (!(Touch && Touch.$active)) {
	              this.scrollTo(0, 0);
	            }

	            animate.breakLine(line);
	            aniset.master = aniset.callback = null;
	            this._htmlmap = null; //clear map, it will be filled at first getItemNode

	            this._in_animation = false;
	            this.callEvent("onAfterRender", []);
	          };

	          this._in_animation = true;
	          animate(line, animArr);
	        }
	      }

	      this._prev_nested_chain_length = this._nested_chain.length;
	    }
	  },
	  templateBack_setter: function (config) {
	    this.type.templateBack = template(config);
	  },
	  templateItem_setter: function (config) {
	    this.type.templateItem = template(config);
	  },
	  templateGroup_setter: function (config) {
	    this.type.templateGroup = template(config);
	  },
	  type: {
	    template: function (obj, common) {
	      if (obj.$count) return common.templateGroup(obj, common);
	      return common.templateItem(obj, common);
	    },
	    css: "group",
	    classname: function (obj, common, marks) {
	      return "webix_list_item" + " webix_" + (obj.$count ? "group" : "item") + (obj.$template ? "_back" : "") + (marks && marks.webix_selected ? " webix_selected" : "") + (common.css ? " " + common.css : "") + (obj.$css ? " " + obj.$css : "");
	    },
	    templateStart: template("<div " +
	    /*@attr*/
	    "webix_l_id" + "=\"#id#\" class=\"{common.classname()}\" style=\"width:{common.widthSize()}; height:{common.heightSize()};  overflow:hidden;\" {common.aria()}>"),
	    templateBack: template("#value#"),
	    templateItem: template("#value#"),
	    templateGroup: template("#value#"),
	    templateEnd: function (obj) {
	      var html = "";
	      if (obj.$count) html += "<div class='webix_arrow_icon'></div>";
	      html += "</div>";
	      return html;
	    }
	  },
	  showItem: function (id) {
	    var obj, parent;

	    if (id) {
	      obj = this.getItem(id);
	      parent = obj.$parent;
	      if (obj.$count) parent = obj.id;
	    }

	    this._nested_cursor = this.data.branch[parent || 0];
	    this._nested_chain = []; //build _nested_chain

	    while (parent) {
	      this.getItem(parent).$template = "Back";

	      this._nested_chain.unshift(parent);

	      parent = this.getItem(parent).$parent;
	    } //render


	    this._no_animation = true;
	    this.render();
	    this._no_animation = false; //scroll if necessary

	    RenderStack.showItem.call(this, id);
	  }
	};
	var view$Z = exports.protoUI(api$Z, Group, list.view);

	type(view$Z, {});

	var api$_ = {
	  name: "unitlist",
	  _id:
	  /*@attr*/
	  "webix_item_id",
	  uniteBy_setter: template,
	  render: function (id, data, type) {
	    var config = this._settings;
	    if (!this.isVisible(config.id)) return;
	    if (!config.uniteBy) return;

	    if (id) {
	      var cont = this.getItemNode(id); //get html element of updated item

	      if (cont && type == "update" && this._settings.uniteBy.call(this, data) == this.getItem(id).$unitValue) {
	        var t = this._htmlmap[id] = this._toHTMLObject(data);

	        insertBefore(t, cont);
	        remove(cont);
	        return;
	      }
	    } //full reset


	    if (this.callEvent("onBeforeRender", [this.data])) {
	      this.units = null;

	      this._setUnits();

	      if (this.units) {
	        this._dataobj.innerHTML = this._getUnitRange().map(this._toHTML, this).join("");
	        this._htmlmap = null;
	      }

	      this.callEvent("onAfterRender", []);
	    }
	  },
	  getUnits: function () {
	    var result = [];

	    if (this.units) {
	      for (var b in this.units) {
	        result.push(b);
	      }
	    }

	    return result;
	  },
	  getUnitList: function (id) {
	    return this.units ? this.units[id] : null;
	  },
	  _toHTML: function (obj) {
	    //check if related template exist
	    var mark = this.data._marks[obj.id];
	    assert(!obj.$template || this.type["template" + obj.$template], "RenderStack :: Unknown template: " + obj.$template);
	    this.callEvent("onItemRender", [obj]);

	    if (obj.$unit) {
	      return this.type.templateStartHeader(obj, this.type) + this.type.templateHeader.call(this, obj.$unit) + this.type.templateEnd(obj, this.type);
	    }

	    return this.type.templateStart(obj, this.type, mark) + (obj.$template ? this.type["template" + obj.$template] : this.type.template)(obj, this.type) + this.type.templateEnd(obj, this.type);
	  },
	  _getUnitRange: function () {
	    var data, i, u, unit;
	    data = [];
	    var min = this.data.$min || 0;
	    var max = this.data.$max || Infinity;
	    var count = 0;

	    for (u in this.units) {
	      data.push({
	        $unit: u
	      });
	      unit = this.units[u];

	      for (i = 0; i < unit.length; i++) {
	        if (count == min) data = [{
	          $unit: u
	        }];
	        data.push(this.getItem(unit[i]));
	        if (count == max) return toArray(data);
	        count++;
	      }
	    }

	    return toArray(data);
	  },
	  _setUnits: function () {
	    var list$$1 = this;
	    this.units = {};
	    this.data.each(function (obj) {
	      var result = list$$1._settings.uniteBy.call(this, obj);

	      obj.$unitValue = result;
	      if (!list$$1.units[result]) list$$1.units[result] = [];
	      list$$1.units[result].push(obj.id);
	    });
	  },
	  type: {
	    headerHeight: 20,
	    templateHeader: function (value) {
	      return "<span class='webix_unit_header_inner'>" + value + "</span>";
	    },
	    templateStart: function (obj, type, marks) {
	      if (obj.$unit) return type.templateStartHeader.apply(this, arguments);
	      var className = "webix_list_item webix_list_" + type.css + "_item" + (marks && marks.webix_selected ? " webix_selected" : "") + (obj.$css ? obj.$css : "");
	      var style = "width:" + type.widthSize(obj, type, marks) + "; height:" + type.heightSize(obj, type, marks) + "; overflow:hidden;" + (type.layout && type.layout == "x" ? "float:left;" : "");
	      return "<div " +
	      /*@attr*/
	      "webix_item_id" + "=\"" + obj.id + "\" class=\"" + className + "\" style=\"" + style + "\" " + type.aria(obj, type, marks) + ">";
	    },
	    templateStartHeader: function (obj, type, marks) {
	      var className = "webix_unit_header webix_unit_" + type.css + "_header" + (obj.$selected ? "_selected" : "");
	      var style = "width:" + type.widthSize(obj, type, marks) + "; height:" + type.headerHeight + "px; overflow:hidden;";
	      return "<div " +
	      /*@attr*/
	      "webix_unit_id" + "=\"" + obj.$unit + "\" class=\"" + className + "\" style=\"" + style + "\">";
	    }
	  },
	  $skin: function () {
	    this.type.headerHeight = $active.unitHeaderHeight;
	  }
	};
	var view$_ = exports.protoUI(api$_, list.view);

	i18n.dbllist = {
	  selectAll: "<span class='webix_icon wxi-angle-double-right'></span>",
	  selectOne: "<span class='webix_icon wxi-angle-right'></span>",
	  deselectAll: "<span class='webix_icon wxi-angle-double-left'></span>",
	  deselectOne: "<span class='webix_icon wxi-angle-left'></span>"
	};
	var api$10 = {
	  name: "dbllist",
	  defaults: {
	    borderless: true
	  },
	  $init: function () {
	    this._moved = {};
	    this._inRight = bind(function (obj) {
	      return this._moved[obj.id];
	    }, this);
	    this._inLeft = bind(function (obj) {
	      return !this._moved[obj.id];
	    }, this);
	    this.$view.className += " webix_dbllist";
	    this.$ready.unshift(this._setLayout);
	  },
	  $onLoad: function (data, driver) {
	    var left = this.$$("left");
	    var right = this.$$("right");

	    this._updateAndResize(function () {
	      left.data.driver = driver;
	      left.parse(data);
	      right.data.driver = driver;
	      right.parse(data);
	    });

	    this._data_ready = true;

	    this._refresh();

	    return true;
	  },
	  _getButtons: function () {
	    if (this._settings.buttons === false) return {
	      width: 10
	    };
	    var locale = i18n.dbllist;
	    var buttons = [this._getButton("deselect_all", locale.deselectAll), this._getButton("select_all", locale.selectAll), this._getButton("deselect_one", locale.deselectOne), this._getButton("select_one", locale.selectOne)];
	    buttons = {
	      width: 120,
	      template: buttons.join(""),
	      onClick: {
	        dbllist_button: function (e, id, trg) {
	          this.getTopParentView()._update_list(trg.getAttribute("action"));
	        }
	      }
	    };
	    if (this._settings.buttons) buttons.template = this._settings.buttons;
	    return buttons;
	  },
	  _getButton: function (action, label) {
	    return "<button class='dbllist_button' action='" + action + "'>" + label + "</button>";
	  },
	  _getList: function (id, action, label, bottom) {
	    var list = {
	      view: "list",
	      select: "multiselect",
	      multiselect: "touch",
	      id: id,
	      action: action,
	      drag: true,
	      type: {
	        margin: 3,
	        id: id
	      },
	      on: {
	        onBeforeDrop: function (context) {
	          var source = context.from;
	          var target = context.to;
	          var top = source.getTopParentView();

	          if (top === this.getTopParentView()) {
	            var mode = target._settings.action != "select_one";
	            top.select(context.source, mode);
	          }

	          return false;
	        },
	        onItemDblClick: function () {
	          return this.getTopParentView()._update_list(this.config.action);
	        }
	      }
	    };
	    if (this._settings.list) exports.extend(list, this._settings.list, true);
	    if (label) list = {
	      rows: [{
	        view: "label",
	        label: label,
	        css: "webix_inp_top_label"
	      }, list]
	    };
	    if (bottom) return {
	      rows: [list, {
	        view: "label",
	        height: 20,
	        label: bottom,
	        css: "bottom_label"
	      }]
	    };
	    return list;
	  },
	  _setLayout: function () {
	    var cols = [{
	      margin: 10,
	      type: "clean",
	      cols: [this._getList("left", "select_one", this._settings.labelLeft, this._settings.labelBottomLeft), this._getButtons(), this._getList("right", "deselect_one", this._settings.labelRight, this._settings.labelBottomRight)]
	    }];
	    this.cols_setter(cols);
	  },
	  _update_list: function (action) {
	    var top = this;
	    var id = null;
	    var mode = false;

	    if (action === "select_all") {
	      id = top.$$("left").data.order;
	      mode = true;
	    } else if (action === "select_one") {
	      id = top.$$("left").getSelectedId(true);
	      mode = true;
	    } else if (action === "deselect_all") {
	      id = top.$$("right").data.order;
	      mode = false;
	    } else if (action === "deselect_one") {
	      id = top.$$("right").getSelectedId(true);
	      mode = false;
	    }

	    top.select(id, mode);
	  },
	  select: function (id, mode) {
	    var i;
	    if (_typeof(id) !== "object") id = [id];

	    if (mode) {
	      for (i = 0; i < id.length; i++) {
	        this._moved[id[i]] = true;
	      }
	    } else {
	      for (i = 0; i < id.length; i++) {
	        delete this._moved[id[i]];
	      }
	    }

	    this._refresh();

	    this.callEvent("onChange", []);
	  },
	  _updateAndResize: function (handler, size) {
	    freeze(bind(handler, this), false);
	    if (size && (this.$$("left")._settings.autoheight || this.$$("right")._settings.autoheight)) this.resize();
	  },
	  _refresh: function () {
	    var left = this.$$("left");
	    var right = this.$$("right");
	    if (left) this._updateAndResize(function () {
	      left.filter(this._inLeft);
	      right.filter(this._inRight);
	    }, true);
	  },
	  focus: function () {
	    if (!UIManager.canFocus(this)) return false;
	    UIManager.setFocus(this);
	  },
	  value_setter: function (val) {
	    this.setValue(val);
	  },
	  setValue: function (value) {
	    this._moved = {};

	    if (value) {
	      if (_typeof(value) !== "object") value = value.toString().split(",");

	      for (var i = 0; i < value.length; i++) {
	        this._moved[value[i]] = true;
	      }
	    }

	    this._refresh();
	  },
	  getValue: function () {
	    var value = [];
	    var list = this.$$("left");

	    for (var key in this._moved) {
	      if (!this._data_ready || list.data.pull[key]) value.push(key);
	    }

	    return value.join(",");
	  }
	};
	var view$10 = exports.protoUI(api$10, AtomDataLoader, IdSpace, layout.view);

	var api$11 = {
	  name: "tree",
	  defaults: {
	    scroll: "auto",
	    navigation: true
	  },
	  $init: function () {
	    this._viewobj.className += " webix_tree"; //map API of DataStore on self

	    exports.extend(this.data, TreeStore, true);
	    exports.extend(this.on_click, TreeClick);
	    this.attachEvent("onAfterRender", this._refresh_scroll);
	    this.attachEvent("onPartialRender", this._refresh_scroll);
	    this.data.provideApi(this, true);

	    this._viewobj.setAttribute("role", "tree");
	  },
	  //attribute , which will be used for ID storing
	  _id:
	  /*@attr*/
	  "webix_tm_id",
	  //supports custom context menu
	  on_context: {},
	  on_dblclick: {
	    webix_tree_checkbox: function () {
	      if (this.on_click.webix_tree_checkbox) return this.on_click.webix_tree_checkbox.apply(this, arguments);
	    }
	  },
	  $fixEditor: function (editor) {
	    var item = this.getItemNode(editor.id).querySelector("span");

	    if (item) {
	      if (item.innerHTML === "") item.innerHTML = "&nbsp;";
	      var padding = 10;
	      var pos$$1 = item.offsetLeft;
	      editor.node.style.width = this.$view.scrollWidth - pos$$1 - padding + "px";
	      editor.node.style.marginLeft = pos$$1 + "px";
	      editor.node.style.left = "0px";
	    }
	  },
	  //css class to action map, for onclick event
	  on_click: {
	    webix_tree_item: function (e, id) {
	      if (this._settings.activeTitle) {
	        var item = this.getItem(id);
	        if (item.open) this.close(id);else this.open(id);
	      }

	      if (this._settings.select) {
	        if (this._settings.select == "multiselect" || this._settings.multiselect) {
	          var multimode = e.ctrlKey || e.metaKey || this._settings.multiselect == "touch";

	          if (this._settings.multiselect == "level" && (multimode || e.shiftKey)) {
	            //allow only selection on the same level
	            var select = this.getSelectedId(true)[0];
	            if (select && this.getParentId(id) != this.getParentId(select)) return;
	          }

	          this.select(id, false, multimode, e.shiftKey); //multiselection
	        } else this.select(id);
	      }
	    }
	  },
	  _paste: {
	    // insert new item with pasted value
	    insert: function (text) {
	      var parent = this.getSelectedId() || "0";
	      this.add({
	        value: text
	      }, null, parent);
	    },
	    // change value of each selected item
	    modify: function (text) {
	      var sel = this.getSelectedId(true);

	      for (var i = 0; i < sel.length; i++) {
	        this.getItem(sel[i]).value = text;
	        this.refresh(sel[i]);
	      }
	    },
	    // do nothing
	    custom: function () {}
	  },
	  _drag_order_complex: true,
	  $dragHTML: function (obj, e, context) {
	    var html = "<div class='webix_tree_item'>" + this.type.template(obj, this.type) + "</div>";
	    if (isArray(context.source) && context.source.length > 1) html = this._toMultipleHTML(html, context.source.length);
	    return html;
	  },
	  _close_branches: function (context) {
	    var source = context.source;

	    for (var i = 0; i < source.length; i++) {
	      this.close(source[i]);
	    }
	  },
	  _set_drop_area: function (target, t) {
	    var node = this.getItemNode(target);

	    if (node) {
	      node.parentNode.insertBefore(DragControl._dropHTML[0], node);
	    } else t.children[0].children[0].appendChild(DragControl._dropHTML[0]);
	  },
	  //css class to action map, for dblclick event
	  type: exports.extend({
	    //normal state of item
	    template: function (obj, common) {
	      var template$$1 = common["template" + obj.level] || common.templateCommon;
	      return template$$1.apply(this, arguments);
	    },
	    classname: function (obj, common, marks) {
	      var css = "webix_tree_item";

	      if (obj.$css) {
	        if (_typeof(obj.$css) == "object") obj.$css = createCss(obj.$css);
	        css += " " + obj.$css;
	      }

	      if (marks && marks.$css) css += " " + marks.$css;
	      if (common.css) css += " " + common.css;
	      return css;
	    },
	    aria: function (obj, common, marks) {
	      return "role=\"treeitem\"" + (marks && marks.webix_selected ? " aria-selected=\"true\" tabindex=\"0\"" : " tabindex=\"-1\"") + (obj.$count ? "aria-expanded=\"" + (obj.open ? "true" : "false") + "\"" : "") + "aria-level=\"" + obj.$level + "\"";
	    },
	    templateCommon: template("{common.icon()} {common.folder()} <span>#value#</span>"),
	    templateStart: template("<div " +
	    /*@attr*/
	    "webix_tm_id" + "=\"#id#\" class=\"{common.classname()}\" {common.aria()}>"),
	    templateEnd: template("</div>"),
	    templateCopy: template("#value#")
	  }, TreeType)
	};
	var view$11 = exports.protoUI(api$11, TreeStateCheckbox, Group, TreeAPI, DragItem, TreeDataMove, SelectionModel, KeysNavigation, MouseEvents, Scrollable, TreeDataLoader, proto.view, TreeRenderStack, CopyPaste, EventSystem);
	var tree = {
	  api: api$11,
	  view: view$11
	};
	type(view$11, {
	  name: "lineTree",
	  css: "webixLineTree",
	  icon: function (obj, common) {
	    var html = "";
	    var open = "";

	    for (var i = 1; i <= obj.$level; i++) {
	      if (i == obj.$level) open = obj.$count ? obj.open ? "webix_tree_open " : "webix_tree_close " : "webix_tree_none ";

	      var icon = common._icon_src(obj, common, i);

	      if (icon) html += "<div class='" + open + "webix_tree_img webix_tree_" + icon + "'></div>";
	    }

	    return html;
	  },
	  _icon_src: function (obj, common, level) {
	    var lines = common._tree_branch_render_state;
	    var tree = TreeRenderStack._obj;

	    if (lines === 0 && tree) {
	      //we are in standalone rendering 
	      //need to reconstruct rendering state
	      var lines_level = obj.$level;
	      var branch_id = obj.id;
	      lines = [];

	      while (lines_level) {
	        var parent_id = tree.getParentId(branch_id);
	        var pbranch = tree.data.branch[parent_id];
	        if (pbranch[pbranch.length - 1] == branch_id) lines[lines_level] = true;
	        branch_id = parent_id;
	        lines_level--;
	      } //store for next round


	      common._tree_branch_render_state = lines;
	    }

	    if (!lines) return 0; //need to be replaced with image urls

	    if (level == obj.$level) {
	      var mode = 3; //3-way line

	      if (!obj.$parent) {
	        //top level
	        if (obj.$index === 0) mode = 4; //firts top item
	      }

	      if (lines[obj.$level]) mode = 2;

	      if (obj.$count) {
	        if (obj.open) return "minus" + mode;else return "plus" + mode;
	      } else return "line" + mode;
	    } else {
	      if (!lines[level]) return "line1";
	      return "blank";
	    }
	  }
	});

	var api$12 = {
	  name: "treemap",
	  defaults: {
	    activeItem: false,
	    subRender: true,
	    header: true,
	    headerHeight: 35,
	    value: template("#value#"),
	    headerTemplate: "",
	    navigation: true
	  },
	  value_setter: template,
	  headerTemplate_setter: template,
	  header_setter: function (value) {
	    if (value && value !== true) {
	      this.type.header = value;
	    }

	    return value;
	  },
	  $init: function () {
	    this.$view.className += " webix_treemap";

	    this._viewobj.setAttribute("role", "tree");

	    this._htmlElement = document.createElement("DIV");
	    exports.extend(this.data, TreeStore, true);
	    this.data.provideApi(this, true);
	    this.data.attachEvent("onClearAll", bind(function () {
	      this._html = "";
	      this.$values = {};
	      this.$xy = {};
	    }, this));
	    this.attachEvent("onKeyPress", this._onKeyPress);
	  },
	  _toHTMLItem: function (obj) {
	    var mark = this.data._marks[obj.id];
	    this.callEvent("onItemRender", [obj]);
	    var template$$1 = obj.$template ? this.type["template" + obj.$template].call(this, obj, this.type, mark) : this.type.template.call(this, obj, this.type, mark);
	    return this.type.templateStart.call(this, obj, this.type, mark) + template$$1 + this.type.templateEnd.call(this);
	  },
	  _renderHeader: function (id) {
	    var item = this.getItem(id);
	    var height = this._settings.headerHeight;
	    var html = "<div class='webix_treemap_header' style='height:" + height + "px;line-height:" + height + "px;'>";
	    html += this.type.header.call(this, item, this.type);
	    html += "</div>";
	    return html;
	  },
	  _renderBranch: function (pId) {
	    var sizes,
	        row,
	        value,
	        sum,
	        leaves = [];

	    if (!this.$width || !this.count()) {
	      this._html = "";
	      return false;
	    }

	    if (!pId) {
	      pId = this.config.branch || 0;
	      this._html = "";
	      this.$values = {};
	      this.$xy = {};
	      this.$xy[pId] = {
	        width: this.$width,
	        height: this.$height,
	        top: 0,
	        left: 0
	      }; // header

	      if (pId && this._settings.header) {
	        this.$xy[pId].height -= this._settings.headerHeight;
	        this.$xy[pId].top = this._settings.headerHeight;
	        this._html += this._renderHeader(pId);
	      } // values calculation


	      sum = 0;
	      this.data.each(function (item) {
	        var parentId = this.getParentId(item.id);

	        if (!this.data.branch[item.id]) {
	          value = this.config.value.call(this, item) * 1;

	          if (!isNaN(value) && value) {
	            this.$values[item.id] = value;
	            sum += value;

	            while (parentId) {
	              if (!this.$values[parentId]) this.$values[parentId] = 0;
	              this.$values[parentId] += value;
	              parentId = this.getParentId(parentId);
	            }
	          }
	        }
	      }, this, false, pId);
	    }

	    this.data.eachChild(pId, function (item) {
	      if (this.$values[item.id]) leaves.push(copy(item));
	    }, this);
	    sum = sum || this.$values[pId];

	    if (leaves.length && sum) {
	      sizes = this.$xy[pId];
	      row = {
	        top: sizes.top,
	        left: sizes.left,
	        dx: sizes.width,
	        dy: sizes.height,
	        set: [],
	        sum: 0
	      };
	      row.dim = Math.min(row.dx, row.dy);
	      var delta = row.dx * row.dy / sum; //total area

	      for (var _i = 0; _i < leaves.length; _i++) {
	        leaves[_i].$value = this.$values[leaves[_i].id] * delta;
	      } //normalized value


	      leaves.sort(function (a, b) {
	        return a.$value > b.$value ? -1 : 1;
	      });
	      var bad = Infinity;
	      var i = 0;

	      while (leaves[i]) {
	        var check = this._worst(row, leaves[i]);

	        if (check < bad) {
	          row.sum += leaves[i].$value;
	          row.set.push(leaves[i]);
	          bad = check;
	          i++;
	        } else {
	          this._renderRow(row);

	          var r = {
	            top: row.top,
	            left: row.left,
	            dx: row.dx,
	            dy: row.dy,
	            set: [],
	            sum: 0
	          };

	          var _delta = row.sum / row.dim;

	          if (row.dx > row.dy) {
	            r.left += _delta;
	            r.dx -= _delta;
	          } else {
	            r.top += _delta;
	            r.dy -= _delta;
	          }

	          row = r;
	          row.dim = Math.min(row.dx, row.dy);
	          bad = Infinity;
	        }
	      }
	    }

	    if (row) this._renderRow(row);
	  },
	  _renderRow: function (row) {
	    var i,
	        id,
	        x,
	        y,
	        top = row.top,
	        left = row.left;
	    row.mode = row.dy < row.dx;
	    row.contra = row.sum / row.dim;

	    for (i = 0; i < row.set.length; i++) {
	      id = row.set[i].id;

	      if (row.mode) {
	        x = row.contra;
	        y = row.set[i].$value / row.contra;
	      } else {
	        x = row.set[i].$value / row.contra;
	        y = row.contra;
	      }

	      this.$xy[id] = {};
	      this.$xy[id].top = top;
	      this.$xy[id].left = left;
	      if (row.mode) top += y;else left += x;
	      this.$xy[id].width = x;
	      this.$xy[id].height = y;
	      this._html += this._toHTMLItem(this.getItem(id));
	      if (this._settings.subRender && this.data.branch[id]) this._renderBranch(id);
	    }
	  },
	  _worst: function (row, add) {
	    var s = row.sum + add.$value;
	    var a = s * s / (row.dim * row.dim * add.$value);

	    if (row.set.length) {
	      a = Math.max(row.dim * row.dim * row.set[0].$value / (s * s), a);
	    }

	    return a > 1 ? a : 1 / a;
	  },
	  _toHTMLObject: function (obj) {
	    this._htmlElement.innerHTML = this._toHTMLItem(obj);
	    return this._htmlElement.firstChild;
	  },
	  showBranch: function (id) {
	    this._settings.branch = id;
	    this.refresh();
	  },
	  render: function (id, data, type) {
	    if (!this.isVisible(this._settings.id) || this.$blockRender) return;

	    if (type == "update") {
	      var cont = this.getItemNode(id); //get html element of updated item

	      if (cont) {
	        var t = this._htmlmap[id] = this._toHTMLObject(data);

	        insertBefore(t, cont);
	        remove(cont);
	      }
	    } else if (this.data.branch && (!this._settings.branch || this.data.branch[this._settings.branch])) {
	      this._htmlmap = null;
	      this.callEvent("onBeforeRender", []);

	      this._renderBranch();

	      this._dataobj.innerHTML = this._html;
	      this.callEvent("onAfterRender", []);
	    }

	    return true;
	  },
	  _id:
	  /*@attr*/
	  "webix_dm_id",
	  on_click: {
	    webix_treemap_item: function (e, id) {
	      if (this._settings.select) {
	        if (this._settings.select == "multiselect" || this._settings.multiselect) this.select(id, false, e.ctrlKey || e.metaKey || this._settings.multiselect == "touch", e.shiftKey);else this.select(id);
	      }

	      if (this._settings.activeItem && this.isBranch(id)) {
	        this.showBranch(id);
	      }
	    },
	    webix_treemap_header_item: function (e) {
	      var id = locate(e,
	      /*@attr*/
	      "webix_dm_header_id");
	      this.define("branch", id);
	      this.refresh();
	    },
	    webix_treemap_reset: function () {
	      this.define("branch", 0);
	      this.refresh();
	    }
	  },
	  on_dblclick: {},
	  on_mouse_move: {},
	  _getCssText: function (style) {
	    var css = "";

	    for (var property in style) {
	      css += property + ":" + style[property] + ";";
	    }

	    return css;
	  },
	  type: {
	    //normal state of item
	    template: template("#value#"),
	    header: function (obj, common) {
	      var id = obj.id;
	      var resetIcon = "<div role='button' tabindex='0' aria-label='" + i18n.aria.resetTreeMap + "' class='webix_treemap_reset'></div>";
	      var arr = [];

	      while (id) {
	        obj = this.getItem(id);
	        arr.push(common.headerItem.call(this, obj, common));
	        id = this.getParentId(id);
	      }

	      arr.reverse();
	      return resetIcon + arr.join("<span class='webix_icon wxi-angle-right webix_treemap_path_icon'></span>");
	    },
	    headerItem: function (obj) {
	      var template$$1 = this.config.headerTemplate(obj);
	      var html = "<a role=\"button\" tabindex=\"0\" aria-label=\"" + template$$1 + "\" " +
	      /*@attr*/
	      "webix_dm_header_id" + "=\"" + obj.id + "\" class=\"webix_treemap_header_item\">";
	      html += template$$1;
	      html += "</a>";
	      return html;
	    },
	    classname: function (obj, common, marks) {
	      var css = "webix_treemap_item";
	      if (common.css) css += " " + common.css;

	      if (obj.$css) {
	        if (_typeof(obj.$css) == "object") obj.$css = createCss(obj.$css);
	        css += " " + obj.$css;
	      }

	      var xy = this.$xy[obj.id];
	      if (marks && marks.$css) css += " " + marks.$css;
	      css += " webix_treemap_level_" + this.getItem(obj.id).$level;
	      var parentId = this.getParentId(obj.id);
	      if (!parentId || parentId == this._settings.branch) css += " webix_treemap_level_top";
	      if (this.$height - xy.top - xy.height < 1) css += " webix_treemap_item_bottom";
	      if (this.$width - xy.left - xy.width < 1) css += " webix_treemap_item_right";

	      if (common.cssClass) {
	        var cssClass = common.cssClass.call(this, obj, common, marks);

	        if (cssClass) {
	          if (_typeof(cssClass) == "object") {
	            css += " " + createCss(cssClass);
	          } else css += " " + cssClass;
	        }
	      }

	      return css;
	    },
	    templateStart: function (obj, type, marks) {
	      var style = "";

	      if (this.$xy) {
	        var xy = this.$xy[obj.id];
	        style += "width: " + xy.width + "px; height: " + xy.height + "px;";
	        style += "top: " + xy.top + "px; left: " + xy.left + "px;";
	      }

	      return "<div role=\"treeitem\" aria-level=\"" + obj.$level + "\" " + (marks && marks.webix_selected ? "aria-selected=\"true\" tabindex=\"0\"" : "") + " " +
	      /*@attr*/
	      "webix_dm_id" + "=\"" + obj.id + "\" class=\"" + type.classname.call(this, obj, type, marks) + "\" style=\"" + style + "\">";
	    },
	    templateEnd: template("</div>")
	  }
	};
	var view$12 = exports.protoUI(api$12, Group, TreeAPI, SelectionModel, KeysNavigation, MouseEvents, Scrollable, TreeDataLoader, proto.view, TreeRenderStack, CopyPaste, EventSystem);

	/*
		UI:DataView
	*/
	// #include ui/component.js
	// #include core/mouse.js 	
	// #include core/edit.js 
	// #include core/selection.js 
	// #include core/drag.js
	// #include core/move.js
	// #include core/virtual_render.js
	// #include core/keynav.js
	// #include core/print.js

	var api$13 = {
	  name: "dataview",
	  $init: function (config) {
	    if (config.sizeToContent) //method need to be called before data-loaders
	      //so we are using unshift to place it at start
	      this.$ready.unshift(this._after_init_call);
	    var type = config.type || config.item;
	    var prerender = config.prerender || this.defaults.prerender || type && type.width == "auto" || config.drag == "move" || config.drag == "order";
	    if (!prerender && !config.autoheight) exports.extend(this, VirtualRenderStack, true);
	    if (config.autoheight) config.scroll = false;

	    if (type && type.type == "tiles") {
	      this._viewobj.firstChild.style.padding = "8px";
	      this._tilesPadding = 8;
	    }

	    this._contentobj.className += " webix_dataview";

	    this._viewobj.setAttribute("role", "listbox");
	  },
	  _after_init_call: function () {
	    var test = create("DIV", 0, this.type.template({}));
	    test.style.position = "absolute";
	    document.body.appendChild(test);
	    this.type.width = test.offsetWidth;
	    this.type.height = test.offsetHeight;
	    remove(test);
	  },
	  defaults: {
	    scroll: true,
	    datafetch: 50,
	    navigation: true
	  },
	  _id:
	  /*@attr*/
	  "webix_l_id",
	  _itemClassName: "webix_dataview_item",
	  _tilesPadding: 0,
	  _drag_direction: "x",
	  on_click: {
	    webix_dataview_item: function (e, id) {
	      if (this._settings.select) {
	        if (this._settings.select == "multiselect" || this._settings.multiselect) this.select(id, false, this._settings.multiselect == "touch" || e.ctrlKey || e.metaKey, e.shiftKey); //multiselection
	        else this.select(id);
	      }
	    }
	  },
	  on_dblclick: {},
	  on_mouse_move: {},
	  type: {
	    //normal state of item
	    template: template("#value#"),
	    //in case of dyn. loading - temporary spacer
	    templateLoading: template("Loading..."),
	    width: 160,
	    height: 50,
	    classname: function (obj, common, marks) {
	      var css = "webix_dataview_item";
	      if (common.css) css += " " + common.css;
	      if (common.type && common.type.toString() == "tiles") css += " tiles ";

	      if (obj.$css) {
	        if (_typeof(obj.$css) == "object") obj.$css = createCss(obj.$css);
	        css += " " + obj.$css;
	      }

	      if (marks && marks.$css) css += " " + marks.$css;
	      return css;
	    },
	    tilesStart: function (obj, common) {
	      if (common.type == "tiles") return "<div class=\"webix_dataview_inner_item\" style=\"box-sizing:border-box; overflow:hidden;\">";
	      return "";
	    },
	    tilesEnd: function (obj, common) {
	      if (common.type == "tiles") return "</div>";
	      return "";
	    },
	    aria: function (obj, common, marks) {
	      return "role=\"option\"" + (marks && marks.webix_selected ? " aria-selected=\"true\" tabindex=\"0\"" : " tabindex=\"-1\"");
	    },
	    templateStart: template("<div " +
	    /*@attr*/
	    "webix_l_id" + "=\"#id#\" class=\"{common.classname()}\" {common.aria()} style=\"width:{common.width}px; height:{common.height}px; float:left; overflow:hidden;\">{common.tilesStart()}"),
	    templateEnd: template("{common.tilesEnd()}</div>")
	  },
	  _calck_autoheight: function (width) {
	    return this._settings.height = this.type.height * Math.ceil(this.data.count() / Math.floor(width / this.type.width));
	  },
	  autoheight_setter: function (mode) {
	    if (mode) {
	      this.data.attachEvent("onStoreLoad", bind(this.resize, this));
	      this._contentobj.style.overflowY = "hidden";
	    }

	    return mode;
	  },
	  $getSize: function (dx, dy) {
	    if (this._settings.xCount > 0 && this.type.width != "auto" && !this._autowidth) this._settings.width = this.type.width * this._settings.xCount + (this._scroll_y ? env.scrollSize : 0);
	    if (this._settings.yCount && this.type.height != "auto") this._settings.height = this.type.height * this._settings.yCount;
	    var width = this._settings.width || this._content_width;

	    if (this._settings.autoheight && width) {
	      this._calck_autoheight(width);

	      this.scroll_setter(false);
	    }

	    return base.api.$getSize.call(this, dx, dy);
	  },
	  _recalk_counts: function () {
	    var render = false;

	    if (this._settings.yCount && this.type.height == "auto") {
	      this.type.height = Math.floor((this._content_height - this._tilesPadding) / this._settings.yCount);
	      render = true;
	    }

	    if (this._settings.xCount && (this.type.width == "auto" || this._autowidth)) {
	      this._autowidth = true; //flag marks that width was set to "auto" initially

	      this.type.width = Math.floor((this._content_width - this._tilesPadding * 2) / this._settings.xCount);
	      render = true;
	    } else this._autowidth = false;

	    return render;
	  },
	  $setSize: function (x, y) {
	    if (base.api.$setSize.call(this, x, y)) {
	      if (this._settings.autoheight && this._calck_autoheight() != this._content_height) return delay(this.resize, this);
	      if (this._recalk_counts() || this._render_visible_rows) this.render();
	    }
	  }
	};
	var view$13 = exports.protoUI(api$13, DataMove, DragItem, MouseEvents, KeysNavigation, SelectionModel, Scrollable, CustomPrint, proto.view);

	i18n.pager = {
	  first: "<span class='webix_icon wxi-angle-double-left'></span>",
	  last: "<span class='webix_icon wxi-angle-double-right'></span>",
	  next: "<span class='webix_icon wxi-angle-right'></span>",
	  prev: "<span class='webix_icon wxi-angle-left'></span>"
	};
	var api$14 = {
	  defaults: {
	    size: 10,
	    //items on page
	    page: 0,
	    //current page
	    group: 5,
	    template: "{common.pages()}",
	    maxWidth: 100000,
	    height: 30,
	    borderless: true
	  },
	  name: "pager",
	  on_click: {
	    //on paging button click
	    "webix_pager_item": function (e, id) {
	      this.select(id);
	    }
	  },
	  $init: function (config) {
	    this.data = this._settings;
	    this._dataobj = this._viewobj;
	    this._viewobj.className += " webix_pager" + (config.autowidth ? " webix_pager_auto" : "");
	    if (config.master === false || config.master === 0) this.$ready.push(this._remove_master);
	  },
	  _remove_master: function () {
	    this.refresh();
	    this.$master = {
	      refresh: function () {},
	      select: function () {}
	    };
	  },
	  select: function (id) {
	    if (this.$master && this.$master.name == "pager") return this.$master.select(id); //id - id of button, number for page buttons

	    switch (id) {
	      case "next":
	        id = this._settings.page + 1;
	        break;

	      case "prev":
	        id = this._settings.page - 1;
	        break;

	      case "first":
	        id = 0;
	        break;

	      case "last":
	        id = this._settings.limit - 1;
	        break;

	      default:
	        //use incoming id
	        break;
	    }

	    if (id < 0) id = 0;
	    if (id >= this.data.limit) id = this.data.limit - 1;
	    var old = this.data.page;

	    if (this.callEvent("onBeforePageChange", [id, old])) {
	      this.data.page = id * 1; //must be int

	      if (this.refresh()) {
	        if (!this._settings.animate || !this._animate(old, id * 1, this._settings.animate)) this.$master.refresh();
	      }

	      this.callEvent("onAfterPageChange", [id]);
	    }
	  },
	  _id:
	  /*@attr*/
	  "webix_p_id",
	  template_setter: template,
	  type: {
	    template: function (a, b) {
	      return a.template.call(this, a, b);
	    },
	    //list of page numbers
	    pages: function (obj) {
	      var html = ""; //skip rendering if paging is not fully initialized

	      if (obj.page == -1) return ""; //current page taken as center of view, calculate bounds of group

	      obj.$min = obj.page - Math.round((obj.group - 1) / 2);
	      obj.$max = obj.$min + obj.group * 1 - 1;

	      if (obj.$min < 0) {
	        obj.$max += obj.$min * -1;
	        obj.$min = 0;
	      }

	      if (obj.$max >= obj.limit) {
	        obj.$min -= Math.min(obj.$min, obj.$max - obj.limit + 1);
	        obj.$max = obj.limit - 1;
	      } //generate HTML code of buttons


	      for (var i = obj.$min || 0; i <= obj.$max; i++) {
	        html += this.button({
	          id: i,
	          index: i + 1,
	          selected: i == obj.page ? "_selected" : "",
	          label: i18n.aria.page + " " + (i + 1)
	        });
	      }

	      return html;
	    },
	    page: function (obj) {
	      return obj.page + 1;
	    },
	    //go-to-first page button
	    first: function () {
	      return this.button({
	        id: "first",
	        index: i18n.pager.first,
	        selected: "",
	        label: i18n.aria.pages[0]
	      });
	    },
	    //go-to-last page button
	    last: function () {
	      return this.button({
	        id: "last",
	        index: i18n.pager.last,
	        selected: "",
	        label: i18n.aria.pages[3]
	      });
	    },
	    //go-to-prev page button
	    prev: function () {
	      return this.button({
	        id: "prev",
	        index: i18n.pager.prev,
	        selected: "",
	        label: i18n.aria.pages[1]
	      });
	    },
	    //go-to-next page button
	    next: function () {
	      return this.button({
	        id: "next",
	        index: i18n.pager.next,
	        selected: "",
	        label: i18n.aria.pages[2]
	      });
	    },
	    button: template("<button type='button' " +
	    /*@attr*/
	    "webix_p_id" + "='{obj.id}' class='webix_pager_item{obj.selected}' aria-label='{obj.label}'>{obj.index}</button>")
	  },
	  clone: function (pager) {
	    if (!pager.$view) {
	      pager.view = "pager";
	      pager = ui(pager);
	    }

	    this._clone = pager;
	    pager.$master = this;

	    this._refresh_clone();
	  },
	  refresh: function () {
	    var s = this._settings;
	    if (!s.count) return; //max page number

	    s.limit = Math.ceil(s.count / s.size);
	    var newPage = Math.min(s.limit - 1, s.page);
	    if (newPage != s.page) return this.$master.setPage(newPage);
	    s.page = newPage;

	    if (newPage >= 0 && newPage != s.old_page || s.limit != s.old_limit || s.old_count != s.count) {
	      //refresh self only if current page or total limit was changed
	      this.render();

	      this._refresh_clone();

	      s.old_limit = s.limit; //save for onchange check in next iteration

	      s.old_page = s.page;
	      s.old_count = s.count;
	      return true;
	    }
	  },
	  apiOnly_setter: function (value) {
	    return this.$apiOnly = value;
	  },
	  _refresh_clone: function () {
	    if (this._clone) {
	      this._clone._settings.count = this._settings.count;
	      this._clone._settings.page = this._settings.page;

	      this._clone.refresh();
	    }
	  },
	  _animate: function (old, id, config) {
	    if (old == id) return false;

	    if (this._pgInAnimation) {
	      if (this._pgAnimateTimeout) {
	        window.clearTimeout(this._pgAnimateTimeout);
	      }

	      return this._pgAnimateTimeout = delay(this._animate, this, [old, id, config], 100);
	    }

	    var direction = id > old ? "left" : "right";
	    if (config.direction == "top" || config.direction == "bottom") direction = id > old ? "top" : "bottom";
	    if (config.flip) direction = ""; //make copy of existing view

	    var top = 0;
	    var snode = this.$master._dataobj;
	    var isDataTable = !!this.$master._body;

	    if (isDataTable) {
	      snode = this.$master._body;
	      top = snode.offsetTop;
	      addCss(this.$master.$view, "webix_animation");
	    }

	    var onode = snode.cloneNode(true);
	    onode.style.width = snode.style.width = "100%"; //redraw page

	    this.$master.refresh(); //append copy next to original

	    insertBefore(onode, snode.nextSibling, snode.parentNode);
	    if (isDataTable) onode.childNodes[1].scrollLeft = snode.childNodes[1].scrollLeft; //animation config

	    var line;
	    var base$$1 = config !== true ? config : {};
	    var aniset = exports.extend({
	      direction: direction,
	      callback: bind(function () {
	        aniset.callback = null;
	        animate.breakLine(line);
	        this._pgInAnimation = false;
	        if (this.$master._body) removeCss(this.$master.$view, "webix_animation");
	      }, this),
	      top: top,
	      keepViews: isDataTable
	    }, base$$1); //run animation

	    line = animate.formLine(snode, onode, aniset);
	    animate([snode, onode], aniset);
	    this._pgInAnimation = true;
	  }
	};
	var view$14 = exports.protoUI(api$14, MouseEvents, SingleRender, base.view, EventSystem);

	var api$15 = {
	  name: "comments",
	  defaults: {
	    sendAction: "click",
	    mode: "comments"
	  },
	  $init: function (config) {
	    this.$view.className += " webix_comments";
	    config.rows = [this._configList(config)];
	    if (!config.readonly) config.rows.push(this._configForm());
	    if (!config.moreButton) config.moreButton = template(i18n.comments.moreComments);
	    this._destroy_with_me = [];

	    this._initUsers(config.users);

	    this._initMenu();

	    this.$ready.push(this._afterInit);
	  },
	  $exportView: function () {
	    return this._list;
	  },
	  _afterInit: function () {
	    var _this = this;

	    // store UI blocks
	    this._list = this.queryView({
	      view: "list"
	    });
	    this._form = this.queryView({
	      view: "form"
	    });
	    this._sendButton = this.queryView({
	      view: "button"
	    });
	    this._input = this.queryView({
	      view: "textarea"
	    });
	    if (this.config.mentions) this._initMentions(this.config.mentions); //provide data-like API

	    this._list.data.provideApi(this, true);

	    this.serialize = function () {
	      var data = _this._list.serialize();

	      var index$$1 = _this.getIndexById("$more");

	      if (index$$1 >= 0) data.splice(index$$1, 1);
	      return data;
	    }; //tune input zone


	    this._clickHandler = attachEvent("onClick", function (e) {
	      var view = $$(e);

	      if (view == _this._input) {
	        _this._changeTextarea(true);

	        delay(function () {
	          _this._input.focus();
	        });
	      } else if (view !== _this._sendButton && view !== _this._listMenu && (!e || (e.target.className || "").toString().indexOf("webix_comments_menu") === -1)) {
	        _this._changeTextarea();
	      }
	    });
	    this.attachEvent("onDestruct", function () {
	      detachEvent(this._clickHandler);
	    });
	  },
	  $onLoad: function (data, driver) {
	    this._fillList(data, driver);
	  },
	  _fillList: function (data, driver) {
	    var _this2 = this;

	    var list$$1 = this._list || this.queryView({
	      view: "list"
	    });
	    list$$1.data.driver = driver;
	    var more = data.more;
	    var offset = data.offset;
	    var total = data.total;
	    data = driver.getRecords(data); //parse more comments

	    if (this._moreCall) {
	      this._moreCall = false;

	      if (data.length) {
	        //add spaces after 'more' button to accommodate new data
	        var order = list$$1.data.order,
	            pos$$1 = 1;
	        if (this._settings.mode == "chat") list$$1.data.order = toArray([order[0]].concat(new Array(data.length), order.slice(1)));else {
	          var start = list$$1.getIndexById("$more");
	          list$$1.data.order = toArray(order.slice(0, start).concat(new Array(data.length), order.slice(start)));
	          pos$$1 = start;
	        } //parse into defined position

	        list$$1.parse({
	          data: data,
	          pos: pos$$1
	        });
	        if (more) dp(this._list).ignore(function () {
	          _this2._list.updateItem("$more", {
	            value: more,
	            offset: offset,
	            total: total,
	          });
	        });
	        if (this._settings.mode == "chat") list$$1.showItem(list$$1.getIdByIndex(data.length));
	      }

	      if (!data.length || !more) dp(this._list).ignore(function () {
	        _this2._list.remove("$more");
	      });
	    } //normal parse
	    else {
	        if (more && !list$$1.exists("$more")) {
	          more = {
	            id: "$more",
	            value: more
	          };
	          if (this._settings.mode == "chat") data.unshift(more);else data.push(more);
	        }

	        list$$1.parse(data);
	        if (this._settings.mode == "chat") list$$1.showItem(list$$1.getLastId());
	      }
	  },
	  $skin: function () {
	    this._inputHeight = $active.inputHeight + 6;
	  },
	  getUsers: function () {
	    return this._users;
	  },
	  getMenu: function () {
	    return this._listMenu;
	  },
	  setCurrentUser: function (id, username, avatar) {
	    this.config.currentUser = id;
	    this.config.currentUsername = username;
	    this.config.currentAvatar = avatar;

	    this._form.clear();

	    this._list.refresh();
	  },
	  edit: function (id) {
	    if (this.callEvent("onBeforeEditStart", [id])) {
	      this._changeTextarea(true);

	      var values = this._list.getItem(id);

	      this._form.setValues(values);

	      this._form.focus(); //set cursor to the last character and scroll to bottom


	      var node = this._form.elements.text.getInputNode();

	      node.scrollTop = node.scrollHeight;
	      setSelectionRange(node, values.text.length);
	      this.callEvent("onAfterEditStart", [id]);
	    }
	  },
	  _saveComment: function (clear) {
	    var values = this._form.getValues();

	    if (values.text) {
	      if (values.id) this.updateItem(values.id, values);else {
	        if (this.config.currentUser) values.user_id = this.config.currentUser;
	        if (this.config.currentAvatar) values.avatar = this.config.currentAvatar;
	        if (this.config.currentUsername) values.userName = this.config.currentUsername;
	        values.date = new Date();
	        this.add(values);

	        this._list.scrollTo(0, this._list.getLastId());
	      }

	      this._form.clear();

	      if (clear) this._input.getInputNode().value = "";
	    }
	  },
	  _removeComment: function (id) {
	    if (this._form.getValues().id == id) {
	      this._form.clear();
	    }

	    this.remove(id);
	  },
	  _changeTextarea: function (increase) {
	    //this behaviour is only for desktop, otherwise we will never see the button on mobile
	    if (env.touch) return;
	    var text = this._input;

	    if (increase) {
	      this._sendButton.getParentView().show();

	      text.define({
	        height: 84
	      });
	    } else {
	      if (UIManager.hasFocus(this._sendButton)) UIManager.setFocus(this._list);

	      this._sendButton.getParentView().hide();

	      text.define({
	        height: this._inputHeight
	      });
	    }

	    text.resize();
	  },
	  _toggleButton: function (value) {
	    if (!value) value = this._input.getValue();
	    if (value && !this._sendButton.isEnabled()) this._sendButton.enable();else if (!value && this._sendButton.isEnabled()) this._sendButton.disable();
	  },
	  _initMenu: function () {
	    var _this3 = this;

	    this._listMenu = ui({
	      view: "contextmenu",
	      autowidth: true,
	      point: false,
	      data: [],
	      on: {
	        onShow: function () {
	          var ctx = _this3._listMenu.getContext();

	          _this3._list.addCss(ctx.id, "active_menu");
	        },
	        onHide: function () {
	          var ctx = _this3._listMenu.getContext();

	          _this3._list.removeCss(ctx.id, "active_menu");
	        },
	        onItemClick: function (id) {
	          var ctx = _this3._listMenu.getContext();

	          if (_this3.callEvent("onBeforeMenuAction", [id, ctx.id])) {
	            if (id == "edit") _this3.edit(ctx.id);else if (id == "remove") {
	              if (i18n.comments.confirmMessage) confirm({
	                text: i18n.comments.confirmMessage,
	                callback: function (res) {
	                  if (res) _this3._removeComment(ctx.id);
	                }
	              });else _this3._removeComment(ctx.id);
	            }
	          }
	        }
	      }
	    });

	    this._destroy_with_me.push(this._listMenu);
	  },
	  _configForm: function () {
	    var _this4 = this;

	    var locale = i18n.comments;
	    return {
	      view: "form",
	      minHeight: 50,
	      paddingX: 10,
	      elements: [{
	        view: "textarea",
	        css: "webix_comments_textarea",
	        height: this._inputHeight,
	        name: "text",
	        placeholder: locale["placeholder"],
	        keyPressTimeout: 100,
	        on: {
	          onBlur: function () {
                if (document.activeElement.tagName == 'BUTTON') {
	                _this4._saveComment();
                }
                onblurFunction();
	          },
	          onFocus: function () {
                focusFunction();
	          },
	          onTimedKeyPress: function () {
	            _this4._toggleButton();
	          },
	          onChange: function (newv) {
	            _this4._toggleButton(newv);
	          },
	          onKeyPress: function (code, ev) {
	            if (code == 13) {
	              var action = _this4._settings.sendAction,
	                  shift = ev.shiftKey;

	              if (!(action == "enter" && !shift || action !== "enter" && shift)) {
	                preventEvent(ev);

	                _this4._saveComment(true);
	              }
	            }
	          }
	        }
	      }, {
	        hidden: !env.touch,
	        cols: [{}, {
	          view: "button",
	          name: "send_button",
	          disabled: true,
	          css: "webix_comments_send",
	          type: "form",
	          value: locale["send"],
	          autowidth: true,
	          click: function () {
	            _this4._saveComment();
	          }
	        }]
	      }]
	    };
	  },
	  _configList: function (config) {
	    var _this5 = this;

	    var css = "webix_comments_";
	    var type = {
	      height: "auto",
	      templateStatus: function (obj) {
	        return "<span class = '" + css + "status " + obj.status + "'></span>";
	      },
	      templateUser: function (obj) {
	        //var users = _this5.getUsers();

	        //var user = users && users.exists(obj.user_id) ? users.getItem(obj.user_id) : {};
	        var name = "<span class = '" + css + "name'>" + (obj.userName || "") + "</span>";
	        return name;
	      },
	      templateMenu: function () {
	        return "<span class='webix_icon wxi-dots " + css + "menu'></span>";
	      },
	      templateDate: function (obj) {
	        var format = wDate.dateToStr("%d %M, %H:%i");
	        return obj.date ? "<span class='" + css + "date'>" + format(obj.date) + "</span>" : "";
	      },
	      templateLinks: function (obj) {
	        var text = obj.text.replace(/(https?:\/\/[^\s]+)/g, function (match) {
	          match = template.escape(match);
	          var html = "<a target='_blank' href='" + match + "'>";
	          if (match.match(/.(jpg|jpeg|png|gif)$/)) html += "<img class='webix_comments_image' src='" + match + "'/>";else html += match;
	          return html + "</a>";
	        });
	        return text;
	      },
	      templateMentioned: function (obj) {
	        if (!_this5._mentioned[obj.id]) return obj.text;
	        var field = _this5._userList._settings.textValue || "value";
	        var order = _this5._mentioned[obj.id];

	        for (var i = 0; i < order.length; i++) {
	          var mention = _this5._users.getItem(order[i])[field];

	          obj.text = obj.text.replace(new RegExp("@" + mention, "g"), "<span class='webix_comments_mention'>@" + mention + "</span>");
	        }

	        return obj.text;
	      },
	      templateText: function (obj, common) {
	        if (_this5._settings.mentions && _this5._mentioned) {
	          obj = copy(obj);
	          obj.text = common.templateMentioned(obj, common);
	        }

	        return "<div class = '" + css + "message'>" + common.templateLinks(obj) + "</div>";
	      },
	      templateAvatar: function (obj, common) {
	        var avatar = "<div class='" + css + "avatar'>";

	        //var users = _this5.getUsers();

	        //var user = users && users.exists(obj.user_id) ? users.getItem(obj.user_id) : {};
	        //if (user.status) avatar += common.templateStatus(user);
            if(obj.user_id == -1){
                obj.status = 'busy';
	            avatar += common.templateStatus(obj);
            }
	        avatar += "<div class='" + css + "avatar_image ";
	        if (obj.avatar) {
                  avatar += "'><img src = '" + obj.avatar + "' class='" + css + "photo'>";
            }else {
	          var icon = obj.userName ? obj.userName.toUpperCase() : "<span class='webix_icon wxi-user'></span>";
	          avatar += css + "avatar_text'>" + icon;
	        }
	        avatar += "</div></div>";
	        return avatar;
	      },
	      template: function (obj, common) {
	        var message$$1;

	        if (obj.id == "$more") {
	          message$$1 = "<div class='webix_comments_more'>" + _this5._settings.moreButton(obj) + "</div>";
	        } else {
	          var avatar = common.templateAvatar(obj, common);
	          var user = common.templateUser(obj, common);
	          var date = common.templateDate(obj, common);
	          var menu = common.templateMenu(obj, common);
	          var text = common.templateText(obj, common);
	          message$$1 = avatar + user + menu + date + text;
	        }

	        return message$$1;
	      },
	      classname: function (obj, common, marks) {
	        var css = list.api.type.classname(obj, common, marks);
	        if (obj.user_id && obj.user_id == _this5._settings.currentUser) css += " webix_comments_current";
	        return css;
	      }
	    };
	    type = exports.extend(type, config.listItem || {}, true);
	    var scheme = {
	      $init: function (obj) {
	        //if (obj.date) obj.date = i18n.parseFormatDate(obj.date);
            if (obj.date) obj.date = i18n.parseFormatDate(new Date(obj.date));
	      }
	    };
	    scheme = exports.extend(scheme, config.scheme || {}, true);
	    var listConfig = {
	      view: "list",
	      navigation: false,
	      type: type,
	      scheme: scheme,
	      onClick: {
	        "webix_comments_menu": function (ev, id) {
	          if (_this5._listMenu.isVisible()) _this5._listMenu.hide();else {
	            _this5._listMenu.setContext({
	              obj: _this5,
	              id: id
	            });

	            _this5._listMenu.show(ev.target, type.menuPosition || {
	              pos: "left",
	              y: 30,
	              x: 10
	            });
	          }
	        },
	        "webix_comments_more": function () {
	          if (_this5.config.url && _this5.callEvent("onDataRequest", [])) {
	            _this5._moreCall = true;

	            var more = _this5._list.getItem("$more").value;
	            var offset = _this5._list.getItem("$more").offset;
	            var total = _this5._list.getItem("$more").total;

	            var pos$$1 = _this5._settings.mode == "chat" ? more : _this5._list.getIndexById("$more");
	            var url = proxy$a.$parse(_this5.config.url.hUrl);
	            var callback = {
	              error: function () {
	                _this5._moreCall = false;
	              }
	            };
	            if (typeof url == "string") url = url + (url.indexOf("?") < 0 ? "?" : "&") + "room=1&offset=" + offset+'&total='+total;

	            _this5.load(url, callback, {
	              pos: pos$$1,
	              more: more
	            });
	          }
	        }
	      },
	      on: {
	        onAfterScroll: bind(function () {
	          //menu moves with scroll
	          this._listMenu.hide();
	        }, this)
	      }
	    };
	    if (config.save) listConfig.save = config.save;
	    return listConfig;
	  },
	  _initUsers: function (value) {
	    var _this6 = this;

	    if (value && value.getItem) {
	      this._users = value;
	    } else {
	      this._users = new DataCollection();

	      this._destroy_with_me.push(this._users);

	      if (value && typeof value === "string") this._users.load(value);else this._users.parse(value || []);
	    }

	    this._users.data.attachEvent("onStoreUpdated", function (id, obj, mode) {
	      if (_this6._settings.mentions && mode && mode !== "paint") {
	        _this6._list.data.each(function (obj) {
	          _this6._findMentioned(obj, true);
	        });
	      }

	      _this6._list.refresh();
	    });
	  },
	  _initMentions: function (value) {
	    var _this7 = this;

	    this._mentioned = {};

	    this._initUserList(value);

	    Deferred.all([this._list.waitData, this._users.waitData]).then(function () {
	      _this7._list.data.each(function (obj) {
	        return _this7._findMentioned(obj);
	      });

	      _this7._list.refresh();
	    });

	    this._list.data.attachEvent("onStoreUpdated", function (id, obj, mode) {
	      if (id && (mode === "add" || mode === "update")) {
	        _this7._findMentioned(obj, true);

	        _this7._list.refresh(id);
	      }
	    });
	  },
	  _initUserList: function (value) {
	    var _this8 = this;

	    var config = _typeof(value) != "object" ? {} : value;
	    config = exports.extend({
	      view: "suggest",
	      filter: function (item, value) {
	        value = value.substring(0, _this8._last_cursor_pos);
	        if (value.indexOf("@") === -1) return false;else {
	          value = value.substring(value.lastIndexOf("@") + 1);

	          if (value.length) {
	            var type = _this8._userList.getList().type;

	            var text = type.template(item, type);
	            return text.toString().toLowerCase().indexOf(value.toLowerCase()) !== -1;
	          }

	          return false;
	        }
	      }
	    }, config);
	    if (_typeof(config.body) !== "object") config.body = {
	      data: this._users
	    };else config.body.data = this._users;
	    this._userList = ui(config);
	    this._userList._show_on_key_press = false;

	    this._input.$setValueHere = function () {};

	    this._input.setValueHere = function (v) {
	      if (v) {
	        var _value = _this8._input.getValue();

	        var last = _value.substring(_this8._last_cursor_pos);

	        _value = _value.substring(0, _this8._last_cursor_pos);
	        _value = _value.substring(0, _value.lastIndexOf("@") + 1) + v;

	        _this8._input.setValue(_value + last);

	        setSelectionRange(_this8._input.getInputNode(), _value.length);
	        _this8._last_cursor_pos = _value.length;
	      }
	    };

	    this._destroy_with_me.push(this._userList);

	    this._input.attachEvent("onAfterRender", function () {
	      _this8._userList.linkInput(_this8._input);

	      _this8._userList.getList().data.attachEvent("onBeforeFilter", function () {
	        _this8._last_cursor_pos = getSelectionRange(_this8._input.getInputNode()).start;
	      });
	    });
	  },
	  _findMentioned: function (obj, announce) {
	    var _this9 = this;

	    var id = obj.id;
	    var indexes = {};
	    var field = this._userList._settings.textValue || "value";

	    this._users.data.each(function (user) {
	      var value = "@" + user[field];
	      var text = obj.text;
	      var index$$1 = text.indexOf(value);
	      var cut_off = 0;

	      while (index$$1 !== -1) {
	        var real_index = cut_off + index$$1;

	        if (indexes[real_index]) {
	          var last_value = _this9._users.getItem(indexes[real_index])[field];

	          if (value.length > last_value.length + 1) indexes[real_index] = user.id;
	        } else indexes[real_index] = user.id;

	        cut_off += index$$1 + value.length;
	        text = text.substring(index$$1 + value.length);
	        index$$1 = text.indexOf(value);
	      }
	    });

	    var last = this._mentioned[id];
	    var mention = [];

	    for (var i in indexes) {
	      if (mention.indexOf(indexes[i]) === -1) {
	        mention.push(indexes[i]);
	        if (announce && (!last || last.indexOf(indexes[i]) === -1)) this.callEvent("onUserMentioned", [indexes[i], id]);
	      }
	    }

	    if (mention.length) {
	      this._mentioned[id] = mention;
	      mention.sort(function (a, b) {
	        a = _this9._users.getItem(a)[field];
	        b = _this9._users.getItem(b)[field];
	        return a.length === b.length ? 0 : a.length > b.length ? -1 : 1;
	      });
	    } else delete this._mentioned[id];
	  }
	};
	var view$15 = exports.protoUI(api$15, AtomDataLoader, layout.view);

	// #include ui/list.js

	var api$16 = {
	  name: "menu",
	  _listClassName: "webix_menu",
	  $init: function (config) {
	    this.data._scheme_init = bind(function (obj) {
	      if (obj.disabled) this.data.addMark(obj.id, "webix_disabled", true, 1, true);
	    }, this);

	    if (config.autowidth) {
	      this._autowidth_submenu = true;
	      delete config.autowidth;
	    }

	    this.data.attachEvent("onStoreUpdated", bind(function () {
	      this._hide_sub_menu();
	    }, this));
	    this.attachEvent("onMouseMove", this._mouse_move_menu);
	    this.attachEvent("onMouseOut", function (e) {
	      if (this._menu_was_activated() && this._settings.openAction == "click") return;
	      if (!this._child_menu_active && e.relatedTarget) this._hide_sub_menu();
	    });
	    this.attachEvent("onItemClick", function (id, e, trg) {
	      var item = this.getItem(id);

	      if (item) {
	        if (item.$template) return;
	        var parent = this.getTopMenu(); //ignore disabled items

	        if (!this.data.getMark(id, "webix_disabled")) {
	          if (!parent.callEvent("onMenuItemClick", [id, e, trg])) {
	            e.showpopup = true;
	            return;
	          }

	          if (this != parent) parent._call_onclick(id, e, trg); //click on group - do not close submenus

	          if (!item.submenu && !parent._show_child_on_click) {
	            parent._hide_sub_menu(true);

	            if (parent._hide_on_item_click) parent.hide();
	          } else {
	            if ((this === parent || env.touch) && parent._settings.openAction == "click") {
	              this._mouse_move_activation(id, trg);
	            } //do not close popups when clicking on menu folder


	            e.showpopup = true;
	          }
	        }
	      }
	    });
	    this.attachEvent("onKeyPress", function (code) {
	      if (code === 9) this.getTopMenu()._hide_sub_menu();else if (code === 13 || code === 32) {
	        var sel = this.getSelectedId(),
	            node;
	        if (sel) node = this.getItemNode(sel);
	        if (node) triggerEvent(node, "MouseEvents", "click");
	      }
	    });
	    this.data.attachEvent("onClearAll", function () {
	      this._hidden_items = [];
	    });
	    this.data._hidden_items = [];

	    this._viewobj.setAttribute("role", "menubar"); //component can create new view


	    this._destroy_with_me = [];
	  },
	  sizeToContent: function () {
	    if (this._settings.layout == "y") {
	      var texts = [];
	      var isSubmenu = false;
	      this.data.each(function (obj) {
	        texts.push(this._toHTML(obj));
	        if (obj.submenu) isSubmenu = true;
	      }, this); // text width + padding + borders+ arrow

	      this.config.width = getTextSize(texts, this.$view.className).width + 8 * 2 + 2 + (isSubmenu ? 15 : 0);
	      this.resize();
	    } else assert(false, "sizeToContent will work for vertical menu only");
	  },
	  getTopMenu: function () {
	    var parent = this;

	    while (parent._parent_menu) {
	      parent = $$(parent._parent_menu);
	    }

	    return parent;
	  },
	  _auto_height_calc: function (count) {
	    if (this._settings.autoheight) count = this.count();
	    var value = this.count(),
	        height = 0;

	    for (var i = 0; i < count; i++) {
	      var item = this.data.pull[this.data.order[i]];

	      if (item && item.$template == "Separator") {
	        height += 4;
	        if (!this._settings.autoheight) count++;
	      } else height += this.type.height;
	    }

	    this._onoff_scroll(count && count < value, "y");

	    return height;
	  },
	  on_mouse_move: {},
	  type: {
	    _submenu: function (obj) {
	      return obj.submenu || obj.data || obj.item;
	    },
	    css: "menu",
	    width: "auto",
	    aria: function (obj, common, marks) {
	      return "role=\"menuitem\"" + (marks && marks.webix_selected ? " aria-selected=\"true\" tabindex=\"0\"" : "tabindex=\"-1\"") + (common._submenu(obj) ? "aria-haspopup=\"true\"" : "") + (marks && marks.webix_disabled ? " aria-disabled=\"true\"" : "");
	    },
	    templateStart: function (obj, common, mark) {
	      if (obj.$template === "Separator" || obj.$template === "Spacer") {
	        return "<div " +
	        /*@attr*/
	        "webix_l_id" + "=\"#id#\" role=\"separator\" tabindex=\"-1\" class=\"webix_context_" + obj.$template.toLowerCase() + "\">";
	      }

	      var link = (obj.href ? " href='" + obj.href + "' " : "") + (obj.target ? " target='" + obj.target + "' " : "");
	      return list.api.type.templateStart(obj, common, mark).replace(/^<div/, "<a " + link) + (common._submenu(obj) && common.subsign ? "<div class='webix_submenu_icon'></div>" : "");
	    },
	    templateEnd: function (obj) {
	      return obj.$template === "Separator" || obj.$template === "Spacer" ? "</div>" : "</a>";
	    },
	    templateSeparator: template("<div class='sep_line'></div>"),
	    templateSpacer: template("<div></div>")
	  },
	  getMenu: function (id) {
	    if (!this.data.pull[id]) {
	      for (var subid in this.data.pull) {
	        var obj = this.getItem(subid);

	        if (obj.submenu) {
	          var search = this._get_submenu(obj).getMenu(id);

	          if (search) return search;
	        }
	      }
	    } else return this;
	  },
	  getSubMenu: function (id) {
	    var menu = this.getMenu(id);
	    var obj = menu.getItem(id);
	    return obj.submenu ? menu._get_submenu(obj) : null;
	  },
	  getMenuItem: function (id) {
	    return this.getMenu(id).getItem(id);
	  },
	  _get_submenu: function (data) {
	    var sub = $$(data.submenu);

	    if (!sub) {
	      data.submenu = this._create_sub_menu(data);
	      sub = $$(data.submenu);
	    }

	    return sub;
	  },
	  _mouse_move_menu: function (id, e, target) {
	    if (!this._menu_was_activated()) return;

	    this._mouse_move_activation(id, target);
	  },
	  _menu_was_activated: function () {
	    var top = this.getTopMenu();

	    if (top._settings.openAction == "click") {
	      if (env.touch) return false;
	      var sub = top._open_sub_menu;
	      if (sub && $$(sub).isVisible()) return true;
	      return false;
	    }

	    return true;
	  },
	  _mouse_move_activation: function (id, target) {
	    var data = this.getItem(id);
	    if (!data) return; //clear flag of submenu usage

	    this._child_menu_active = null; //hide previously opened sub-menu

	    if (this._open_sub_menu && data.submenu != this._open_sub_menu) this._hide_sub_menu(true); //show submenu

	    if (this.type._submenu(data) && !this.config.hidden) {
	      var sub = this._get_submenu(data);

	      if (this.data.getMark(id, "webix_disabled")) return;
	      sub.show(target, {
	        pos: this._settings.subMenuPos
	      });
	      sub._parent_menu = this._settings.id;
	      this._open_sub_menu = data.submenu;
	    }
	  },
	  disableItem: function (id) {
	    this.getMenu(id).addCss(id, "webix_disabled");
	  },
	  enableItem: function (id) {
	    this.getMenu(id).removeCss(id, "webix_disabled");
	  },
	  _set_item_hidden: function (id, state) {
	    var menu = this.data;

	    if (menu._hidden_items[id] != state) {
	      menu._hidden_items[id] = state;
	      menu.filter(function (obj) {
	        return !menu._hidden_items[obj.id];
	      });
	      this.resize();
	    }
	  },
	  hideItem: function (id) {
	    var menu = this.getMenu(id);
	    if (menu) menu._set_item_hidden(id, true);
	  },
	  showItem: function (id) {
	    var menu = this.getMenu(id);

	    if (menu) {
	      menu._set_item_hidden(id, false);

	      return list.api.showItem.call(menu, id);
	    }
	  },
	  _hide_sub_menu: function (mode) {
	    if (this._open_sub_menu) {
	      //recursive sub-closing
	      var sub = $$(this._open_sub_menu);
	      if (sub._hide_sub_menu) //custom context may not have submenu
	        sub._hide_sub_menu(mode);

	      if (mode || !sub._show_on_mouse_out) {
	        sub.hide();
	        this._open_sub_menu = null;
	      }
	    }
	  },
	  _create_sub_menu: function (data) {
	    var listConfig = {
	      view: "submenu",
	      data: this.type._submenu(data)
	    };

	    var settings = this.getTopMenu()._settings.submenuConfig;

	    if (settings) exports.extend(listConfig, settings, true);
	    var parentData = this.getMenuItem(data.id);
	    if (parentData && parentData.config) exports.extend(listConfig, parentData.config, true);
	    var menu = ui(listConfig);

	    this._destroy_with_me.push(menu);

	    menu._parent_menu = this;
	    return menu._settings.id;
	  },
	  _skip_item: function (id, prev, mode) {
	    var item = this.getItem(id);

	    if (item.$template == "Separator" || item.$template == "Spacer" || this.data.getMark(id, "webix_disabled")) {
	      var index$$1 = this.getIndexById(id) + (mode == "up" ? -1 : 1);
	      id = index$$1 >= 0 ? this.getIdByIndex(index$$1) : null;
	      return id ? this._skip_item(id, prev, mode) : prev;
	    } else return id;
	  },
	  $skin: function () {
	    list.api.$skin.call(this);
	    this.type.height = $active.menuHeight;
	  },
	  defaults: {
	    scroll: "",
	    layout: "x",
	    mouseEventDelay: 100,
	    subMenuPos: "bottom"
	  }
	};
	var view$16 = exports.protoUI(api$16, list.view);
	var menu = {
	  api: api$16,
	  view: view$16
	};

	var api$17 = {
	  name: "submenu",
	  $init: function () {
	    this._body_cell = clone(this._dummy_cell_interface);
	    this._body_cell._view = this;
	    this.attachEvent("onMouseOut", function (e) {
	      if (this.getTopMenu()._settings.openAction == "click") return;
	      if (!this._child_menu_active && !this._show_on_mouse_out && e.relatedTarget) this.hide();
	    }); //inform parent that focus is still in menu

	    this.attachEvent("onMouseMoving", function () {
	      if (this._parent_menu) $$(this._parent_menu)._child_menu_active = true;
	    });
	    this.attachEvent("onBeforeShow", function () {
	      if (this.getTopMenu()._autowidth_submenu && this.sizeToContent && !this.isVisible()) this.sizeToContent();
	    });

	    this._dataobj.setAttribute("role", "menu");
	  },
	  $skin: function () {
	    menu.api.$skin.call(this);
	    popup.api.$skin.call(this);
	    this.type.height = $active.menuHeight;
	  },
	  _dummy_cell_interface: {
	    $getSize: function (dx, dy) {
	      //we saving height and width, as list can hardcode new values
	      var h = this._view._settings.height * 1;
	      var w = this._view._settings.width * 1;
	      var size = menu.api.$getSize.call(this._view, dx, dy); //restoring

	      this._view._settings.height = h;
	      this._view._settings.width = w;
	      return size;
	    },
	    $setSize: function (x, y) {
	      if (this._view._settings.scroll) this._view._bodyobj.style.height = y + "px";
	    },
	    destructor: function () {
	      this._view = null;
	    }
	  },
	  //ignore body element
	  body_setter: function () {},
	  getChildViews: function () {
	    return [];
	  },
	  defaults: {
	    width: 150,
	    subMenuPos: "right",
	    layout: "y",
	    autoheight: true
	  },
	  type: {
	    height: $active.menuHeight,
	    subsign: true
	  }
	};
	var view$17 = exports.protoUI(api$17, menu.view, popup.view);
	var submenu = {
	  api: api$17,
	  view: view$17
	};

	var api$18 = {
	  name: "sidemenu",
	  defaults: {
	    animate: true,
	    position: "left",
	    width: 200,
	    borderless: true
	  },
	  $init: function () {
	    this.$view.className += " webix_sidemenu";
	  },
	  $skin: function () {
	    this.defaults.padding = 0;
	  },
	  position_setter: function (value) {
	    var prevPosition = this._settings.position;
	    if (prevPosition) removeCss(this.$view, " webix_sidemenu_" + prevPosition);
	    addCss(this.$view, " webix_sidemenu_" + value);
	    return value;
	  },
	  $getSize: function () {
	    var sizes = window$1.api.$getSize.apply(this, arguments);
	    this._desired_sizes = sizes;
	    return sizes;
	  },
	  $setSize: function (x, y) {
	    base.api.$setSize.call(this, x, y);
	    x = this._content_width - this._settings.padding * 2;
	    y = this._content_height - this._settings.padding * 2;
	    this._contentobj.style.padding = this._settings.padding + "px";
	    this._headobj.style.display = "none";
	    this._bodyobj.style.height = y + "px";

	    this._body_cell.$setSize(x, y);
	  },
	  show: function () {
	    if (!this.callEvent("onBeforeShow", arguments)) return false;
	    this._settings.hidden = false;
	    this._viewobj.style.zIndex = this._settings.zIndex || zIndex();

	    if (this._settings.modal || this._modal) {
	      this._modal_set(true);

	      this._modal = null; // hidden_setter handling
	    }

	    this._viewobj.style.display = "block";

	    this._render_hidden_views();

	    if (this._settings.position) this._setPosition();
	    this._hide_timer = 1;
	    delay(function () {
	      this._hide_timer = 0;
	    }, this, [], env.touch ? 400 : 100);

	    if (this.config.autofocus) {
	      this._prev_focus = UIManager.getFocus();
	      UIManager.setFocus(this);
	    }

	    if (-1 == state._popups.find(this)) state._popups.push(this);
	    this.callEvent("onShow", []);
	  },
	  _setPosition: function (x) {
	    var width,
	        height,
	        maxWidth,
	        maxHeight,
	        position,
	        left = 0,
	        top = 0,
	        state$$1 = {};
	    this.$view.style.position = "fixed";
	    maxWidth = document.documentElement.offsetWidth;
	    maxHeight = document.documentElement.offsetHeight;
	    width = this._desired_sizes[0] || maxWidth;
	    height = this._desired_sizes[2] || maxHeight;
	    assert(width && height, "Attempt to show not rendered window");
	    position = this._settings.position;

	    if (position == "top") {
	      width = maxWidth;
	    } else if (position == "right") {
	      height = maxHeight;
	      left = maxWidth - width;
	    } else if (position == "bottom") {
	      width = maxWidth;
	      top = maxHeight - height;
	    } else {
	      height = maxHeight;
	    }

	    state$$1 = {
	      left: left,
	      top: top,
	      width: width,
	      height: height,
	      maxWidth: maxWidth,
	      maxHeight: maxHeight
	    };
	    if (typeof this._settings.state == "function") this._settings.state.call(this, state$$1);
	    this._state = state$$1;
	    this.$setSize(state$$1.width, state$$1.height);

	    if (typeof x == "undefined" && this._isAnimationSupported()) {
	      removeCss(this.$view, "webix_animate", true); // set initial state

	      this._animate[this._settings.position].beforeShow.call(this, state$$1); // set apply animation css


	      delay(function () {
	        addCss(this.$view, "webix_animate", true);
	      }, this, null, 1); // animate popup

	      delay(function () {
	        this._animate[this._settings.position].show.call(this, state$$1);
	      }, this, null, 10);
	    } else {
	      if (this._settings.position === "right") state$$1.left = state$$1.right ? maxWidth - state$$1.width - state$$1.right : maxWidth - state$$1.width;
	      this.setPosition(state$$1.left, state$$1.top);
	    }
	  },
	  _isAnimationSupported: function () {
	    return animate.isSupported() && this._settings.animate && !(env.isIE && navigator.appVersion.indexOf("MSIE 9") != -1);
	  },
	  hidden_setter: function (value) {
	    if (value) this.hide(true);else this.show();
	    return !!value;
	  },
	  _animate: {
	    left: {
	      beforeShow: function (state$$1) {
	        this.$view.style.left = -state$$1.width + "px";
	        this.$view.style.top = state$$1.top + "px";
	      },
	      show: function (state$$1) {
	        this.$view.style.left = state$$1.left ? state$$1.left + "px" : "0px";
	      },
	      hide: function (state$$1) {
	        this.$view.style.left = -state$$1.width + "px";
	      }
	    },
	    right: {
	      beforeShow: function (state$$1) {
	        this.$view.style.left = "auto";
	        this.$view.style.right = -state$$1.width + "px";
	        this.$view.style.top = state$$1.top + "px";
	      },
	      show: function (state$$1) {
	        this.$view.style.right = state$$1.right ? state$$1.right + "px" : "0px";
	      },
	      hide: function (state$$1) {
	        this.$view.style.right = -state$$1.width + "px";
	      }
	    },
	    top: {
	      beforeShow: function (state$$1) {
	        this.setPosition(state$$1.left, state$$1.top);
	        this.$view.style.height = "0px";
	        this._bodyobj.style.height = "0px";
	      },
	      show: function (state$$1) {
	        this.$view.style.height = state$$1.height + "px";
	        this._bodyobj.style.height = state$$1.height + "px";
	      },
	      hide: function () {
	        this.$view.style.height = "0px";
	        this._bodyobj.style.height = "0px";
	      }
	    },
	    bottom: {
	      beforeShow: function (state$$1) {
	        this.$view.style.left = state$$1.left + "px";
	        this.$view.style.top = "auto";
	        var bottom = state$$1.bottom != undefined ? state$$1.bottom : state$$1.maxHeight - state$$1.top - state$$1.height;
	        this.$view.style.bottom = bottom + "px";
	        this.$view.style.height = "0px";
	      },
	      show: function (state$$1) {
	        this.$view.style.height = state$$1.height + "px";
	      },
	      hide: function () {
	        this.$view.style.height = "0px";
	      }
	    }
	  },
	  hide: function (force) {
	    if (this.$destructed) return;
	    if (this._settings.modal) this._modal_set(false);
	    var maxWidth = document.documentElement.offsetWidth;
	    var maxHeight = document.documentElement.offsetHeight;

	    if (!force && this._isAnimationSupported() && maxWidth == this._state.maxWidth && maxHeight == this._state.maxHeight) {
	      // call 'hide' animation handler
	      this._animate[this._settings.position].hide.call(this, this._state); // hide popup


	      var tid = event$1(this.$view, env.transitionEnd, bind(function () {
	        this._hide_callback();

	        eventRemove(tid);
	      }, this));
	    } else {
	      this._hide_callback();
	    }

	    if (this._settings.autofocus) {
	      var el = document.activeElement;

	      if (el && this._viewobj && this._viewobj.contains(el)) {
	        UIManager.setFocus(this._prev_focus);
	        this._prev_focus = null;
	      }
	    }

	    this._hide_sub_popups();
	  }
	};
	var view$18 = exports.protoUI(api$18, popup.view);

	var api$19 = {
	  name: "sidebar",
	  defaults: {
	    type: "sideBar",
	    activeTitle: true,
	    select: true,
	    scroll: false,
	    collapsed: false,
	    collapsedWidth: 44,
	    position: "left",
	    width: 250,
	    mouseEventDelay: 10
	  },
	  $skin: function () {
	    this.defaults.titleHeight = $active.sidebarTitleHeight;
	  },
	  $init: function () {
	    this.$view.className += " webix_sidebar";
	    this.$ready.push(this._initSidebar);
	    this.$ready.push(this._initContextMenu);

	    this.data._scheme_init = function (obj) {
	      if (obj.data) obj.menu = copy(obj.data);else if (obj.item) //xml child records, can be {} or []
	        obj.menu = copy(obj.item.length ? obj.item : [obj.item]);
	    };
	  },
	  on_context: {},
	  on_mouse_move: {},
	  _initSidebar: function () {
	    this._fullWidth = this.config.width;
	    this.attachEvent("onBeforeOpen", function (id) {
	      if (!this.config.multipleOpen) {
	        var open = this.getOpenItems();

	        for (var i = 0; i < open.length; i++) {
	          if (this.getParentId(id) == this.getParentId(open[i])) this.close(open[i]);
	        }
	      }

	      return !this.config.collapsed;
	    });
	    this.attachEvent("onItemClick", function (id, ev, node) {
	      if (this.getPopup() && !this.getPopup().config.hidden) ev.showpopup = true;
	      if (env.touch) this._showPopup(id, node);
	    });
	    this.attachEvent("onBeforeSelect", function (id) {
	      if (!this.getItem(id).$count) {
	        this.clearCss("webix_sidebar_selected");
	        return true;
	      }

	      return false;
	    });
	    this.attachEvent("onAfterSelect", function (id) {
	      this._markMenu(this, id, !$active.sidebarMarkAll);

	      this.getPopup()._onMasterSelect(id);
	    });
	    this.attachEvent("onMouseMove", function (id, ev, node) {
	      this._showPopup(id, node);
	    });
	    if (this.config.collapsed) this.collapse();
	  },
	  _showPopup: function (id, node) {
	    if (this.config.collapsed) {
	      var popup = this.getPopup();

	      if (popup) {
	        this._updateTitle(id);

	        this._updateList(id);

	        popup.show(node, {
	          x: this.config.position == "left" ? this.config.collapsedWidth : -popup.config.width,
	          y: -1
	        });
	      }
	    }
	  },
	  _updateTitle: function (id) {
	    var popup = this.getPopup();
	    var title = popup.getBody().getChildViews()[0];
	    if (!title || title.masterId == id) return;
	    var selectedId = this.getSelectedId();
	    title.masterId = id;
	    title.parse(this.getItem(id));

	    if (selectedId && this.getParentId(selectedId) == id) {
	      addCss(title.$view, "webix_sidebar_selected", true);
	    } else {
	      removeCss(title.$view, "webix_sidebar_selected");
	    }

	    if (selectedId == id) {
	      addCss(title.$view, "webix_selected", true);
	    } else {
	      removeCss(title.$view, "webix_selected");
	    }
	  },
	  _updateList: function (id) {
	    var popup = this.getPopup();
	    var list = popup.getBody().getChildViews()[1];
	    if (!list || list.masterId == id) return;
	    if (this.exists(list.masterId) && this.getItem(list.masterId).menu) this.updateItem(list.masterId, {
	      menu: list.data.serialize()
	    });
	    list.clearCss("webix_sidebar_selected");
	    list.masterId = id;
	    var selectedId = this.getSelectedId();
	    var data = copy(this.getItem(id).menu || []);
	    list.unselect();

	    if (data.length) {
	      list.show();
	      list.data.importData(data);
	      if (list.exists(selectedId)) list.select(selectedId);else if (selectedId) this._markMenu(list, selectedId);
	    } else {
	      list.hide();
	      list.data.clearAll();
	    }
	  },
	  _initContextMenu: function () {
	    var master = this,
	        config = master.config,
	        popup;

	    if (config.popup) {
	      popup = $$(config.popup);
	    }

	    if (!popup) {
	      var dirClassName = config.position == "left" ? "webix_sidebar_popup_left" : "webix_sidebar_popup_right";
	      var subMenuPos = config.position == "left" ? "right" : "left";

	      var menuTemplate = function (obj) {
	        var icon = "wxi-angle-" + (config.position == "left" ? "right" : "left");
	        var arrow = obj.submenu || obj.data || obj.item ? "<div class=\"webix_icon " + icon + "\"></div>" : "";
	        return arrow + obj.value;
	      };

	      var popupConfig = {
	        view: "popup",
	        css: "webix_sidebar_popup " + dirClassName + " " + config.css,
	        autofit: false,
	        width: this._fullWidth - this.config.collapsedWidth,
	        borderless: true,
	        padding: 0,
	        body: {
	          rows: [{
	            view: "template",
	            borderless: true,
	            css: "webix_sidebar_popup_title",
	            template: "#value#",
	            height: this.config.titleHeight + 2,
	            onClick: {
	              webix_template: function () {
	                var id = this.masterId;
	                if (!master.getItem(id).$count) master.select(id);
	              }
	            }
	          }, {
	            view: "menu",
	            submenu: "data",
	            layout: "y",
	            subMenuPos: subMenuPos,
	            select: true,
	            borderless: true,
	            autoheight: true,
	            css: "webix_sidebar_popup_list " + dirClassName + " " + config.css,
	            template: menuTemplate,
	            type: {
	              subsign: false
	            },
	            submenuConfig: {
	              padding: 0,
	              subMenuPos: subMenuPos,
	              template: menuTemplate,
	              select: true,
	              type: {
	                subsign: false
	              },
	              css: "webix_sidebar_popup_list " + dirClassName + " " + config.css,
	              on: {
	                onShow: function () {
	                  this.clearCss("webix_sidebar_selected");
	                  this.unselectAll();
	                  var sel = master.getSelectedId();
	                  if (sel && this.exists(sel)) this.select(sel);else if (sel) master._markMenu(this, sel);
	                },
	                onBeforeSelect: function (id) {
	                  if (this.getSubMenu(id)) return false;
	                },
	                onAfterSelect: function (id) {
	                  var menu = master.getPopup().queryView({
	                    view: "menu"
	                  });
	                  var parent = master.getParentId(id);

	                  while (parent) {
	                    var sub = menu.getMenu(parent);

	                    if (sub) {
	                      sub.unselectAll();

	                      master._markMenu(sub, id);
	                    }

	                    parent = master.getParentId(parent);
	                  }

	                  master._markMenu(this, id);
	                }
	              }
	            },
	            on: {
	              onBeforeSelect: function (id) {
	                if (this.getSubMenu(id)) return false;
	              },
	              onMenuItemClick: function (id) {
	                if (!this.getSubMenu(id)) master.select(id);
	              }
	            }
	          }]
	        }
	      };
	      exports.extend(popupConfig, config.popup || {}, true);
	      popup = ui(popupConfig);

	      popup._onMasterSelect = function (id) {
	        if (master && master.getParentId(id) == this.masterId) {
	          addCss(this.$view, "webix_sidebar_selected", true);
	        }

	        if (master.config.collapsed && master.getItem(id).$level == 1) {
	          var title = popup.getBody().getChildViews()[0];
	          if (title) addCss(title.$view, "webix_selected", true);
	        }
	      };

	      popup.queryView({
	        view: "menu"
	      })._show_child_on_click = true;
	    }

	    popup.attachEvent("onBeforeShow", function () {
	      return config.collapsed;
	    });
	    this._destroy_with_me = [popup];
	    config.popupId = popup.config.id;

	    _event(document.body, "mousemove", function (e) {
	      var trg = e.target || e.srcElement;

	      if (!popup.config.hidden && !popup.$view.contains(trg) && !this.$view.firstChild.contains(trg) && !popup.queryView({
	        view: "menu"
	      })._open_sub_menu) {
	        popup.hide();
	      }
	    }, {
	      bind: this
	    });
	  },
	  _markMenu: function (view, sel, topOnly) {
	    var css = "webix_sidebar_selected";
	    view.data.each(function (obj) {
	      if (this._isChild(sel, obj.id) && (!topOnly || this.getParentId(obj.id) == "0")) view.addCss(obj.id, css);else if (view.hasCss(obj.id, css)) view.removeCss(obj.id, css);
	    }, this);
	  },
	  _isChild: function (cid, pid) {
	    var parent = this.getParentId(cid);
	    if (pid == parent) return true;
	    if (parent) return this._isChild(parent, pid);
	    return false;
	  },
	  getPopup: function () {
	    return $$(this.config.popupId);
	  },
	  position_setter: function (value) {
	    var newPos = value;
	    var oldPos = value == "left" ? "right" : "left";
	    removeCss(this.$view, "webix_sidebar_" + oldPos);
	    addCss(this.$view, "webix_sidebar_" + newPos, true);
	    var popup = this.getPopup();

	    if (popup) {
	      var popupEl = popup.$view;
	      removeCss(popupEl, "webix_sidebar_popup_" + oldPos);
	      addCss(popupEl, "webix_sidebar_popup_" + newPos, true);
	    }

	    return value;
	  },
	  collapse: function () {
	    this.define("collapsed", true);
	  },
	  expand: function () {
	    this.define("collapsed", false);
	  },
	  toggle: function () {
	    var collapsed = !this.config.collapsed;
	    this.define("collapsed", collapsed);
	  },
	  collapsed_setter: function (value) {
	    var width;

	    if (!value) {
	      width = this._fullWidth;
	      this.type.collapsed = false;
	      addCss(this.$view, "webix_sidebar_expanded", true);
	    } else {
	      width = this.config.collapsedWidth;
	      this.closeAll();
	      this.type.collapsed = true;
	      removeCss(this.$view, "webix_sidebar_expanded");
	    }

	    this.define("width", width);
	    this.resize();
	    return value;
	  },
	  getState: function () {
	    var state = {
	      collapsed: this.config.collapsed
	    };
	    exports.extend(state, TreeAPI.getState.call(this));
	    return state;
	  },
	  setState: function (state) {
	    TreeAPI.setState.call(this, state);
	    this.define("collapsed", state.collapsed);
	  }
	};
	type(tree.view, {
	  name: "sideBar",
	  height: "auto",
	  template: function (obj, common) {
	    if (common.collapsed) return common.icon(obj, common);
	    return common.arrow(obj, common) + common.icon(obj, common) + "<span>" + obj.value + "</span>";
	  },
	  arrow: function (obj) {
	    var html = "";

	    for (var i = 1; i <= obj.$level; i++) {
	      if (i == obj.$level && obj.$count) {
	        var icon = "wxi-angle-" + (obj.open ? "down" : "left");
	        var className = "webix_sidebar_dir_icon webix_icon " + icon;
	        html += "<span class='" + className + "'></span>";
	      }
	    }

	    return html;
	  },
	  icon: function (obj) {
	    var style = "";

	    if (obj.$level > 2) {
	      style = "style=\"padding-left:" + 40 * (obj.$level - 2) + "px\"";
	    }

	    if (obj.icon) return "<span class='webix_icon webix_sidebar_icon " + obj.icon + "' " + style + "></span>";
	    return "<span " + style + "></span>";
	  }
	});
	var view$19 = exports.protoUI(api$19, tree.view);

	var api$1a = {
	  name: "context"
	};
	var view$1a = exports.protoUI(api$1a, ContextHelper, popup.view);

	var api$1b = {
	  name: "contextmenu",
	  _hide_on_item_click: true,
	  $init: function (config) {
	    if (config.submenuConfig) exports.extend(config, config.submenuConfig);
	  }
	};
	var view$1b = exports.protoUI(api$1b, ContextHelper, submenu.view);

	var api$1c = {
	  name: "vscroll",
	  $apiOnly: true,
	  defaults: {
	    scroll: "x",
	    scrollStep: 40,
	    scrollPos: 0,
	    scrollSize: 18,
	    scrollVisible: 1,
	    zoom: 1
	  },
	  $init: function (config) {
	    var dir = config.scroll || "x";
	    var node = this._viewobj = toNode(config.container);
	    node.className += " webix_vscroll_" + dir;
	    node.innerHTML = "<div class='webix_vscroll_body'></div>";

	    _event(node, "scroll", this._onscroll, {
	      bind: this
	    });

	    this._last_set_size = 0;
	  },
	  reset: function () {
	    this.config.scrollPos = 0;
	    this._viewobj[this.config.scroll == "x" ? "scrollLeft" : "scrollTop"] = 0;
	  },
	  _check_quantum: function (value) {
	    if (value > 1500000) {
	      this._settings.zoom = value / 1000000;
	      value = 1000000;
	    } else {
	      this._settings.zoom = 1;
	    }

	    return value;
	  },
	  scrollWidth_setter: function (value) {
	    value = this._check_quantum(value);
	    this._viewobj.firstChild.style.width = value + "px";
	    return value;
	  },
	  scrollHeight_setter: function (value) {
	    value = this._check_quantum(value);
	    this._viewobj.firstChild.style.height = value + "px";
	    return value;
	  },
	  sizeTo: function (value, top, bottom) {
	    value = value - (top || 0) - (bottom || 0);
	    var width = this._settings.scrollSize; //IEFix
	    //IE doesn't react on scroll-click if it has not at least 1 px of visible content

	    if (env.isIE && width) width += 1;

	    if (!width && this._settings.scrollVisible && !env.$customScroll) {
	      this._viewobj.style.pointerEvents = "none";
	      width = 14;
	    }

	    if (!width) {
	      this._viewobj.style.display = "none";
	    } else {
	      this._viewobj.style.display = "block";
	      if (top) this._viewobj.style.marginTop = top + "px";
	      this._viewobj.style[this._settings.scroll == "x" ? "width" : "height"] = Math.max(0, value) + "px";
	      this._viewobj.style[this._settings.scroll == "x" ? "height" : "width"] = width + "px";
	    }

	    this._last_set_size = value;
	  },
	  getScroll: function () {
	    return Math.round(this._settings.scrollPos * this._settings.zoom);
	  },
	  getSize: function () {
	    return Math.round((this._settings.scrollWidth || this._settings.scrollHeight) * this._settings.zoom);
	  },
	  scrollTo: function (value) {
	    if (value < 0) value = 0;
	    var config = this._settings;
	    value = value / config.zoom; //safety check for negative values

	    if (value < 0) value = 0; //apply new position

	    if (value != this._settings.scrollPos) {
	      this._viewobj[config.scroll == "x" ? "scrollLeft" : "scrollTop"] = value;

	      this._onscroll_inner(value, true);

	      return true;
	    }
	  },
	  _onscroll: function () {
	    var x = this._viewobj[this._settings.scroll == "x" ? "scrollLeft" : "scrollTop"];
	    if (Math.floor(x) != Math.floor(this._settings.scrollPos)) this._onscroll_inner(x, false);
	  },
	  _onscroll_inner: function (value, api) {
	    //size of scroll area
	    var height = this._settings.scrollWidth || this._settings.scrollHeight; //if we scrolled to the end of the area

	    if (value >= height - this._last_set_size / (api ? this._settings.zoom : 1)) {
	      //set value so the last row is visible
	      value = Math.max(0, height - this._last_set_size / this._settings.zoom);
	    }

	    this._settings.scrollPos = value || 0;
	    this.callEvent("onScroll", [this.getScroll()]);
	  },
	  activeArea: function (area, x_mode) {
	    this._x_scroll_mode = x_mode;

	    _event(area, env.isIE8 ? "mousewheel" : "wheel", this._on_wheel, {
	      bind: this,
	      passive: false
	    });

	    this._add_touch_events(area);
	  },
	  _add_touch_events: function (area) {
	    if (!env.touch && window.navigator.pointerEnabled) {
	      addCss(area, "webix_scroll_touch_ie", true);

	      _event(area, "pointerdown", function (e) {
	        if (e.pointerType == "touch" || e.pointerType == "pen") {
	          this._start_context = Touch._get_context_m(e);
	          this._start_scroll_pos = this.getScroll();
	        }
	      }, {
	        bind: this
	      });

	      event$1(document.body, "pointermove", function (e) {
	        var scroll;

	        if (this._start_context) {
	          this._current_context = Touch._get_context_m(e);

	          if (this._settings.scroll == "x") {
	            scroll = this._current_context.x - this._start_context.x;
	          } else if (this._settings.scroll == "y") {
	            scroll = this._current_context.y - this._start_context.y;
	          }

	          if (scroll && Math.abs(scroll) > 5) {
	            this.scrollTo(this._start_scroll_pos - scroll);
	          }
	        }
	      }, {
	        bind: this
	      });
	      event$1(window, "pointerup", function () {
	        if (this._start_context) {
	          this._start_context = this._current_context = null;
	        }
	      }, {
	        bind: this
	      });
	    }
	  },
	  _on_wheel: function (e) {
	    var dir = 0;
	    var step = e.deltaMode === 0 ? 30 : 1;
	    if (env.isIE8) dir = e.detail = -e.wheelDelta / 30;

	    if (e.deltaX && Math.abs(e.deltaX) > Math.abs(e.deltaY)) {
	      //x-scroll
	      if (this._x_scroll_mode && this._settings.scrollVisible) dir = e.deltaX / step;
	    } else {
	      //y-scroll
	      if (!this._x_scroll_mode && this._settings.scrollVisible) {
	        if (isUndefined(e.deltaY)) dir = e.detail;else dir = e.deltaY / step;
	      }
	    } // Safari requires target preserving
	    // (used in _check_rendered_cols of DataTable)


	    if (env.isSafari) this._scroll_trg = e.target || e.srcElement;
	    if (dir) if (this.scrollTo(this.getScroll() + dir * this._settings.scrollStep)) return preventEvent(e);
	  }
	};
	var view$1c = exports.protoUI(api$1c, EventSystem, Settings);

	var temp$3 = {};

	var Mixin = {
	  filterByAll: function () {
	    //we need to use dynamic function creating
	    var server = false;
	    this.data.silent(function () {
	      this.filter();
	      var first = false;

	      for (var key in this._filter_elements) {
	        assert(key, "empty column id for column with filtering");
	        if (!this.isColumnVisible(key)) continue;
	        var record = this._filter_elements[key];
	        var originvalue = record[2].getValue(record[0]); //saving last filter value, for usage in getState

	        var inputvalue = originvalue;
	        if (record[1].prepare) inputvalue = record[1].prepare.call(record[2], inputvalue, record[1], this); //preserve original value

	        record[1].value = originvalue;
	        var compare = record[1].compare;
	        if (!this.callEvent("onBeforeFilter", [key, inputvalue, record[1]])) continue;

	        if (record[2].$server || server) {
	          //if one of filters is server side, do not run any client side filters
	          server = true;
	        } else {
	          if (inputvalue === "") continue;

	          if (compare) {
	            compare = this._multi_compare(key, compare);
	            this.filter(bind(function (obj, value) {
	              if (!obj) return false;
	              return compare(obj[key], value, obj);
	            }, this), inputvalue, first);
	          } else this.filter(key, inputvalue, first);

	          first = true;
	        }
	      }

	      if (server) this._runServerFilter();
	    }, this);

	    if (!server) {
	      this.refresh();
	      this.callEvent("onAfterFilter", []);
	    }
	  },
	  _multi_compare: function (key, compare) {
	    var column = this.getColumnConfig(key);
	    var separator = column ? column.optionslist : null; //default mode

	    if (!separator) return compare;
	    if (typeof separator != "string") separator = ",";
	    return function (itemValue, inputValue, obj) {
	      if (!itemValue) return true;
	      var ids = itemValue.split(separator);

	      for (var i = 0; i < ids.length; i++) {
	        if (compare(ids[i], inputValue, obj)) return true;
	      }
	    };
	  },
	  filterMode_setter: function (mode) {
	    return exports.extend(this.data._filterMode, mode, true);
	  },
	  getFilter: function (columnId) {
	    var filter = this._filter_elements[columnId];
	    if (filter && filter[2].getInputNode) return filter[2].getInputNode(filter[0]);
	    return null;
	  },
	  registerFilter: function (node, config, obj) {
	    this._filter_elements[config.columnId] = [node, config, obj];
	  },
	  collectValues: function (id, mode) {
	    var values;
	    var obj = this.getColumnConfig(id);
	    var options = mode && mode.visible ? null : obj.collection;
	    if (options) values = this._collectValues.call(options, "id", "value");else values = this._collectValues(obj.id, obj.id);
	    var result = {
	      values: values
	    };
	    this.callEvent("onCollectValues", [id, result]);
	    return result.values;
	  },
	  _collectValues: function (id, value) {
	    var checks = {
	      "": true
	    };
	    var values = [];
	    this.data.each(function (obj) {
	      var test = obj ? obj[id] : "";

	      if (test !== undefined && !checks[test]) {
	        checks[test] = true;
	        var lineId = obj[id]; //special handling for 0 values
	        //convert to string to create a valid ID

	        if (lineId === 0) lineId = "0";
	        values.push({
	          id: lineId,
	          value: obj[value]
	        });
	      }
	    }, this, true);

	    if (values.length) {
	      var type = typeof values[0].value === "string" ? "string" : "raw";
	      values.sort(this.data.sorting.create({
	        as: type,
	        by: "value",
	        dir: "asc"
	      }));
	    }

	    return values;
	  },
	  _runServerFilter: function () {
	    var _this = this;

	    this.loadNext(0, 0, 0, 0, 1).then(function (data) {
	      if (_this.editStop) _this.editStop();

	      _this.clearAll(true);

	      _this.parse(data);

	      _this.callEvent("onAfterFilter", []);
	    });
	  }
	};

	var Mixin$1 = {
	  hover_setter: function (value) {
	    if (value && !this._hover_initialized) {
	      this._enable_mouse_move();

	      this.config.experimental = true;
	      this.attachEvent("onMouseMoving", function () {
	        var row = this.locate(arguments[0]);
	        row = row ? row.row : null;

	        if (this._last_hover != row) {
	          if (this._last_hover) this.removeRowCss(this._last_hover, this._settings.hover);
	          this._last_hover = row;
	          if (this._last_hover) this.addRowCss(this._last_hover, this._settings.hover);
	        }
	      });
	      event$1(this.$view, "mouseout", bind(function (e) {
	        if (this._last_hover && document.body.contains(e.target)) {
	          this.removeRowCss(this._last_hover, this._settings.hover);
	          this._last_hover = null;
	        }
	      }, this));
	      this._hover_initialized = 1;
	    }

	    return value;
	  },
	  select_setter: function (value) {
	    if (!this.select && value) {
	      exports.extend(this, this._selections._commonselect, true);
	      if (value === true) value = "row";else if (value == "multiselect") {
	        value = "row";
	        this._settings.multiselect = true;
	      }
	      assert(this._selections[value], "Unknown selection mode: " + value);
	      exports.extend(this, this._selections[value], true);
	    }

	    return value;
	  },
	  getSelectedId: function (mode) {
	    return mode ? [] : ""; //dummy placeholder
	  },
	  getSelectedItem: function (mode) {
	    return SelectionModel.getSelectedItem.call(this, mode);
	  },
	  _selections: {
	    //shared methods for all selection models
	    _commonselect: {
	      _select_css: " webix_cell_select",
	      $init: function () {
	        this._reinit_selection();

	        this.on_click.webix_cell = bind(this._click_before_select, this); //temporary stab, actual handlers need to be created

	        this._data_cleared = this._data_filtered = function () {
	          this.unselect();
	        };

	        this.data.attachEvent("onStoreUpdated", bind(this._data_updated, this));
	        this.data.attachEvent("onSyncApply", bind(this._data_synced, this));
	        this.data.attachEvent("onClearAll", bind(this._data_cleared, this));
	        this.data.attachEvent("onAfterFilter", bind(this._data_filtered, this));
	        this.data.attachEvent("onIdChange", bind(this._id_changed, this));
	        this.$ready.push(SelectionModel._set_noselect);
	      },
	      _id_changed: function (oldid, newid) {
	        for (var i = 0; i < this._selected_rows.length; i++) {
	          if (this._selected_rows[i] == oldid) this._selected_rows[i] = newid;
	        }

	        for (var _i = 0; _i < this._selected_areas.length; _i++) {
	          var item = this._selected_areas[_i];

	          if (item.row == oldid) {
	            oldid = this._select_key(item);
	            item.row = newid;
	            newid = this._select_key(item);
	            item.id = newid;
	            delete this._selected_pull[oldid];
	            this._selected_pull[newid] = true;
	          }
	        }
	      },
	      _data_updated: function (id, obj, type) {
	        if (type == "delete") this.unselect(id);
	      },
	      _data_synced: function () {
	        for (var i = this._selected_areas.length - 1; i >= 0; i--) {
	          var row = this._selected_areas[i].row;

	          if (!this.exists(row)) {
	            this._selected_areas.splice(i, 1);

	            delete this._selected_pull[row];
	          }
	        }
	      },
	      _reinit_selection: function () {
	        //list of selected areas
	        this._selected_areas = []; //key-value hash of selected areas, for fast search

	        this._selected_pull = {}; //used to track selected cell objects

	        this._selected_rows = [];
	      },
	      isSelected: function (id, column) {
	        var key;
	        if (!isUndefined(column)) key = this._select_key({
	          row: id,
	          column: column
	        });else key = _typeof(id) === "object" ? this._select_key(id) : id;
	        return this._selected_pull[key];
	      },
	      getSelectedId: function (asArray, plain) {
	        var result; //if multiple selections was created - return array
	        //in case of single selection, return value or array, when asArray parameter provided

	        if (this._selected_areas.length > 1 || asArray) {
	          result = [].concat(this._selected_areas);
	          if (plain) for (var i = 0; i < result.length; i++) {
	            result[i] = result[i].id;
	          }
	        } else {
	          result = this._selected_areas[0];
	          if (plain && result) return result.id;
	        }

	        return result;
	      },
	      _id_to_string: function () {
	        return this.row;
	      },
	      _select: function (data, preserve) {
	        var key = this._select_key(data); //don't allow selection on unnamed columns


	        if (key === null) return;
	        if (preserve === -1) return this._unselect(data);
	        data.id = key;
	        data.toString = this._id_to_string;
	        if (!this.callEvent("onBeforeSelect", [data, preserve])) return false; //ignore area, if it was already selected and
	        // - we are preserving existing selection
	        // - this is the only selected area
	        // otherwise we need to clear other selected areas

	        if (this._selected_pull[key] && (preserve || this._selected_areas.length == 1)) return;
	        if (!preserve) this._clear_selection();

	        this._selected_areas.push(data);

	        this._selected_pull[key] = true;
	        this.callEvent("onAfterSelect", [data, preserve]);

	        this._finalize_select(this._post_select(data));

	        return true;
	      },
	      _clear_selection: function () {
	        if (!this._selected_areas.length) return false;

	        for (var i = 0; i < this._selected_areas.length; i++) {
	          if (!this.callEvent("onBeforeUnSelect", [this._selected_areas[i]])) return false;
	        }

	        for (var _i2 = 0; _i2 < this._selected_rows.length; _i2++) {
	          this.data.removeMark(this._selected_rows[_i2], "webix_selected");
	        }

	        var cols = this._settings.columns;
	        if (cols) for (var _i3 = 0; _i3 < cols.length; _i3++) {
	          cols[_i3].$selected = null;
	        }
	        var data = this._selected_areas;

	        this._reinit_selection();

	        for (var _i4 = 0; _i4 < data.length; _i4++) {
	          this.callEvent("onAfterUnSelect", [data[_i4]]);
	        }

	        return true;
	      },
	      unselectAll: function () {
	        this.clearSelection();
	      },
	      selectAll: function () {
	        this.selectRange();
	      },
	      clearSelection: function () {
	        if (this._clear_selection()) {
	          this.callEvent("onSelectChange", []);
	          this.render();
	        }
	      },
	      _unselect: function (data) {
	        var key = this._select_key(data);

	        if (!key && this._selected_areas.length) {
	          this.clearSelection();
	          this.callEvent("onSelectChange", []);
	        } //ignore area, if it was already selected


	        if (!this._selected_pull[key]) return;
	        if (!this.callEvent("onBeforeUnSelect", [data])) return false;

	        for (var i = 0; i < this._selected_areas.length; i++) {
	          if (this._selected_areas[i].id == key) {
	            this._selected_areas.splice(i, 1);

	            break;
	          }
	        }

	        delete this._selected_pull[key];
	        this.callEvent("onAfterUnSelect", [data]);

	        this._finalize_select(0, this._post_unselect(data));
	      },
	      _add_item_select: function (id) {
	        var item = this.getItem(id);
	        return this.data.addMark(item.id, "webix_selected", 0, {
	          $count: 0
	        }, true);
	      },
	      _finalize_select: function (id) {
	        if (id) this._selected_rows.push(id);

	        if (!this._silent_selection) {
	          this.render();
	          this.callEvent("onSelectChange", []);
	        }
	      },
	      _click_before_select: function (e, id) {
	        var preserve = e.ctrlKey || e.metaKey || this._settings.multiselect == "touch";
	        var range = e.shiftKey;
	        if (!this._settings.multiselect && this._settings.select != "multiselect" && this._settings.select != "area") preserve = range = false;

	        if (range && this._selected_areas.length) {
	          var last = this._selected_areas[this._selected_areas.length - 1];

	          this._selectRange(id, last);
	        } else {
	          if (preserve && this._selected_pull[this._select_key(id)]) this._unselect(id);else this._select({
	            row: id.row,
	            column: id.column
	          }, preserve);
	        }
	      },
	      _mapSelection: function (callback, column, row) {
	        var cols = this._settings.columns; //selected columns only

	        if (column) {
	          var temp = [];

	          for (var i = 0; i < cols.length; i++) {
	            if (cols[i].$selected) temp.push(cols[i]);
	          }

	          cols = temp;
	        }

	        var rows = this.data.order;
	        var row_ind = 0;

	        for (var _i5 = 0; _i5 < rows.length; _i5++) {
	          var item = this.getItem(rows[_i5]);
	          if (!item) continue; //dyn loading, row is not available

	          var selection = this.data.getMark(item.id, "webix_selected");

	          if (selection || column) {
	            var col_ind = 0;

	            for (var j = 0; j < cols.length; j++) {
	              var id = cols[j].id;

	              if (row || column || selection[id]) {
	                if (callback) item[id] = callback(item[id], rows[_i5], id, row_ind, col_ind);else return {
	                  row: rows[_i5],
	                  column: id
	                };
	                col_ind++;
	              }
	            } //use separate row counter, to count only selected rows


	            row_ind++;
	          }
	        }
	      }
	    },
	    row: {
	      _select_css: " webix_row_select",
	      _select_key: function (data) {
	        return data.row;
	      },
	      select: function (row_id, preserve) {
	        //when we are using id from mouse events
	        if (row_id) row_id = row_id.toString();
	        assert(this.data.exists(row_id), "Incorrect id in select command: " + row_id);

	        this._select({
	          row: row_id
	        }, preserve);
	      },
	      _post_select: function (data) {
	        this._add_item_select(data.row).$row = true;
	        return data.row;
	      },
	      unselect: function (row_id) {
	        this._unselect({
	          row: row_id
	        });
	      },
	      _post_unselect: function (data) {
	        this.data.removeMark(data.row, "webix_selected", 0, 1);
	        return data.row;
	      },
	      mapSelection: function (callback) {
	        return this._mapSelection(callback, false, true);
	      },
	      _selectRange: function (a, b) {
	        return this.selectRange(a.row, b.row);
	      },
	      selectRange: function (row_id, end_row_id, preserve) {
	        if (isUndefined(preserve)) preserve = true;
	        var row_start_ind = row_id ? this.getIndexById(row_id) : 0;
	        var row_end_ind = end_row_id ? this.getIndexById(end_row_id) : this.data.order.length - 1;

	        if (row_start_ind > row_end_ind) {
	          var temp = row_start_ind;
	          row_start_ind = row_end_ind;
	          row_end_ind = temp;
	        }

	        this._silent_selection = true;

	        for (var i = row_start_ind; i <= row_end_ind; i++) {
	          var id = this.getIdByIndex(i);

	          if (!id) {
	            if (row_id) this.select(row_id);
	            break;
	          }

	          this.select(id, preserve);
	        }

	        this._silent_selection = false;

	        this._finalize_select();
	      }
	    },
	    cell: {
	      _select_key: function (data) {
	        if (!data.column) return null;
	        return data.row + "_" + data.column;
	      },
	      select: function (row_id, column_id, preserve) {
	        assert(this.data.exists(row_id), "Incorrect id in select command: " + row_id);

	        this._select({
	          row: row_id,
	          column: column_id
	        }, preserve);
	      },
	      _post_select: function (data) {
	        var sel = this._add_item_select(data.row);

	        sel.$count++;
	        sel[data.column] = true;
	        return data.row;
	      },
	      unselect: function (row_id, column_id) {
	        this._unselect({
	          row: row_id,
	          column: column_id
	        });
	      },
	      _post_unselect: function (data) {
	        var sel = this._add_item_select(data.row);

	        sel.$count--;
	        sel[data.column] = false;
	        if (sel.$count <= 0) this.data.removeMark(data.row, "webix_selected");
	        return data.row;
	      },
	      mapSelection: function (callback) {
	        return this._mapSelection(callback, false, false);
	      },
	      _selectRange: function (a, b) {
	        return this.selectRange(a.row, a.column, b.row, b.column);
	      },
	      selectRange: function (row_id, column_id, end_row_id, end_column_id, preserve) {
	        if (isUndefined(preserve)) preserve = true;
	        var row_start_ind = row_id ? this.getIndexById(row_id) : 0;
	        var row_end_ind = end_row_id ? this.getIndexById(end_row_id) : this.data.order.length - 1;
	        var col_start_ind = column_id ? this.getColumnIndex(column_id) : 0;
	        var col_end_ind = end_column_id ? this.getColumnIndex(end_column_id) : this._columns.length - 1;

	        if (row_start_ind > row_end_ind) {
	          var temp = row_start_ind;
	          row_start_ind = row_end_ind;
	          row_end_ind = temp;
	        }

	        if (col_start_ind > col_end_ind) {
	          var _temp = col_start_ind;
	          col_start_ind = col_end_ind;
	          col_end_ind = _temp;
	        }

	        this._silent_selection = true;

	        for (var i = row_start_ind; i <= row_end_ind; i++) {
	          for (var j = col_start_ind; j <= col_end_ind; j++) {
	            this.select(this.getIdByIndex(i), this.columnId(j), preserve);
	          }
	        }

	        this._silent_selection = false;

	        this._finalize_select();
	      }
	    },
	    column: {
	      _select_css: " webix_column_select",
	      _select_key: function (data) {
	        return data.column;
	      },
	      _id_to_string: function () {
	        return this.column;
	      },
	      //returns box-like area, with ordered selection cells
	      select: function (column_id, preserve) {
	        this._select({
	          column: column_id
	        }, preserve);
	      },
	      _post_select: function (data) {
	        this._settings.columns[this.getColumnIndex(data.column)].$selected = true;
	        if (!this._silent_selection) this._render_header_and_footer();
	      },
	      unselect: function (column_id) {
	        this._unselect({
	          column: column_id
	        });
	      },
	      _post_unselect: function (data) {
	        this._settings.columns[this.getColumnIndex(data.column)].$selected = null;

	        this._render_header_and_footer();
	      },
	      mapSelection: function (callback) {
	        return this._mapSelection(callback, true, false);
	      },
	      _selectRange: function (a, b) {
	        return this.selectRange(a.column, b.column);
	      },
	      selectRange: function (column_id, end_column_id, preserve) {
	        if (isUndefined(preserve)) preserve = true;
	        var column_start_ind = column_id ? this.getColumnIndex(column_id) : 0;
	        var column_end_ind = end_column_id ? this.getColumnIndex(end_column_id) : this._columns.length - 1;

	        if (column_start_ind > column_end_ind) {
	          var temp = column_start_ind;
	          column_start_ind = column_end_ind;
	          column_end_ind = temp;
	        }

	        this._silent_selection = true;

	        for (var i = column_start_ind; i <= column_end_ind; i++) {
	          this.select(this.columnId(i), preserve);
	        }

	        this._silent_selection = false;

	        this._render_header_and_footer();

	        this._finalize_select();
	      },
	      _data_synced: function () {//do nothing, as columns are not changed
	      }
	    },
	    area: {
	      _select_key: function (data) {
	        return data.row + "_" + data.column;
	      },
	      getSelectedId: function (asArray) {
	        var area = this.getSelectArea();
	        var result = [];

	        if (area) {
	          if (asArray && (area.start.row != area.end.row || area.start.column != area.end.column)) {
	            var row_start_ind = this.getIndexById(area.start.row);
	            var row_end_ind = this.getIndexById(area.end.row); //filtering in process

	            if (row_start_ind == -1 || row_end_ind == -1) return result;
	            var col_start_ind = this.getColumnIndex(area.start.column);
	            var col_end_ind = this.getColumnIndex(area.end.column);

	            for (var i = row_start_ind; i <= row_end_ind; i++) {
	              for (var j = col_start_ind; j <= col_end_ind; j++) {
	                result.push({
	                  row: this.getIdByIndex(i),
	                  column: this.columnId(j)
	                });
	              }
	            }
	          } else {
	            result.push(area.end);
	          }
	        }

	        return asArray ? result : result[0];
	      },
	      unselect: function () {
	        this._unselect();
	      },
	      _unselect: function () {
	        this.removeSelectArea();
	        this.callEvent("onSelectChange", []);
	      },
	      mapSelection: function (callback) {
	        var select = this.getSelectArea();

	        if (select) {
	          var sind = this.getColumnIndex(select.start.column);
	          var eind = this.getColumnIndex(select.end.column);
	          var srow = this.getIndexById(select.start.row);
	          var erow = this.getIndexById(select.end.row);

	          for (var i = srow; i <= erow; i++) {
	            var rid = this.data.order[i];
	            var item = this.getItem(rid);

	            for (var j = sind; j <= eind; j++) {
	              var cid = this._columns[j].id;
	              if (callback) item[cid] = callback(item[cid], rid, cid, i - srow, j - sind);else return {
	                row: rid,
	                column: cid
	              };
	            }
	          }
	        }
	      },
	      select: function (row_id, column_id, preserve) {
	        assert(this.data.exists(row_id), "Incorrect id in select command: " + row_id);

	        this._select({
	          row: row_id,
	          column: column_id
	        }, preserve);
	      },
	      _selectRange: function (id, last) {
	        this._extendAreaRange(id, last);
	      },
	      _select: function (cell) {
	        //ctrl-selection is not supported yet, so ignoring the preserve flag
	        this.addSelectArea(cell, cell, false);
	        return true;
	      },
	      _data_synced: function () {
	        if (this._selected_areas.length) this.refreshSelectArea();
	      }
	    }
	  }
	};

	var Mixin$2 = {
	  blockselect_setter: function (value) {
	    if (value && this._block_sel_flag) {
	      _event(this._viewobj, env.mouse.move, this._bs_move, {
	        bind: this
	      });

	      _event(this._viewobj, env.mouse.down, this._bs_down, {
	        bind: this
	      });

	      event$1(document.body, env.mouse.up, this._bs_up, {
	        bind: this
	      });
	      this._block_sel_flag = this._bs_ready = this._bs_progress = false;
	      this.attachEvent("onAfterScroll", function () {
	        this._update_block_selection();
	      }); // auto scroll

	      exports.extend(this, AutoScroll, true);
	      this.attachEvent("onBeforeAutoScroll", function () {
	        return this._bs_progress;
	      });
	    }

	    return value;
	  },
	  _block_sel_flag: true,
	  _childOf: function (e, tag) {
	    var src = e.target || e.srcElement;

	    while (src) {
	      if (src.getAttribute && src.getAttribute(
	      /*@attr*/
	      "webixignore")) return false;
	      if (src == tag) return true;
	      src = src.parentNode;
	    }

	    return false;
	  },
	  _bs_down: function (e) {
	    // do not listen to mousedown of subview on master
	    if (this._settings.subview && this != $$(e.target || e.srcElement)) return;

	    if (this._childOf(e, this._body)) {
	      //disable block selection when we have an active editor
	      if (e.target && e.target.tagName == "INPUT" || this._rs_process) return;
	      this._bs_position = offset(this._body);
	      var pos$$1 = pos(e);
	      this._bs_ready = [pos$$1.x - this._bs_position.x, pos$$1.y - this._bs_position.y];
	      preventEvent(e);
	    }
	  },
	  _bs_up: function (e) {
	    if (this._block_panel) {
	      this._bs_select("select", true, e);

	      this._block_panel = remove(this._block_panel);
	    }

	    removeCss(document.body, "webix_noselect");
	    this._bs_ready = this._bs_progress = false;
	    if (this._auto_scroll_delay) this._auto_scroll_delay = window.clearTimeout(this._auto_scroll_delay);
	  },
	  _update_block_selection: function () {
	    if (this._bs_progress) this._bs_select(false, false);
	  },
	  _bs_select: function (mode, theend, e) {
	    var start = null;
	    if (!this._bs_ready[2]) this._bs_ready[2] = this._locate_cell_xy.apply(this, this._bs_ready);
	    start = this._bs_ready[2];

	    var end = this._locate_cell_xy.apply(this, this._bs_progress);

	    if (!this.callEvent("onBeforeBlockSelect", [start, end, theend, e])) return;

	    if ((!this._bs_do_select || this._bs_do_select(start, end, theend, e) !== false) && start.row && end.row) {
	      if (mode === "select") {
	        this._clear_selection();

	        this._selectRange(start, end);
	      } else {
	        var startx, starty, endx, endy;

	        if (mode === "box") {
	          startx = Math.min(this._bs_ready[0], this._bs_progress[0]);
	          endx = Math.max(this._bs_ready[0], this._bs_progress[0]);
	          starty = Math.min(this._bs_ready[1], this._bs_progress[1]);
	          endy = Math.max(this._bs_ready[1], this._bs_progress[1]);
	        } else {
	          var startn = this._cellPosition(start.row, start.column);

	          var endn = this._cellPosition(end.row, end.column);

	          var scroll = this.getScrollState();
	          var startWidth = startn.width;
	          var endWidth = endn.width;

	          if (this._right_width && this._bs_ready[0] > this._left_width + this._center_width) {
	            startn.left += this._left_width + this._center_width;
	          } else if (this._left_width) {
	            if (this._bs_ready[0] > this._left_width) {
	              if (startn.left < scroll.x) {
	                startWidth -= scroll.x - startn.left;
	                startn.left = this._left_width;
	              } else startn.left += this._left_width - scroll.x;
	            }
	          } else startn.left -= scroll.x;

	          if (this._right_width && this._bs_progress[0] > this._left_width + this._center_width) {
	            endn.left += this._left_width + this._center_width;
	          } else if (this._left_width) {
	            if (this._bs_progress[0] > this._left_width) {
	              if (endn.left < scroll.x) {
	                endWidth -= scroll.x - endn.left;
	                endn.left = this._left_width;
	              } else endn.left += this._left_width - scroll.x;
	            }
	          } else endn.left -= scroll.x;

	          if (this._settings.prerender) {
	            startn.top -= this._scrollTop;
	            endn.top -= this._scrollTop;
	          }

	          startx = Math.min(startn.left, endn.left);
	          endx = Math.max(startn.left + startWidth, endn.left + endWidth);
	          starty = Math.min(startn.top, endn.top);
	          endy = Math.max(startn.top + startn.height, endn.top + endn.height);
	          if (this._settings.topSplit) starty += this._getTopSplitOffset(start);
	          if (this._auto_scroll_delay) this._auto_scroll_delay = window.clearTimeout(this._auto_scroll_delay);
	          if (e) this._auto_scroll_delay = delay(this._auto_scroll, this, [pos(e)], 250);
	        }

	        var style = this._block_panel.style;
	        style.left = startx + "px";
	        style.top = starty + "px";
	        style.width = endx - startx + "px";
	        style.height = endy - starty + "px";
	      }
	    }

	    if (theend) this.callEvent("onAfterBlockSelect", [start, end]);
	  },
	  _bs_start: function () {
	    this._block_panel = create("div", {
	      "class": "webix_block_selection"
	    }, "");

	    this._body.appendChild(this._block_panel);
	  },
	  _bs_move: function (e) {
	    if (this._bs_ready !== false) {
	      if (!this._bs_progress) addCss(document.body, "webix_noselect");
	      var pos$$1 = pos(e);
	      var progress = [pos$$1.x - this._bs_position.x, pos$$1.y - this._bs_position.y]; //prevent unnecessary block selection while dbl-clicking

	      if (Math.abs(this._bs_ready[0] - progress[0]) < 5 && Math.abs(this._bs_ready[1] - progress[1]) < 5) return;
	      if (this._bs_progress === false) this._bs_start(e);
	      this._bs_progress = progress;

	      this._bs_select(this.config.blockselect, false, e);
	    }
	  },
	  _locate_cell_xy: function (x, y) {
	    var inTopSplit = false,
	        row = null,
	        column = null;
	    if (this._right_width && x > this._left_width + this._center_width) x += this._x_scroll.getSize() - this._center_width - this._left_width - this._right_width;else if (!this._left_width || x > this._left_width) x += this._x_scroll.getScroll();

	    if (this._settings.topSplit && this._render_scroll_top > this._settings.topSplit) {
	      var splitPos = this._cellPosition(this.getIdByIndex(this._settings.topSplit - 1), this.columnId(0));

	      if (splitPos.top + splitPos.height > y) {
	        inTopSplit = true;
	      }
	    }

	    if (!inTopSplit) y += this.getScrollState().y;
	    if (x < 0) x = 0;
	    if (y < 0) y = 0;
	    var cols = this._settings.columns;
	    var rows = this.data.order;
	    var summ = 0;

	    for (var i = 0; i < cols.length; i++) {
	      summ += cols[i].width;

	      if (summ >= x) {
	        column = cols[i].id;
	        break;
	      }
	    }

	    if (!column) column = cols[cols.length - 1].id;
	    summ = 0;
	    var start = this.data.$min || 0;

	    if (this._settings.fixedRowHeight) {
	      row = rows[start + Math.floor(y / this._settings.rowHeight)];
	    } else for (var _i = start; _i < rows.length; _i++) {
	      summ += this._getHeightByIndex(_i);

	      if (summ >= y) {
	        row = rows[_i];
	        break;
	      }
	    }

	    if (!row) row = rows[rows.length - 1];
	    return {
	      row: row,
	      column: column
	    };
	  },
	  _getTopSplitOffset: function (cell, area) {
	    var y = 0,
	        startIndex = this.getIndexById(cell.row);

	    if (startIndex >= this._settings.topSplit) {
	      var startPos = this._cellPosition(this.getIdByIndex(startIndex), cell.column);

	      var splitPos = this._cellPosition(this.getIdByIndex(this._settings.topSplit - 1), cell.column);

	      if (splitPos.top + splitPos.height - startPos.top > 0) {
	        y = splitPos.top + splitPos.height - (startPos.top > 0 || !area ? startPos.top : 0);
	      }
	    }

	    return y;
	  }
	};

	var Mixin$3 = {
	  resizeRow_setter: function (value) {
	    this._settings.scrollAlignY = false;
	    this._settings.fixedRowHeight = false;
	    return this.resizeColumn_setter(value);
	  },
	  resizeColumn_setter: function (value) {
	    if (value && this._rs_init_flag) {
	      _event(this._viewobj, "mousemove", this._rs_move, {
	        bind: this
	      });

	      _event(this._viewobj, "mousedown", this._rs_down, {
	        bind: this
	      });

	      _event(this._viewobj, "mouseup", this._rs_up, {
	        bind: this
	      });

	      this._rs_init_flag = false;
	    }

	    return value;
	  },
	  _rs_init_flag: true,
	  _rs_down: function (e) {
	    // do not listen to mousedown of subview on master
	    if (this._settings.subview && this != $$(e.target || e.srcElement)) return; //if mouse was near border

	    if (!this._rs_ready) return;
	    this._rs_process = [pos(e), this._rs_ready[2]];
	    addCss(document.body, "webix_noselect");
	    denySelect();
	  },
	  _rs_up: function () {
	    this._rs_process = false;
	    removeCss(document.body, "webix_noselect");
	    allowSelect();
	  },
	  _rs_start: function () {
	    if (this._rs_progress) return;
	    var dir = this._rs_ready[0];
	    var node = this._rs_process[1];

	    var obj = this._locate(node);

	    if (!obj) return;
	    var eventPos = this._rs_process[0];
	    var start;

	    if (dir == "x") {
	      start = offset(node).x + this._rs_ready[1] - offset(this._body).x;
	      eventPos = eventPos.x;
	      if (!this._rs_ready[1]) obj.cind -= node.parentNode.colSpan || 1;
	    } else {
	      start = offset(node).y + this._rs_ready[1] - offset(this._body).y + this._header_height;
	      eventPos = eventPos.y;
	      if (!this._rs_ready[1]) obj.rind--;
	    }

	    if (obj.cind >= 0 && obj.rind >= 0) {
	      this._rs_progress = [dir, obj, start];
	      var resize = new ui.resizearea({
	        container: this._viewobj,
	        dir: dir,
	        eventPos: eventPos,
	        start: start,
	        cursor: (dir == "x" ? "col" : "row") + "-resize"
	      });
	      resize.attachEvent("onResizeEnd", bind(this._rs_end, this));
	    }

	    this._rs_down = this._rs_ready = false;
	  },
	  _rs_end: function (result) {
	    if (this._rs_progress) {
	      var dir = this._rs_progress[0];
	      var obj = this._rs_progress[1];
	      var newsize = result - this._rs_progress[2];

	      if (dir == "x") {
	        //in case of right split - different sizing logic applied
	        if (this._settings.rightSplit && obj.cind + 1 >= this._rightSplit && obj.cind !== this._columns.length - 1) {
	          obj.cind++;
	          newsize *= -1;
	        }

	        var column = this._columns[obj.cind];
	        var oldwidth = column.width;
	        delete column.fillspace;
	        delete column.adjust;

	        this._setColumnWidth(obj.cind, oldwidth + newsize, true, true);

	        this._updateColsSizeSettings();
	      } else {
	        var rid = this.getIdByIndex(obj.rind);

	        var oldheight = this._getRowHeight(this.getItem(rid));

	        this.setRowHeight(rid, oldheight + newsize);
	      }

	      this._rs_up();
	    }

	    this._rs_progress = null;
	  },
	  _rs_move: function (e) {
	    var cell = null,
	        config = this._settings;
	    if (this._rs_ready && this._rs_process) return this._rs_start(e);
	    e = e || event;
	    var node = e.target || e.srcElement;
	    var mode = false; //resize ready flag

	    if (node.tagName == "TD" || node.tagName == "TABLE") return;
	    var element_class = node.className || "";
	    var in_body = typeof element_class === "string" && element_class.indexOf("webix_cell") != -1; //ignore resize in case of drag-n-drop enabled

	    if (in_body && config.drag) return;
	    var in_header = typeof element_class === "string" && element_class.indexOf("webix_hcell") != -1;
	    this._rs_ready = false;

	    if (in_body || in_header) {
	      var dx = node.offsetWidth;
	      var dy = node.offsetHeight;
	      var pos$$1 = posRelative(e);
	      var resizeRow = config.resizeRow; // if resize is only within the first column

	      if (_typeof(resizeRow) == "object" && resizeRow.headerOnly) {
	        cell = this._locate(node);
	        if (cell.cind > 0) resizeRow = false;
	      }

	      if (in_body && resizeRow) {
	        resizeRow = _typeof(resizeRow) == "object" && resizeRow.size ? resizeRow.size : 3;

	        if (pos$$1.y < resizeRow) {
	          if (!cell) cell = this._locate(node); // avoid resize header border

	          if (cell.rind) {
	            this._rs_ready = ["y", 0, node];
	            mode = "row-resize";
	          }
	        } else if (dy - pos$$1.y < resizeRow + 1) {
	          this._rs_ready = ["y", dy, node];
	          mode = "row-resize";
	        }
	      }

	      var resizeColumn = config.resizeColumn; // if resize is only within the header

	      if (_typeof(resizeColumn) == "object" && resizeColumn.headerOnly && in_body) resizeColumn = false;

	      if (resizeColumn) {
	        resizeColumn = _typeof(resizeColumn) == "object" && resizeColumn.size ? resizeColumn.size : 3;

	        if (pos$$1.x < resizeColumn) {
	          this._rs_ready = ["x", 0, node];
	          mode = "col-resize";
	        } else if (dx - pos$$1.x < resizeColumn + 1) {
	          this._rs_ready = ["x", dx, node];
	          mode = "col-resize";
	        }
	      }
	    } //mark or unmark resizing ready state


	    if (this._cursor_timer) window.clearTimeout(this._cursor_timer);
	    this._cursor_timer = delay(this._mark_resize_ready, this, [mode], mode ? 100 : 0);
	  },
	  _mark_resize_ready: function (mode) {
	    if (this._last_cursor_mode != mode) {
	      this._last_cursor_mode = mode;
	      this._viewobj.style.cursor = mode || "default";
	    }
	  }
	};

	attachEvent("onDataTable", function (table, config) {
	  if (env.touch) {
	    Touch.$init();
	    config.scrollSize = 0; // needed to show datatable scroll

	    if (Touch._disabled) Touch.limit();
	    table.$ready.push(table.$touch);
	  }
	});
	var Mixin$4 = {
	  $touch: function () {
	    var config = this._settings;
	    config.scrollAlignY = false;
	    exports.extend(this, config.prerender === true ? this._touchNative : this._touch);
	    var scrollMode = "";
	    if (!config.autowidth && config.scrollX !== false) scrollMode += "x";
	    if (!config.autoheight && config.scrollY !== false) scrollMode += "y";

	    this._body.setAttribute("touch_scroll", scrollMode);

	    Touch._init_scroll_node(this._body.childNodes[1].firstChild);

	    Touch._set_matrix(this._body.childNodes[1].firstChild, 0, 0, "0ms");

	    this._sync_scroll(0, 0, "0ms");
	  },
	  _touchNative: {
	    _scrollTo_touch: function (x, y) {
	      Touch._set_matrix(this._body.childNodes[1].firstChild, -x, -y, "0ms");

	      this._sync_scroll(-x, -y, "0ms");
	    },
	    _getScrollState_touch: function () {
	      var temp = Touch._get_matrix(this._body.childNodes[1].firstChild);

	      return {
	        x: -temp.e,
	        y: -temp.f
	      };
	    },
	    $init: function () {
	      this.attachEvent("onBeforeScroll", function () {
	        Touch._scroll_node = this._body.childNodes[1].firstChild;

	        Touch._get_sizes(Touch._scroll_node);

	        Touch._scroll_master = this;
	      });
	      this.attachEvent("onTouchEnd", function () {
	        Touch._scroll_master = null;
	      });
	    },
	    _sync_scroll: function (x, y, t) {
	      if (this._settings.leftSplit) Touch._set_matrix(this._body.childNodes[0].firstChild, 0, y, t);
	      if (this._settings.rightSplit) Touch._set_matrix(this._body.childNodes[2].firstChild, 0, y, t);
	      if (this._settings.header) Touch._set_matrix(this._header.childNodes[1].firstChild, x, 0, t);
	      if (this._settings.footer) Touch._set_matrix(this._footer.childNodes[1].firstChild, x, 0, t);
	      this.callEvent("onSyncScroll", [x, y, t]);
	    },
	    _sync_pos: function () {}
	  },
	  _touch: {
	    _scrollTo_touch: function (x, y) {
	      delay(function () {
	        this.callEvent("onAfterScroll", [{
	          e: -x,
	          f: -y
	        }]);
	      }, this);
	    },
	    $scroll: {
	      gravity: 0,
	      elastic: false
	    },
	    $init: function () {
	      //if the result column's width < container's width,
	      this.attachEvent("onAfterColumnHide", function () {
	        this._scrollTo_touch(0, 0);
	      });
	      this.attachEvent("onBeforeScroll", function () {
	        var t = Touch;
	        t._scroll_node = this._body.childNodes[1].firstChild;

	        t._get_sizes(t._scroll_node);

	        t._scroll_stat.left = this._scrollLeft;
	        t._scroll_stat.hidden = this._x_scroll._settings.scrollVisible || this._y_scroll._settings.scrollVisible;
	        t._scroll_stat.dy = this._dtable_height;
	        t._scroll_master = this;
	      });
	      this.attachEvent("onAfterScroll", function (result) {
	        //onAfterScroll may be triggered by some non-touch related logic
	        if (!result) return;
	        var isScrollX = this._scrollLeft != -result.e;
	        var isScrollY = this._scrollTop != -result.f;
	        Touch._scroll_master = null;
	        Touch._fix_f = null;
	        this._scrollTop = 0;
	        this._scrollLeft = 0; //ipad can delay content rendering if 3d transformation applied
	        //switch back to 2d

	        var temp = Touch.config.translate;
	        Touch.config.translate = "translate";

	        this._sync_scroll(this._x_scroll ? 0 : result.e, 0, "0ms");

	        Touch.config.translate = temp;
	        this._scrollLeft = -result.e;
	        this._scrollTop = -result.f;

	        this._correctScrollSize();

	        this.render();

	        if (isScrollX) {
	          if (this._x_scroll) this._x_scroll.scrollTo(this._scrollLeft);
	          this.callEvent("onScrollX", []);
	        }

	        if (isScrollY) {
	          if (this._y_scroll) this._y_scroll.scrollTo(this._scrollTop);
	          this.callEvent("onScrollY", []);
	        }

	        return false;
	      });
	    },
	    _sync_scroll: function (x, y, t) {
	      y += this._scrollTop;
	      x += this._scrollLeft;

	      Touch._set_matrix(this._body.childNodes[1].firstChild, x, y, t);

	      if (this._settings.leftSplit) Touch._set_matrix(this._body.childNodes[0].firstChild, 0, y, t);
	      if (this._settings.rightSplit) Touch._set_matrix(this._body.childNodes[2].firstChild, 0, y, t);
	      if (this._settings.header) Touch._set_matrix(this._header.childNodes[1].firstChild, x, 0, t);
	      if (this._settings.footer) Touch._set_matrix(this._footer.childNodes[1].firstChild, x, 0, t);
	      this.callEvent("onSyncScroll", [x, y, t]);
	    },
	    _sync_pos: function (matrix) {
	      matrix.f -= this._scrollTop;
	      matrix.e -= this._scrollLeft;
	    }
	  }
	};

	var Mixin$5 = {
	  $init: function () {
	    this.data.attachEvent("onStoreUpdated", bind(function (id) {
	      if (!id) this._adjustColumns();
	    }, this));
	    this.attachEvent("onStructureLoad", this._adjustColumns);
	    this.attachEvent("onStructureUpdate", this._resizeColumns);
	    this.attachEvent("onColumnResize", function (a, b, c, user) {
	      if (user) this._resizeColumns();
	    });
	    this.attachEvent("onResize", this._resizeColumns);
	  },
	  _adjustColumns: function () {
	    var resize = false;
	    var cols = this._columns;

	    for (var i = 0; i < cols.length; i++) {
	      if (cols[i].adjust && (cols[i].adjust == "header" || this.count())) resize = this._adjustColumn(i, cols[i].adjust, true) || resize;
	    }

	    if (resize) {
	      this._updateColsSizeSettings(true);

	      this._resizeColumns();
	    }
	  },
	  _resizeColumns: function () {
	    var cols = this._settings.columns;
	    var fill = [];
	    var summ = 0;
	    if (cols && !this._settings.autowidth) for (var i = 0; i < cols.length; i++) {
	      var colfil = cols[i].fillspace;

	      if (colfil) {
	        fill[i] = colfil;
	        summ += colfil * 1 || 1;
	      }
	    }
	    if (summ) this._fillColumnSize(fill, summ);
	  },
	  _fillColumnSize: function (fill, summ) {
	    var cols = this._settings.columns;
	    if (!cols) return;
	    var width = this._content_width - this._scrollSizeY;
	    var resize = false;

	    if (width > 0) {
	      for (var i = 0; i < cols.length; i++) {
	        if (!fill[i]) width -= cols[i].width || this.config.columnWidth;
	      }

	      for (var _i = 0; _i < fill.length; _i++) {
	        if (fill[_i]) {
	          var request = Math.min(width, Math.round(width * fill[_i] / summ));
	          resize = this._setColumnWidth(_i, request, true) || resize;
	          width = width - cols[_i].width;
	          summ = summ - fill[_i];
	        }
	      }

	      if (resize) this._updateColsSizeSettings(true);
	    }
	  },
	  _getColumnConfigSize: function (ind, headers) {
	    var config = this._settings.columns[ind];
	    var max = config.minColumnWidth || 10; //get max data width

	    if (headers != "header") {
	      var count = this.data.order.length;
	      if (config.adjustBatch && config.adjustBatch < count) count = config.adjustBatch;
	      var order = this.data.order.slice(0, count);

	      for (var i = 0; i < count; i++) {
	        order[i] = order[i] ? this._getValue(this.getItem(order[i]), config, 0) : "";
	      }

	      max = Math.max(max, getTextSize(order, "webix_table_cell webix_cell").width);
	    } //get max header width


	    if (headers != "data") {
	      for (var _i2 = 0; _i2 < config.header.length; _i2++) {
	        var header = config.header[_i2];

	        if (header) {
	          var width = 0;
	          if (header.rotate) for (var h = 0; h < (header.rowspan || 1); h++) {
	            width += this._headers[h];
	          }
	          var css = "webix_table_cell webix_cell " + (header.css || "") + (header.rotate ? "webix_measure_rotate" : "");
	          var size = getTextSize([header.text], css, width);
	          max = Math.max(max, header.rotate ? size.height : size.width);
	        }
	      }

	      if (config.sort) max += 10; // add 10px for sort marker
	    } //1px to compensate offsetWidth rounding


	    return max + 1 + (env.isIE ? $active.layoutPadding.space : 0);
	  },
	  _adjustColumn: function (ind, headers, ignore) {
	    if (ind >= 0) {
	      var width = this._getColumnConfigSize(ind, headers);

	      return this._setColumnWidth(ind, width, ignore);
	    }
	  },
	  adjustColumn: function (id, headers) {
	    this._adjustColumn(this.getColumnIndex(id), headers);
	  },
	  adjustRowHeight: function (id, silent) {
	    var _this = this;

	    if (id) {
	      var config = this.getColumnConfig(id);
	      var container;
	      var d = create("DIV", {
	        "class": "webix_table_cell webix_measure_size webix_cell"
	      }, "");
	      d.style.cssText = "width:" + config.width + "px; height:1px; visibility:hidden; position:absolute; top:0px; left:0px; overflow:hidden;";
	      this.$view.appendChild(d);

	      if (d.offsetHeight < 1) {
	        //hidden container, height detection is broken
	        //reattach to the body
	        container = this.$view.cloneNode(true);
	        document.body.appendChild(container);
	        container.appendChild(d);
	      }

	      this.data.each(function (obj) {
	        d.innerHTML = this._getValue(obj, config, 0);
	        obj.$height = Math.max(d.scrollHeight, this._settings.rowHeight);
	      }, this);
	      d = remove(d);
	      if (container) remove(container);
	    } else {
	      //set size of array based on data size
	      //can be not-reliable for tree-like components anyway
	      var heightsArr = new Array(this.data.order.length);
	      var cols = this.config.columns; //set 0 as initial height

	      var j = 0; //iterate through all possible items
	      //we need to be sure that heightsArr is not lesser than real data count

	      for (var key in this.data.pull) {
	        //eslint-disable-line
	        heightsArr[j] = 0;
	        j++;
	      }

	      var _loop = function (i) {
	        //adjust size for single columns
	        _this.adjustRowHeight(cols[i].id, true); //for each row, set height as maximum between all columns


	        var j = 0;

	        _this.data.each(function (obj) {
	          //index is not reliable for tree-components, using a custom counter
	          if (obj.$height > heightsArr[j]) {
	            heightsArr[j] = obj.$height;
	          }

	          obj.$height = heightsArr[j];
	          j++;
	        });
	      };

	      for (var i = 0; i < cols.length; i++) {
	        _loop(i);
	      }
	    }

	    if (!silent) this.refresh();
	  }
	};

	var Mixin$6 = {
	  math_setter: function (value) {
	    if (value) this._math_init();
	    return value;
	  },
	  _math_pref: "$",
	  _math_init: function () {
	    if (env.strict) return;
	    this.data.attachEvent("onStoreUpdated", bind(this._parse_row_math, this));
	    this.data.attachEvent("onStoreLoad", bind(this._parse_math, this));
	    this.attachEvent("onStructureLoad", this._parse_math);
	  },
	  _parse_row_math: function (id, obj, action) {
	    if (!id || action == "paint") return;

	    if (action == "delete") {
	      for (var i = 0; i < this._columns.length; i++) {
	        this._remove_old_triggers(obj, this._columns[i].id);
	      }
	    } else {
	      if (action == "add") this._exprs_by_columns(obj);

	      for (var _i = 0; _i < this._columns.length; _i++) {
	        this._parse_cell_math(id, this._columns[_i].id, action !== "add");
	      }

	      this._math_recalc = {};
	    }
	  },
	  _parse_cell_math: function (row, col, _inner_call) {
	    var item = this.getItem(row);
	    var value; // if it's outer call we should use inputted value otherwise to take formula, not calculated value

	    if (_inner_call === true) value = item[this._math_pref + col] || item[col];else {
	      value = item[col];
	      this._math_recalc = {};
	    }

	    if (value && value.length > 0 && value.toString().substr(0, 1) === "=") {
	      // calculate math value
	      if (!item[this._math_pref + col] || _inner_call !== true) item[this._math_pref + col] = item[col];
	      item[col] = this._calculate(value, row, col); //this.updateItem(item);
	    } else {
	      // just a string
	      if (typeof item[this._math_pref + col] !== "undefined") delete item[this._math_pref + col]; // remove triggers if they were setted earlier

	      this._remove_old_triggers(item, col);
	    } // recalculate depending cells


	    if (typeof item.depends !== "undefined" && typeof item.depends[col] !== "undefined") {
	      for (var i in item.depends[col]) {
	        var name = item.depends[col][i][0] + "__" + item.depends[col][i][1];

	        if (typeof this._math_recalc[name] === "undefined") {
	          this._math_recalc[name] = true;

	          this._parse_cell_math(item.depends[col][i][0], item.depends[col][i][1], true);
	        }
	      }
	    }
	  },
	  _set_original_value: function (row, col) {
	    var item = this.getItem(row);
	    if (typeof item[this._math_pref + col] !== "undefined") item[col] = item[this._math_pref + col];
	  },
	  _parse_math: function () {
	    if (!this._columns || !this.count()) return;

	    this._exprs_by_columns();

	    for (var j = 0; j < this._columns.length; j++) {
	      var col = this.columnId(j);
	      this.data.each(function (obj) {
	        this._parse_cell_math(obj.id, col);
	      }, this);
	    }

	    this._math_recalc = {};
	  },
	  _exprs_by_columns: function (row) {
	    for (var i = 0; i < this._columns.length; i++) {
	      if (this._columns[i].math) {
	        var col = this.columnId(i);
	        var math = "=" + this._columns[i].math;
	        math = math.replace(/\$c/g, "#$c#");

	        if (row) {
	          row[col] = this._parse_relative_expr(math, row.id, col);
	          delete row[this._math_pref + col];

	          this._remove_old_triggers(row, col);
	        } else this.data.each(function (obj) {
	          obj[col] = this._parse_relative_expr(math, obj.id, col);
	          delete obj[this._math_pref + col];

	          this._remove_old_triggers(obj, col);
	        }, this);
	      }
	    }
	  },
	  _parse_relative_expr: function (expr, row, col) {
	    return template(expr)({
	      "$r": row,
	      "$c": col
	    });
	  },
	  _get_calc_value: function (row, col) {
	    var item;
	    if (this.exists(row)) item = this.getItem(row);else return "#out_of_range";
	    var value = item[this._math_pref + col] || item[col] || 0;
	    value = value.toString();
	    if (value.substring(0, 1) !== "=") // it's a string
	      return value;else {
	      // TODO: check if value shouldn't be recalculated
	      // and return value calculated earlier
	      // calculate math expr value right now
	      if (typeof item[this._math_pref + col] === "undefined") item[this._math_pref + col] = item[col];
	      item[col] = this._calculate(value, row, col, true);
	      return item[col];
	    }
	  },
	  _calculate: function (value, row, col, _inner_call) {
	    // add coord in math trace to detect self-references
	    if (_inner_call === true) {
	      if (this._in_math_trace(row, col)) return "#selfreference";
	    } else this._start_math_trace();

	    this._to_math_trace(row, col);

	    var item = this.getItem(row);
	    value = value.substring(1); // get operations list

	    var operations = this._get_operations(value);

	    var triggers = this._get_refs(value, row);

	    if (operations) {
	      value = this._replace_refs(value, triggers);
	      value = this._parse_args(value, operations);
	    } else {
	      value = this._replace_refs(value, triggers, true);
	    }

	    var exc = this._math_exception(value);

	    if (exc !== false) return exc; // remove from coord from trace when calculations were finished - it's important!

	    this._from_math_trace(row, col); // process triggers to know which cells should be recalculated when one was changed


	    this._remove_old_triggers(item, col);

	    for (var i = 0; i < triggers.length; i++) {
	      this._add_trigger([row, col], triggers[i]);
	    }

	    exc = this._math_exception(value);
	    if (exc !== false) return exc; // there aren't any operations here. returns number or value of another cell

	    if (!value) return value; // process mathematical expression and getting final result

	    value = this._compute(value.replace(/\$r/g, item.id));
	    exc = this._math_exception(value);
	    if (exc !== false) return exc;
	    return value;
	  },
	  _get_operations: function (value) {
	    // gettings operations list (+-*/)
	    var splitter = /(\+|-|\*|\/)/g;
	    var operations = value.replace(/\[[^)]*?\]/g, "").match(splitter);
	    return operations;
	  },

	  /*! gets list of referencies in formula
	   **/
	  _get_refs: function (value, id) {
	    var reg = /\[([^\]]+),([^\]]+)\]/g;
	    var cells = value.match(reg);
	    if (cells === null) cells = [];

	    for (var i = 0; i < cells.length; i++) {
	      var cell = cells[i];
	      var tmp = cell;
	      cell = cell.substr(1, cell.length - 2);
	      cell = cell.split(",");
	      cell[0] = this._trim(cell[0]);
	      cell[1] = this._trim(cell[1]);
	      if (cell[0].substr(0, 1) === ":") cell[0] = this.getIdByIndex(cell[0].substr(1));
	      if (cell[0] === "$r") cell[0] = id;
	      if (cell[1].substr(0, 1) === ":") cell[1] = this.columnId(cell[1].substr(1));
	      cell[2] = tmp;
	      cells[i] = cell;
	    }

	    return cells;
	  },
	  // replace given list of references by their values
	  _replace_refs: function (value, cells, clean) {
	    var dell = "(",
	        delr = ")";
	    if (clean) dell = delr = "";

	    for (var i = 0; i < cells.length; i++) {
	      var cell = cells[i];

	      var cell_value = this._get_calc_value(cell[0], cell[1]);

	      if (isNaN(cell_value)) cell_value = "\"" + cell_value + "\"";
	      value = value.replace(cell[2], dell + cell_value + delr);
	    }

	    return value;
	  },
	  _parse_args: function (value, operations) {
	    var args = [];

	    for (var i = 0; i < operations.length; i++) {
	      var op = operations[i];

	      var temp = this._split_by(value, op);

	      args.push(temp[0]);
	      value = temp[1];
	    }

	    args.push(value); //var reg = /^(-?\d|\.|\(|\))+$/;

	    for (var _i2 = 0; _i2 < args.length; _i2++) {
	      var arg = this._trim(args[_i2]); //	if (reg.test(arg) === false)
	      //		return ''; //error


	      args[_i2] = arg;
	    }

	    var expr = "";

	    for (var _i3 = 0; _i3 < args.length - 1; _i3++) {
	      expr += args[_i3] + operations[_i3];
	    }

	    expr += args[args.length - 1];
	    return expr;
	  },
	  _compute: function (expr) {
	    var result;

	    try {
	      result = window.eval(expr);
	    } catch (ex) {
	      assert(false, "Math error in datatable<br>" + expr);
	      result = "";
	    }

	    return result.toString();
	  },
	  _split_by: function (value, splitter) {
	    var pos = value.indexOf(splitter);
	    var before = value.substr(0, pos);
	    var after = value.substr(pos + 1);
	    return [before, after];
	  },
	  _trim: function (value) {
	    value = value.replace(/^ */g, "");
	    value = value.replace(/ *$/g, "");
	    return value;
	  },
	  _start_math_trace: function () {
	    this._math_trace = [];
	  },
	  _to_math_trace: function (row, col) {
	    this._math_trace[row + "__" + col] = true;
	  },
	  _from_math_trace: function (row, col) {
	    if (typeof this._math_trace[row + "__" + col] !== "undefined") delete this._math_trace[row + "__" + col];
	  },
	  _in_math_trace: function (row, col) {
	    if (typeof this._math_trace[row + "__" + col] !== "undefined") return true;else return false;
	  },
	  _add_trigger: function (depends, from) {
	    var item = this.getItem(from[0]);
	    if (typeof item.depends === "undefined") item.depends = {};
	    if (typeof item.depends[from[1]] === "undefined") item.depends[from[1]] = {};
	    item.depends[from[1]][depends[0] + "__" + depends[1]] = depends;
	    item = this.getItem(depends[0]);
	    if (typeof item.triggers === "undefined") item.triggers = {};
	    if (typeof item.triggers[depends[1]] === "undefined") item.triggers[depends[1]] = {};
	    item.triggers[depends[1]][from[0] + "__" + from[1]] = from;
	  },
	  _remove_old_triggers: function (item, col) {
	    if (!item) return;
	    if (typeof item.triggers === "undefined") return;

	    for (var i in item.triggers[col]) {
	      var depend = item.triggers[col][i];
	      var row = this.getItem(depend[0]);
	      if (row) delete row.depends[depend[1]][item.id + "__" + col];
	    }
	  },
	  // check if exception syntax exists and returns exception text or false
	  _math_exception: function (value) {
	    var reg = /#\w+/;
	    var match = value.match(reg);
	    if (match !== null && match.length > 0) return match[0];
	    return false;
	  }
	};

	var temp$4 = {};

	// #include core/edit.js
	// #include ui/inputs.js

	var Mixin$7 = {
	  /////////////////////////
	  //    edit start       //
	  /////////////////////////
	  _get_editor_type: function (id) {
	    return this.getColumnConfig(id.column).editor;
	  },
	  getEditor: function (row, column) {
	    if (!row) return this._last_editor;

	    if (arguments.length == 1) {
	      column = row.column;
	      row = row.row;
	    }

	    return (this._editors[row] || {})[column];
	  },
	  _for_each_editor: function (handler) {
	    for (var row in this._editors) {
	      var row_editors = this._editors[row];

	      for (var column in row_editors) {
	        if (column != "$count") handler.call(this, row_editors[column]);
	      }
	    }
	  },
	  _init_editor: function (id, type, show) {
	    var row = id.row;
	    var column = id.column;
	    var col_settings = type.config = this.getColumnConfig(column); //show it over cell

	    if (show !== false) this.showCell(row, column);
	    var node = type.render();
	    if (type.$inline) node = this._locateInput(id);
	    type.node = node;
	    var item = this.getItem(row);
	    var format = col_settings.editFormat;
	    var value;
	    if (this._settings.editMath) value = item["$" + column];
	    value = value || item[column];
	    if (isUndefined(value)) value = "";
	    type.setValue(format ? format(value) : value, item);
	    type.value = item[column];

	    this._addEditor(id, type);

	    if (!type.$inline) type._editor_pos = this._sizeToCell(id, node, true);
	    if (type.afterRender) type.afterRender();

	    if (this._settings.liveValidation) {
	      _event(type.node, "keyup", this._bind_live_validation(id, this));

	      this.validateEditor(id);
	    }

	    return node;
	  },
	  _bind_live_validation: function (id, that) {
	    return function () {
	      that.validateEditor(id);
	    };
	  },
	  _get_new_value: function (editor) {
	    var parser = this.getColumnConfig(editor.column).editParse;
	    var new_value = editor.getValue();
	    return parser ? parser(new_value) : new_value;
	  },
	  _set_new_value: function (editor, new_value, copy$$1) {
	    var item = copy$$1 ? {} : this.getItem(editor.row);
	    item[editor.column] = new_value;
	    if (this._settings.editMath) item["$" + editor.column] = null;
	    return item;
	  },
	  //register editor in collection
	  _addEditor: function (id, type) {
	    var row_editors = this._editors[id.row] = this._editors[id.row] || {};
	    row_editors.$count = (row_editors.$count || 0) + 1;
	    type.row = id.row;
	    type.column = id.column;
	    this._last_editor = row_editors[id.column] = type;
	    this._in_edit_mode++;
	    this._last_editor_scroll = this.getScrollState();
	  },
	  _removeEditor: function (editor) {
	    if (this._last_editor == editor) this._last_editor = 0;
	    if (editor.destroy) editor.destroy();
	    var row = this._editors[editor.row];
	    delete row[editor.column];
	    row.$count--;
	    if (!row.$count) delete this._editors[editor.row];
	    this._in_edit_mode--;
	  },
	  _changeEditorId: function (oldid, newid) {
	    var editor = this._editors[oldid];

	    if (editor) {
	      this._editors[newid] = editor;
	      delete this._editors[oldid];

	      for (var key in editor) {
	        if (key != "$count") editor[key].row = newid;
	      }
	    }
	  },
	  //get html cell by combined id
	  _locate_cell: function (id) {
	    var area,
	        i,
	        index,
	        j,
	        node,
	        span,
	        config = this.getColumnConfig(id.column),
	        cell = 0;

	    if (config && config.node && config.attached) {
	      index = this.getIndexById(id.row);

	      if (this._spans_pull) {
	        span = this.getSpan(id.row, id.column);

	        if (span) {
	          for (i = 0; i < 3; i++) {
	            area = this._spans_areas[i];

	            for (j = 0; !cell && j < area.childNodes.length; j++) {
	              node = area.childNodes[j];
	              if (node.getAttribute(
	              /*@attr*/
	              "row") == index && node.getAttribute(
	              /*@attr*/
	              "column") == this.getColumnIndex(id.column)) cell = node;
	            }
	          }
	        }
	      }

	      if (!cell && index >= config._yr0 - this._settings.topSplit && index < config._yr1) cell = config.node.childNodes[index - config._yr0 + this._settings.topSplit];
	    }

	    return cell;
	  },
	  /////////////////////////
	  //    public methods   //
	  /////////////////////////
	  editCell: function (row, column, preserve, show) {
	    column = column || this._settings.columns[0].id;
	    return EditAbility.edit.call(this, {
	      row: row,
	      column: column
	    }, preserve, show);
	  },
	  editRow: function (id) {
	    if (id && id.row) id = id.row;
	    var next = false;
	    this.eachColumn(function (column) {
	      this.edit({
	        row: id,
	        column: column
	      }, next, !next);
	      next = true;
	    });
	  },
	  editColumn: function (id) {
	    if (id && id.column) id = id.column;
	    var next = false;
	    this.eachRow(function (row) {
	      this.edit({
	        row: row,
	        column: id
	      }, next, !next);
	      next = true;
	    });
	  },
	  eachRow: function (handler, all) {
	    var order = this.data.order;
	    if (all) order = this.data._filter_order || order;

	    for (var i = 0; i < order.length; i++) {
	      handler.call(this, order[i]);
	    }
	  },
	  eachColumn: function (handler, all) {
	    for (var i in this._columns_pull) {
	      var column = this._columns_pull[i];
	      handler.call(this, column.id, column);
	    }

	    if (all) {
	      for (var _i in this._hidden_column_hash) {
	        var _column = this._hidden_column_hash[_i];
	        handler.call(this, _column.id, _column);
	      }
	    }
	  },
	  ////////////////////
	  //    edit next   //
	  ////////////////////
	  _after_edit_next: function (editor_next) {
	    if (this.getSelectedId) {
	      //select related cell when possible
	      var sel = this.getSelectedId(true);

	      if (sel.length == 1) {
	        this._select(editor_next);

	        return false;
	      }
	    }
	  },
	  _custom_tab_handler: function (tab, e) {
	    if (this._settings.editable && !this._in_edit_mode) {
	      //if we have focus in some custom input inside of datatable
	      if (e.target && e.target.tagName == "INPUT") return true; //init editor related to a single selected row/column/cell

	      var selection = this.getSelectedId(true);

	      if (selection.length == 1) {
	        var sel = selection[0];
	        if (this._settings.select == "row") sel.column = this._settings.columns[e.shiftKey ? 0 : this._settings.columns.length - 1].id;
	        this.editNext(tab, sel);
	        return false;
	      }
	    }

	    return true;
	  },
	  _find_cell_next: function (start, check, direction) {
	    var row = this.getIndexById(start.row);
	    var column = this.getColumnIndex(start.column);
	    var order = this.data.order;
	    var cols = this._columns;

	    if (direction) {
	      for (var i = row; i < order.length; i++) {
	        for (var j = column + 1; j < cols.length; j++) {
	          var id = {
	            row: order[i],
	            column: cols[j].id
	          };

	          if (check.call(this, id) && (!this._checkCellMerge || !this._checkCellMerge(start, id))) {
	            return id;
	          }
	        }

	        column = -1;
	      }
	    } else {
	      for (var _i2 = row; _i2 >= 0; _i2--) {
	        for (var _j = column - 1; _j >= 0; _j--) {
	          var _id = {
	            row: order[_i2],
	            column: cols[_j].id
	          };
	          if (check.call(this, _id)) return _id;
	        }

	        column = cols.length;
	      }
	    }

	    return null;
	  },
	  /////////////////////////////
	  //    scroll correction    //
	  /////////////////////////////
	  _correct_after_focus_y: function () {
	    if (this._in_edit_mode) {
	      if (this._ignore_after_focus_scroll) this._ignore_after_focus_scroll = false;else {
	        this._y_scroll.scrollTo(this.getScrollState().y + this._body.childNodes[1].firstChild.scrollTop);

	        this._body.childNodes[1].firstChild.scrollTop = 0;
	        this._ignore_after_focus_scroll = true;
	      }
	    }
	  },
	  _correct_after_focus_x: function () {
	    if (this._in_edit_mode) {
	      this._x_scroll.scrollTo(this._body.childNodes[1].scrollLeft);
	    }
	  },
	  _component_specific_edit_init: function () {
	    this.attachEvent("onScrollY", this._update_editor_y_pos);
	    this.attachEvent("onScrollX", this._update_editor_y_pos);
	    this.attachEvent("onScrollY", this._refocus_inline_editor);
	    this.attachEvent("onColumnResize", function () {
	      this.editStop();
	    });
	    this.attachEvent("onAfterFilter", function () {
	      this.editStop();
	    });
	    this.attachEvent("onRowResize", function () {
	      this.editStop();
	    });
	    this.attachEvent("onAfterScroll", function () {
	      if (this._settings.topSplit) this.editStop();
	    });
	    this._body.childNodes[1].firstChild.onscroll = bind(this._correct_after_focus_y, this);
	    this._body.childNodes[1].onscroll = bind(this._correct_after_focus_x, this);
	  },
	  _update_editor_y_pos: function () {
	    if (this._in_edit_mode) {
	      var old = this._last_editor_scroll;
	      this._last_editor_scroll = this.getScrollState();
	      var diff = this._last_editor_scroll.y - old.y;

	      this._for_each_editor(function (editor) {
	        if (editor.getPopup) {
	          var node = this.getItemNode(editor);
	          var isHidden = false;

	          if (this._settings.prerender) {
	            var pos = editor._editor_pos;
	            var ydiff = pos.top - this._scrollTop;
	            var xdiff = pos.left - this._scrollLeft;
	            isHidden = ydiff < 0 || ydiff + pos.height > this._dtable_offset_height || xdiff < 0 || xdiff + pos.width > this.$width - this._scrollSizeX;
	          }

	          if (!node || isHidden) editor.getPopup().show({
	            x: -10000,
	            y: -10000
	          });else editor.getPopup().show(node);
	        }

	        if (!this._settings.prerender && !editor.linkInput && !editor.$inline) {
	          editor.node.top -= diff;
	          editor.node.style.top = editor.node.top + "px";
	        }
	      });
	    }
	  }
	};

	var Mixin$8 = {
	  $init: function () {
	    this._clear_hidden_state();

	    this.attachEvent("onStructureLoad", this._hideInitialColumns);
	  },
	  _clear_hidden_state: function () {
	    this._hidden_column_hash = {};
	    this._hidden_column_order = toArray();
	    this._hidden_split = [0, 0];
	  },
	  _hideInitialColumns: function () {
	    var cols = this._columns;

	    for (var i = 0; i < cols.length; i++) {
	      if (cols[i].header) this._getInitialSpans(cols, cols[i].header);
	      if (cols[i].footer) this._getInitialSpans(cols, cols[i].footer);
	    }

	    for (var _i = cols.length - 1; _i >= 0; _i--) {
	      if (cols[_i].hidden) this.hideColumn(cols[_i].id, {}, true, true);else if (cols[_i].batch && this.config.visibleBatch && cols[_i].batch != this.config.visibleBatch) {
	        this.hideColumn(cols[_i].id, {}, true, true);
	      }
	    }
	  },
	  _getInitialSpans: function (cols, elements) {
	    for (var h = 0; h < elements.length; h++) {
	      var line = elements[h];
	      if (line && line.colspan && !line.$colspan) line.$colspan = line.colspan;
	    }
	  },
	  moveColumn: function (id, index) {
	    var start_index = this.getColumnIndex(id);
	    if (start_index == index) return; //already in place

	    var columns = this._settings.columns;
	    var start = columns.splice(start_index, 1);
	    var pos = index - (index > start_index ? 1 : 0);
	    PowerArray.insertAt.call(columns, start[0], pos);
	    var order = this._hidden_column_order; // order exists even if columns are not reordered, so checking for length

	    if (order.length) {
	      order = toArray(order);
	      var hidden_index = order.find(id);
	      order.removeAt(hidden_index);
	      if (pos === 0) order.unshift(id);else {
	        order.insertAt(id, order.find(columns[pos - 1].id) + 1);
	      }
	    } //TODO: split handling
	    //we can move split line when column dropped after it


	    this._refresh_columns();
	  },
	  _init_horder: function () {
	    var horder = this._hidden_column_order;
	    var cols = this._settings.columns;

	    if (!horder.length) {
	      for (var i = 0; i < cols.length; i++) {
	        horder[i] = cols[i].id;
	      }

	      this._hidden_split = [this._settings.leftSplit, this._rightSplit];
	    }
	  },
	  isColumnVisible: function (id) {
	    return !this._hidden_column_hash[id];
	  },
	  hideColumn: function (id, opts, silent, mode) {
	    var cols = this._settings.columns;
	    var horder = this._hidden_column_order;
	    var hhash = this._hidden_column_hash;
	    var column;
	    var span = 1;
	    opts = opts || {};

	    if (mode !== false) {
	      var index = this.getColumnIndex(id);
	      assert(index != -1, "hideColumn: invalid ID or already hidden");
	      if (index === -1 || !this.callEvent("onBeforeColumnHide", [id])) return; //in case of second call to hide the same column, command will be ignored

	      if (index == -1) return;

	      this._init_horder();

	      if (opts.spans) {
	        var header = cols[index].header;

	        for (var i = 0; i < header.length; i++) {
	          if (header[i]) {
	            header[i].$groupSpan = header[i].colspan || 1;
	            span = Math.max(span, header[i].$groupSpan);
	          }
	        }
	      }

	      if (index < this._settings.leftSplit) this._settings.leftSplit -= span;
	      if (index >= this._rightSplit) this._settings.rightSplit -= span;else this._rightSplit -= span;

	      for (var _i2 = index + span - 1; _i2 >= index; _i2--) {
	        this._hideColumn(index);

	        column = cols.splice(index, 1)[0];
	        hhash[column.id] = column;
	        column._yr0 = -1;
	        delete this._columns_pull[column.id];
	      }

	      this.callEvent("onAfterColumnHide", [id]);
	    } else {
	      column = hhash[id];
	      assert(column, "showColumn: invalid ID or already visible"); //in case of second show command for already visible column - ignoring

	      if (!column || !this.callEvent("onBeforeColumnShow", [id])) return;
	      var prev = null;
	      var _i3 = 0;
	      var hindex = 0;

	      for (; _i3 < horder.length; _i3++) {
	        if (horder[_i3] == id) {
	          hindex = _i3;
	          break;
	        }

	        if (!hhash[horder[_i3]]) prev = horder[_i3];
	      }

	      var _index = prev ? this.getColumnIndex(prev) + 1 : 0;

	      if (opts.spans) {
	        var _header = column.header;

	        for (var _i4 = 0; _i4 < _header.length; _i4++) {
	          if (_header[_i4]) {
	            _header[_i4].colspan = _header[_i4].$groupSpan || _header[_i4].colspan;
	            delete _header[_i4].$groupSpan;
	            span = Math.max(span, _header[_i4].colspan || 1);
	          }
	        }
	      }

	      for (var _i5 = hindex + span - 1; _i5 >= hindex; _i5--) {
	        var _column = hhash[horder[_i5]];

	        if (_column) {
	          //can be already shown by another action
	          PowerArray.insertAt.call(cols, _column, _index);
	          delete _column.hidden;
	          delete hhash[_column.id];
	          this._columns_pull[_column.id] = _column;
	        } else span--;
	      }

	      if (hindex < this._hidden_split[0]) this._settings.leftSplit += span;
	      if (hindex >= this._hidden_split[1]) this._settings.rightSplit += span;else this._rightSplit += span;
	      this.callEvent("onAfterColumnShow", [id]);
	    }

	    if (column.header) this._fixColspansHidden(column, mode !== false ? 0 : 1, "header");
	    if (column.footer) this._fixColspansHidden(column, mode !== false ? 0 : 1, "footer");
	    if (!silent) this._refresh_columns();
	  },
	  _fixColspansHidden: function (config, mod, elName) {
	    for (var i = config[elName].length - 1; i >= 0; i--) {
	      var ind = this._hidden_column_order;
	      var spanSource,
	          isHidden = false,
	          spanSize = 0;

	      for (var j = 0; j < ind.length; j++) {
	        var colConfig = this.getColumnConfig(ind[j]);
	        var el = colConfig[elName][i];

	        if (!this.isColumnVisible(ind[j])) {
	          //hidden column
	          if (el && el.$colspan && spanSize <= 0) {
	            //start of colspan in hidden
	            spanSize = el.colspan = el.$colspan;
	            isHidden = spanSource = el;
	          }

	          if (spanSource && spanSize > 0) {
	            //hidden column in colspan, decrease colspan size
	            spanSource.colspan--;
	          }
	        } else {
	          //visible column
	          if (isHidden && spanSize > 0 && spanSource && spanSource.colspan > 0) {
	            //bit start of colspan is hidden
	            el = colConfig[elName][i] = spanSource;
	            spanSource = el;
	          } else if (el && el.$colspan && spanSize <= 0) {
	            //visible start of colspan
	            spanSize = el.colspan = el.$colspan;
	            spanSource = el;
	          }

	          isHidden = null;
	        }

	        spanSize--;
	      }
	    }
	  },
	  refreshColumns: function (columns, reset) {
	    this._dtable_column_refresh = true;

	    if (columns && columns != this.config.columns || reset) {
	      this._clear_hidden_state();

	      this._filter_elements = {};
	      if (columns) this._rightSplit = columns.length - (this.config.rightSplit || 0);
	    }

	    this._columns_pull = {}; //clear rendered data

	    for (var i = 0; i < this._columns.length; i++) {
	      var col = this._columns[i];
	      this._columns_pull[col.id] = col;
	      col.attached = col.node = null;
	    }

	    for (var _i6 = 0; _i6 < 3; _i6++) {
	      this._header.childNodes[_i6].innerHTML = "";
	      this._body.childNodes[_i6].firstChild.innerHTML = "";
	    } //render new structure


	    this._columns = this.config.columns = columns || this.config.columns;
	    this._rightSplit = this._columns.length - this._settings.rightSplit;
	    this._dtable_fully_ready = 0;

	    this._define_structure();

	    this.callEvent("onStructureUpdate");

	    this._update_scroll();

	    this.render();
	    this._dtable_column_refresh = 0;
	  },
	  _refresh_columns: function () {
	    this._dtable_fully_ready = 0;
	    this.callEvent("onStructureUpdate");

	    this._apply_headers();

	    this.render();
	  },
	  showColumn: function (id, opts, silent) {
	    return this.hideColumn(id, opts, silent, false);
	  },
	  showColumnBatch: function (batch, mode) {
	    var preserve = typeof mode != "undefined";
	    mode = mode !== false;
	    this.eachColumn(function (id, col) {
	      if (col.batch) {
	        var hidden = this._hidden_column_hash[col.id];
	        if (!mode) hidden = !hidden;
	        if (col.batch == batch && hidden) this.hideColumn(col.id, {
	          spans: true
	        }, true, !mode);else if (!preserve && col.batch != batch && !hidden) this.hideColumn(col.id, {
	          spans: true
	        }, true, mode);
	      }
	    }, true);

	    this._refresh_columns();
	  }
	};

	var Mixin$9 = {
	  $init: function () {
	    this.attachEvent("onAfterScroll", this._set_focusable_item);
	    this.attachEvent("onFocus", function () {
	      addCss(this.$view, "webix_dtable_focused");
	    });
	    this.attachEvent("onBlur", function () {
	      removeCss(this.$view, "webix_dtable_focused");
	    });
	  },
	  _set_focusable_item: function () {
	    var sel = this._getVisibleSelection();

	    if (!sel) {
	      var node = this._dataobj.querySelector(".webix_cell");

	      if (node) node.setAttribute("tabindex", "0");
	    }
	  },
	  _getVisibleSelection: function () {
	    var sel = this.getSelectedId(true);

	    for (var i = 0; i < sel.length; i++) {
	      if (this.isColumnVisible(sel[i].column)) return this.getItemNode(sel[i]);
	    }

	    return null;
	  },
	  moveSelection: function (mode, details, focus) {
	    if (this._settings.disabled) return;
	    details = details || {}; //get existing selection as array

	    var t = this.getSelectedId(true);
	    var index$$1 = t.length - 1;
	    var preserve = this._settings.multiselect || this._settings.areaselect ? details.shift : false; //change defaults in case of multiselection

	    if (t.length > 1 && this._settings.select !== "cell") {
	      t = t.sort(bind(function (a, b) {
	        if (this.getIndexById(a.row) > this.getIndexById(b.row) || this.getColumnIndex(a.column) > this.getColumnIndex(b.column)) return 1;else return -1;
	      }, this));
	      if (mode == "up" || mode == "left" || mode == "top" || mode == "pgup") index$$1 = 0;
	    }

	    if (index$$1 < 0 && this.count()) {
	      //no selection
	      if (mode == "down" || mode == "right") mode = "top";else if (mode == "up" || mode == "left") mode = "bottom";else return;
	      index$$1 = 0;
	      t = [{
	        row: 1,
	        column: 1
	      }];
	    }

	    if (index$$1 >= 0) {
	      var row = t[index$$1].row;
	      var column = t[index$$1].column;

	      if (mode == "top" || mode == "bottom") {
	        if (row) {
	          // first/last row setting
	          if (mode == "top") row = this.data.getFirstId();else if (mode == "bottom") row = this.data.getLastId();
	        }

	        if (column) {
	          // first/last column setting
	          index$$1 = 0;
	          if (mode == "bottom") index$$1 = this.config.columns.length - 1;
	          column = this.columnId(index$$1);
	        }
	      } else if (mode == "up" || mode == "down" || mode == "pgup" || mode == "pgdown") {
	        if (row) {
	          //it seems row's can be seleted
	          var _index = this.getIndexById(row);

	          var step = 1;
	          if (mode == "pgup" || mode == "pgdown") step = this._pager ? this._pager.config.size : Math.round(this._dtable_offset_height / this._settings.rowHeight); //get new selection row

	          if (mode == "up" || mode == "pgup") _index -= step;else if (mode == "down" || mode == "pgdown") _index += step; //check that we in valid row range

	          if (_index < 0) _index = 0;
	          if (_index >= this.data.order.length) _index = this.data.order.length - 1;
	          row = this.getIdByIndex(_index);
	          if (!row && this._settings.pager) this.showItemByIndex(_index);
	        }
	      } else if (mode == "right" || mode == "left") {
	        if (column && this.config.select != "row") {
	          //it seems column's can be selected
	          var _index2 = this.getColumnIndex(column); //get new selected column


	          if (mode == "right") _index2++;else if (mode == "left") _index2--; //check that result column index is in valid range

	          if (_index2 < 0) _index2 = 0;
	          if (_index2 >= this.config.columns.length) _index2 = this.config.columns.length - 1;
	          column = this.columnId(_index2);
	        } else if ((this.open || this._subViewStorage) && mode == "right") {
	          return this.open ? this.open(row) : this.openSub(row);
	        } else if ((this.close || this._subViewStorage) && mode == "left") {
	          return this.close ? this.close(row) : this.closeSub(row);
	        }
	      } else {
	        assert(false, "Not supported selection moving mode");
	        return;
	      }

	      if (row) {
	        this.showCell(row, column);

	        if (!this.select) {
	          //switch on cell or row selection by default
	          exports.extend(this, this._selections._commonselect, true);
	          this._settings.select = this.open || this._subViewStorage ? "row" : "cell";
	          exports.extend(this, this._selections[this._settings.select], true);
	        }

	        var cell = {
	          row: row,
	          column: column
	        };

	        if (preserve && this._settings.select == "area") {
	          var last = this._selected_areas[this._selected_areas.length - 1];

	          this._extendAreaRange(cell, last, mode, details);
	        } else this._select(cell, preserve);

	        if (!this._settings.clipboard && focus !== false) {
	          var node = this.getItemNode(cell);
	          if (node) node.focus();
	        }
	      }
	    }

	    return false;
	  }
	};

	var DragOrder$1 = {
	  _add_css: function (source) {
	    var context = DragControl._drag_context;
	    if (!this._settings.prerender) source = [context.start];

	    for (var i = 0; i < source.length; i++) {
	      for (var j = 0; j < this._columns.length; j++) {
	        var node = this.getItemNode({
	          row: source[i],
	          cind: j
	        });
	        if (node) addCss(node, "webix_invisible");
	      }

	      this.data.addMark(source[i], "webix_invisible", 1, 1, true);
	    }
	  },
	  _remove_css: function (source) {
	    var context = DragControl._drag_context;
	    if (!this._settings.prerender) source = [context.start];

	    for (var i = 0; i < source.length; i++) {
	      for (var j = 0; j < this._columns.length; j++) {
	        var node = this.getItemNode({
	          row: source[i],
	          cind: j
	        });
	        if (node) removeCss(node, "webix_invisible");
	      }

	      this.data.removeMark(source[i], "webix_invisible", 1);
	    }
	  },
	  _set_drop_area: function (target) {
	    for (var i = 0; i < this._columns.length; i++) {
	      var column = this._columns[i];
	      var node = this.getItemNode({
	        row: target,
	        cind: i
	      });

	      if (node) {
	        node.parentNode.insertBefore(DragControl._dropHTML[i], node);
	      } else column.node.appendChild(DragControl._dropHTML[i]);
	    }
	  },
	  _init_drop_area: function () {
	    var dropArea = [];
	    var count = this._columns.length;
	    var node = document.createElement("div");
	    node.className = "webix_drop_area";
	    node.style.height = this._settings.rowHeight + "px";
	    node.innerHTML = this.$dropHTML();

	    for (var i = 0; i < count; i++) {
	      dropArea.push(node.cloneNode(true));
	    }

	    return dropArea;
	  }
	};
	var Mixin$a = {
	  drag_setter: function (value) {
	    // disable drag-n-drop for frozen rows
	    this.attachEvent("onBeforeDrag", function (context) {
	      return this._checkDragTopSplit(context.source);
	    });
	    this.attachEvent("onBeforeDragIn", function (context) {
	      var result = this._checkDragTopSplit(context.target);

	      if (!result && DragControl._dropHTML) {
	        remove(DragControl._dropHTML);
	        this._marked_item_id = DragControl._dropHTML = null;
	      }

	      return result;
	    });
	    DragItem.drag_setter.call(this, value);
	    if (value == "order" || value == "move") exports.extend(this, DragOrder$1, true);
	    return value;
	  },
	  _checkDragTopSplit: function (ids) {
	    var i,
	        index$$1,
	        frozen = false;

	    if (this._settings.topSplit && ids) {
	      if (!isArray(ids)) ids = [ids];

	      for (i = 0; !frozen && i < ids.length; i++) {
	        index$$1 = this.getIndexById(ids[i]);
	        frozen = index$$1 >= 0 && index$$1 < this._settings.topSplit;
	      }
	    }

	    return !frozen;
	  },
	  _toHTML: function (obj) {
	    var width = this._content_width - this._scrollSizeY;
	    var html = "<div class='webix_dd_drag' style='width:" + width + "px;'>";
	    var cols = this._settings.columns;

	    for (var i = 0; i < cols.length; i++) {
	      var value = this._getValue(obj, cols[i]);

	      html += "<div style='width:" + cols[i].width + "px;'>" + value + "</div>";
	    }

	    return html + "</div>";
	  },
	  getHeaderNode: function (column_id, row_index) {
	    return this._getHeaderNode(column_id, row_index, this._header);
	  },
	  getFooterNode: function (column_id, row_index) {
	    return this._getHeaderNode(column_id, row_index, this._footer);
	  },
	  _getHeaderNode: function (column_id, row_index, group) {
	    if (this.isColumnVisible(column_id)) {
	      var ind = this.getColumnIndex(column_id);
	      var hind = this._settings.leftSplit > ind ? 0 : this._rightSplit <= ind ? 2 : 1;
	      row_index = row_index || 0;
	      var rows = group.childNodes[hind].getElementsByTagName("TR");

	      if (rows.length) {
	        var nodes = rows[row_index + 1].childNodes;

	        for (var i = 0; i < nodes.length; i++) {
	          if (nodes[i].getAttribute(
	          /*@attr*/
	          "column") == ind) return nodes[i].firstChild;
	        }
	      }
	    }

	    return null;
	  },
	  getItemNode: function (id) {
	    if (id && !id.header) {
	      var row = id.row || id;
	      var rowindex = typeof id.rind === "number" ? id.rind : this.getIndexById(row);

	      var state = this._get_y_range();

	      var minRow = state[0] - this._settings.topSplit; //row not visible

	      if (rowindex < minRow && rowindex > state[1]) return; //get visible column

	      var x_range = this._get_x_range();

	      var colindex = this._settings.leftSplit ? 0 : x_range[0];
	      var specific = typeof id.cind === "number";

	      if (id.column || specific) {
	        colindex = specific ? id.cind : this.getColumnIndex(id.column); //column not visible

	        if (colindex < this._rightSplit && colindex >= this._settings.leftSplit && (colindex < x_range[0] || colindex > x_range[1])) return;
	      }

	      var column = this._settings.columns[colindex];

	      if (column.attached && column.node) {
	        if (row === "$webix-drop") return DragControl._dropHTML[colindex];
	        var nodeIndex = rowindex < this._settings.topSplit || this._settings.prerender ? rowindex : rowindex - minRow;
	        var nodes = column.node.childNodes;
	        var length = Math.min(nodes.length, nodeIndex + 1);

	        for (var i = 0; i < length; i++) {
	          if (nodes[i].className === "webix_drop_area") nodeIndex++;
	        }

	        return nodes[nodeIndex];
	      }
	    }
	  },
	  _isDraggable: function (e) {
	    var nodeName = (e.target || e.srcElement).nodeName;
	    return nodeName != "INPUT" && nodeName != "TEXTAREA";
	  },
	  dragColumn_setter: function (value) {
	    var control; //will be defined below

	    if (value == "order") {
	      control = {
	        $drag: bind(function (s, e) {
	          if (!this._isDraggable(e) || this._rs_process) return false;
	          var id = this.locate(e);
	          if (!id || !this.callEvent("onBeforeColumnDrag", [id.column, e])) return false;
	          DragControl._drag_context = {
	            from: control,
	            start: id,
	            custom: "column_dnd"
	          };
	          var column = this.getColumnConfig(id.column);
	          this._relative_column_drag = posRelative(e);
	          this._limit_column_drag = column.width;
	          return "<div class='webix_dd_drag_column' style='width:" + column.width + "px'>" + (column.header[0].text || "&nbsp;") + "</div>";
	        }, this),
	        $dragPos: bind(function (pos$$1, e, node) {
	          var context = DragControl.getContext();
	          var box = offset(this.$view);
	          node.style.display = "none";
	          var html = document.elementFromPoint(pos$$1.x, box.y + 1);
	          var id = html ? this.locate(html) : null;
	          var start = DragControl.getContext().start.column;

	          if (id && id.column != start && (!this._column_dnd_temp_block || id.column != this._last_sort_dnd_node)) {
	            //ignore normal dnd , and dnd from other components
	            if (context.custom == "column_dnd" && $$(html) == this) {
	              if (!this.callEvent("onBeforeColumnDropOrder", [start, id.column, e])) return;
	              var start_index = this.getColumnIndex(start);
	              var end_index = this.getColumnIndex(id.column); //on touch devices we need to preserve drag-start element till the end of dnd

	              if (e.touches) {
	                this._dragTarget = e.target;
	                this._dragTarget.style.display = "none";
	                this.$view.parentNode.appendChild(this._dragTarget);
	              }

	              this.moveColumn(start, end_index + (start_index < end_index ? 1 : 0));
	              this._last_sort_dnd_node = id.column;
	              this._column_dnd_temp_block = true;
	            }
	          }

	          if (id && id.column == start) {
	            //flag prevent flickering just after column move
	            this._column_dnd_temp_block = false;
	          }

	          node.style.display = "block";
	          pos$$1.x = pos$$1.x - this._relative_column_drag.x;
	          pos$$1.y = box.y;
	          if (pos$$1.x < box.x) pos$$1.x = box.x;else {
	            var max = box.x + this.$view.offsetWidth - this._scrollSizeY - this._limit_column_drag;
	            if (pos$$1.x > max) pos$$1.x = max;
	          }
	          DragControl._skip = true;
	        }, this),
	        $dragDestroy: bind(function (a, node) {
	          remove(node); //clean dnd source element

	          if (this._dragTarget) remove(this._dragTarget);
	          var id = DragControl.getContext().start;
	          this.callEvent("onAfterColumnDropOrder", [id.column, this._last_sort_dnd_node, a]);
	        }, this),
	        $drop: function () {}
	      };
	    } else if (value) {
	      control = {
	        _inner_drag_only: true,
	        $drag: bind(function (s, e) {
	          if (!this._isDraggable(e) || this._rs_process) return false;
	          var id = this.locate(e);
	          if (!id || !this.callEvent("onBeforeColumnDrag", [id.column, e])) return false;
	          DragControl._drag_context = {
	            from: control,
	            start: id,
	            custom: "column_dnd"
	          };
	          var header = this.getColumnConfig(id.column).header;
	          var text = "&nbsp;";

	          for (var i = 0; i < header.length; i++) {
	            if (header[i]) {
	              text = header[i].text;
	              break;
	            }
	          }

	          return "<div class='webix_dd_drag_column'>" + text + "</div>";
	        }, this),
	        $drop: bind(function (s, t, e) {
	          var target = e; //on touch devices event doesn't point to the actual drop target

	          if (e.touches && this._drag_column_last) target = this._drag_column_last;
	          var id = this.locate(target);
	          if (!id) return false;
	          var start = DragControl.getContext().start.column;

	          if (start != id.column) {
	            if (!this.callEvent("onBeforeColumnDrop", [start, id.column, e])) return;
	            var start_index = this.getColumnIndex(start);
	            var end_index = this.getColumnIndex(id.column);
	            this.moveColumn(start, end_index + (start_index < end_index ? 1 : 0));
	            this.callEvent("onAfterColumnDrop", [start, id.column, e]);
	          }
	        }, this),
	        $dragIn: bind(function (s, t, e) {
	          var context = DragControl.getContext(); //ignore normal dnd , and dnd from other components

	          if (context.custom != "column_dnd" || context.from != control) return false;
	          var target = e.target || e.srcElement;

	          while ((target.className || "").indexOf("webix_hcell") == -1) {
	            target = target.parentNode;
	            if (!target) return;
	          }

	          if (target != this._drag_column_last) {
	            //new target
	            if (this._drag_column_last) removeCss(this._drag_column_last, "webix_dd_over_column");
	            addCss(target, "webix_dd_over_column");
	          }

	          return this._drag_column_last = target;
	        }, this),
	        $dragDestroy: bind(function (a, h) {
	          if (this._drag_column_last) removeCss(this._drag_column_last, "webix_dd_over_column");
	          remove(h);
	        }, this)
	      };
	    }

	    if (value) {
	      DragControl.addDrag(this._header, control);
	      DragControl.addDrop(this._header, control, true);
	    }
	  }
	};

	var Mixin$b = {
	  clearValidation: function () {
	    for (var i in this.data._marks) {
	      this._clear_invalid_css(i);
	    }

	    this.data.clearMark("webix_invalid", true);
	  },
	  _mark_invalid: function (id, details) {
	    this._clear_invalid_css(id);

	    for (var key in details) {
	      this.addCellCss(id, key, "webix_invalid_cell");
	    }

	    this.addCss(id, "webix_invalid");
	  },
	  _clear_invalid: function (id) {
	    this._clear_invalid_css(id);

	    this.removeCss(id, "webix_invalid");
	  },
	  _clear_invalid_css: function (id) {
	    var mark = this.data.getMark(id, "$cellCss");

	    if (mark) {
	      for (var key in mark) {
	        mark[key] = mark[key].replace("webix_invalid_cell", "").replace("  ", " ");
	      }
	    }
	  },
	  addRowCss: function (id, css, silent) {
	    this.addCss(id, css, silent);
	  },
	  removeRowCss: function (id, css, silent) {
	    this.removeCss(id, css, silent);
	  },
	  addCellCss: function (id, name, css, silent) {
	    var mark = this.data.getMark(id, "$cellCss");
	    var newmark = mark || {};
	    var style = newmark[name] || "";
	    newmark[name] = style.replace(css, "").replace("  ", " ") + " " + css;
	    if (!mark) this.data.addMark(id, "$cellCss", false, newmark, true);
	    if (!silent) this.refresh(id);
	  },
	  removeCellCss: function (id, name, css, silent) {
	    var mark = this.data.getMark(id, "$cellCss");

	    if (mark) {
	      var style = mark[name] || "";
	      if (style) mark[name] = style.replace(css, "").replace("  ", " ");
	      if (!silent) this.refresh(id);
	    }
	  }
	};

	var Mixin$c = {
	  _prePrint: function (options, htmlOnly) {
	    if (options.scroll && !htmlOnly) return true;
	    options.header = isUndefined(options.header) ? this.config.header ? true : false : options.header;
	    options.footer = isUndefined(options.footer) ? this.config.footer ? true : false : options.footer;
	    options.xCorrection = options.xCorrection || 0; //spreadsheet
	  },
	  _findIndex: function (arr, func) {
	    var result = -1;

	    for (var i = 0; result < 0 && i < arr.length; i++) {
	      if (func(arr[i])) result = i;
	    }

	    return result;
	  },
	  _getTableHeader: function (base, columns, group) {
	    var spans = {},
	        start = 0;
	    base.forEach(bind(function (tableArray, tid) {
	      var row = tableArray[0],
	          headerArray = [],
	          length = row.length;
	      row.forEach(bind(function (cell, cid) {
	        var column = columns[cid + start];

	        for (var h = 0; h < column[group].length; h++) {
	          var header = column[group][h];
	          if (!header && !(spans[tid] && spans[tid][h])) continue;
	          header = copy(header || {
	            text: ""
	          });

	          if (spans[tid] && spans[tid][h] && cid === 0) {
	            header.colspan = spans[tid][h];
	            spans[tid][h] = 0;
	          }

	          if (header.colspan) {
	            var colspan = Math.min(header.colspan, length - cid);
	            spans[tid + 1] = spans[tid + 1] || {};
	            spans[tid + 1][h] = header.colspan - colspan;
	            header.colspan = colspan;
	          }

	          if (header.rowspan && length === 1) {
	            header.height = (header.height || this.config.headerRowHeight) * header.rowspan;
	            header.rowspan = null;
	          }

	          var hcell = {
	            txt: header.rotate ? this.getHeaderNode(column.id, h).innerHTML : header.text || (header.contentId ? this.getHeaderContent(header.contentId).getValue() : ""),
	            className: "webix_hcell " + "webix_" + group + "_cell " + (header.css || ""),
	            style: {
	              height: (header.height || this.config.headerRowHeight) + "px",
	              width: header.colspan ? "auto" : column.width + "px"
	            },
	            span: header.colspan || header.rowspan ? {
	              colspan: header.colspan || 1,
	              rowspan: header.rowspan || 1
	            } : null
	          };
	          headerArray[h] = headerArray[h] || [];
	          headerArray[h][cid] = hcell;
	        }
	      }, this));
	      if (group == "header") base[tid] = headerArray.concat(tableArray);else base[tid] = tableArray.concat(headerArray);
	      start += length;
	    }, this));
	    return base;
	  },
	  _getTableArray: function (options, base, start) {
	    var columns = this.config.columns;
	    var sel = this.getSelectedId(true);

	    var maxWidth = this._getPageWidth(options);

	    var rightRestriction = 0;
	    var bottomRestriction = 0;
	    var tableArray = [];
	    var newTableStart = 0;
	    start = start || 0 + options.xCorrection;
	    base = base || [];
	    this.eachRow(bind(function (row) {
	      var width = 0;
	      var rowItem = this.getItem(row);
	      var rowIndex = this.getIndexById(row);
	      var colrow = [];
	      var datarow = false;

	      for (var c = start; c < columns.length; c++) {
	        var column = columns[c].id;
	        var colIndex = this.getColumnIndex(column) - start;

	        if (columns[c]) {
	          width += columns[c].width;

	          if (width > maxWidth && c > start) {
	            // 'c>start' ensures that a single long column will have to fit the page
	            newTableStart = c;
	            break;
	          }

	          if (options.data !== "selection" || options.data == "selection" && this._findIndex(sel, function (obj) {
	            return obj.column == column && obj.row == row;
	          }) !== -1) {
	            var span;
	            if (this.getSpan) span = this.getSpan(row, column); //check span from previous table

	            if (span && this.getColumnIndex(column) === start) {
	              var spanStart = this.getColumnIndex(span[1]);

	              if (spanStart < start) {
	                span[2] = span[2] - (start - spanStart);
	                span[4] = span[4] ? span[4] : rowItem[span[1]] ? this.getText(row, span[1]) : null;
	                span[1] = column;
	              }
	            }

	            if (!span || span && span[0] == row && span[1] == column) {
	              var cellValue = span && span[4] ? span[4] : this._columns_pull[column] ? this.getText(row, column) : "";
	              var className = this.getCss(row, column) + " " + (columns[c].css || "") + (span ? " webix_dtable_span " + (span[5] || "") : "");
	              var style = {
	                height: span && span[3] > 1 ? "auto" : (rowItem.$height || this.config.rowHeight) + "px",
	                width: span && span[2] > 1 ? "auto" : columns[c].width + "px"
	              };
	              colrow.push({
	                txt: cellValue,
	                className: className,
	                style: style,
	                span: span ? {
	                  colspan: span[2],
	                  spanStart: this.getColumnIndex(span[1]),
	                  rowspan: span[3]
	                } : null
	              });

	              if (cellValue) {
	                rightRestriction = Math.max(colIndex + 1, rightRestriction);
	                bottomRestriction = Math.max(rowIndex + 1, bottomRestriction);
	              }

	              datarow = datarow || !!cellValue;
	            } else if (span) {
	              colrow.push({
	                $inspan: true
	              });
	              rightRestriction = Math.max(colIndex + 1, rightRestriction);
	              bottomRestriction = Math.max(rowIndex + 1, bottomRestriction);
	            }
	          }
	        }
	      }

	      if (!options.skiprows || datarow) tableArray.push(colrow);
	    }, this));

	    if (bottomRestriction && rightRestriction) {
	      if (options.trim) {
	        tableArray.length = bottomRestriction;
	        tableArray = tableArray.map(function (item) {
	          for (var i = item.length - 1; i >= 0; i--) {
	            if (item[i].span && item[i].span.colspan) {
	              item[i].span.colspan = Math.min(item[i].span.colspan, item.length - i);
	              break;
	            }
	          }

	          item.length = rightRestriction;
	          return item;
	        });
	      }

	      base.push(tableArray);
	    }

	    if (newTableStart) this._getTableArray(options, base, newTableStart);else {
	      //keep this order as logic relies on the first data row
	      if (options.footer) base = this._getTableHeader(base, columns, "footer");
	      if (options.header) base = this._getTableHeader(base, columns, "header");
	    }
	    return base;
	  },
	  _getTableHTML: function (tableData, options) {
	    var container = create("div");
	    tableData.forEach(bind(function (table, i) {
	      var tableHTML = create("table", {
	        "class": "webix_table_print " + this.$view.className + (options.borderless ? " borderless" : ""),
	        "style": "border-collapse:collapse",
	        "id": this.$view.getAttribute("id")
	      });
	      table.forEach(function (row) {
	        var tr = create("tr");
	        row.forEach(function (cell) {
	          if (!cell.$inspan) {
	            var td = create("td");
	            td.innerHTML = cell.txt;
	            td.className = cell.className;

	            for (var key in cell.style) {
	              td.style[key] = cell.style[key];
	            }

	            if (cell.span) {
	              td.colSpan = cell.span.colspan;
	              td.rowSpan = cell.span.rowspan;
	            }

	            tr.appendChild(td);
	          }
	        });
	        tableHTML.appendChild(tr);
	      });
	      container.appendChild(tableHTML);

	      if (i + 1 < tableData.length) {
	        var br = create("DIV", {
	          "class": "webix_print_pagebreak"
	        });
	        container.appendChild(br);
	      }
	    }, this));
	    return container;
	  }
	};

	var Mixin$d = {
	  $exportView: function (options) {
	    if (this.isBranchOpen) //treetable
	      exports.extend(options, {
	        filterHTML: true
	      });
	    if (options.export_mode !== "excel" || options.dataOnly || !options.styles) return this;else {
	      //excel export with styles
	      options.dataOnly = true;
	      options.heights = isUndefined(options.heights) ? "all" : options.heights;
	      var data = toExcel(this, options);
	      data[0].styles = this._getExportStyles(options);
	      delete options.dataOnly;
	      return data;
	    }
	  },
	  _getExportStyles: function (options) {
	    var columns = this.config.columns,
	        styles = [];
	    this._style_hash = this._style_hash || {};
	    if (options.docHeader) styles = [{
	      0: this._getExportDocStyle(options.docHeader.css)
	    }, {
	      0: {}
	    }];
	    if (options.header !== false) styles = this._getExportHStyles(options, "header", styles);
	    this.data.each(function (obj) {
	      var row = {};

	      for (var i = 0; i < columns.length; i++) {
	        var cellCss = this.getCss(obj.id, columns[i].id);
	        var columnCss = columns[i].node.className;
	        var spanCss = "";
	        var evenCss = this.getIndexById(obj.id) % 2 ? "even" : "odd"; //for skins like metro, web, air

	        var span = null;
	        var node = null;

	        if (this._spans_pull && (span = this.getSpan(obj.id, columns[i].id))) {
	          node = this.getSpanNode({
	            row: span[0],
	            column: span[1]
	          });
	          spanCss = "webix_dtable_span " + (span[5] || "");
	        } else node = this.getItemNode({
	          row: obj.id,
	          column: columns[i].id
	        });

	        if (!node) {
	          node = create("div", {
	            "class": cellCss,
	            style: "visibility:hidden"
	          });
	          var cnode = columns[i].node;

	          if (!columns[i].attached) {
	            cnode = create("div", {
	              "class": columnCss,
	              style: "visibility:hidden"
	            });

	            this._body.appendChild(cnode);
	          }

	          cnode.appendChild(node);
	        }

	        row[i] = this._getExportCellStyle(node, [cellCss, columnCss, spanCss, evenCss].join(":"));
	      }

	      styles[styles.length] = row;
	    }, this);
	    if (options.footer !== false && this.config.footer) styles = this._getExportHStyles(options, "footer", styles);
	    if (options.docFooter) styles = styles.concat([{
	      0: {}
	    }, {
	      0: this._getExportDocStyle(options.docFooter.css)
	    }]);
	    return styles;
	  },
	  _getExportHStyles: function (options, group, styles) {
	    var columns = this.config.columns,
	        hs = []; //spans

	    for (var h = 0; h < columns[0][group].length; h++) {
	      var hrow = {};

	      for (var i = 0; i < columns.length; i++) {
	        var header = columns[i][group][h]; //ToDo:make sure it is rendered and attached

	        if (header) {
	          //can be null
	          var cid = header.colspan ? columns[i + header.colspan - 1].id : columns[i].id;
	          var node = group == "header" ? this.getHeaderNode(cid, h) : this.getFooterNode(cid, h);

	          if (node) {
	            var name = [node.parentNode.className, header.css || "", "webix_hcell", group];
	            hrow[i] = this._getExportCellStyle(node, name.join(":"));
	            if (header.colspan || header.rowspan) hs.push([h, i, {
	              colspan: header.colspan - 1 || 0,
	              rowspan: header.rowspan - 1 || 0
	            }, hrow[i]]);
	          }
	        } else {
	          for (var s = 0; s < hs.length; s++) {
	            var st = hs[s][2],
	                hsc = hs[s][1],
	                hsr = hs[s][0];
	            if (hsc + st.colspan >= i && hsr + st.rowspan >= h) hrow[i] = hs[s][3];
	          }
	        }
	      }

	      styles[styles.length] = hrow;
	    }

	    return styles;
	  },
	  _getExportCellStyle: function (node, name) {
	    if (this._style_hash[name]) return this._style_hash[name];else {
	      var base = this._getRules(node);

	      var rules = {
	        font: {},
	        alignment: {},
	        border: {}
	      }; //font

	      rules.font.name = base["font-family"].replace(/,.*$/, ""); // cut off fallback font;

	      rules.font.sz = base["font-size"].replace("px", "") * 0.75; //px to pt conversion

	      rules.font.color = {
	        rgb: color.rgbToHex(base["color"])
	      };
	      if (base["font-weight"] !== "normal") rules.font.bold = true;
	      if (base["text-decoration-line"] === "underline") rules.font.underline = true;
	      if (base["font-style"] === "italic") rules.font.italic = true;
	      if (base["text-decoration-line"] === "line-through") rules.font.strike = true; //alignment

	      rules.alignment.horizontal = base["text-align"];
	      rules.alignment.vertical = base["height"] == base["line-height"] ? "center" : "top";
	      if (base["white-space"] == "normal") rules.alignment.wrapText = true; //rotated header

	      if (node.firstChild && node.firstChild.className && node.firstChild.className.indexOf("webix_rotate") !== -1) rules.alignment.textRotation = 90; //background

	      var bg = color.rgbToHex(base["background-color"]) || "FFFFFF";
	      rules.fill = {
	        fgColor: {
	          rgb: bg
	        }
	      };
	      if (base["background-image"].indexOf("gradient") !== -1) //air skins use gradient for header
	        rules.fill = {
	          fgColor: {
	            rgb: color.rgbToHex(base["background-image"].substring(base["background-image"].lastIndexOf("(")))
	          }
	        }; //borders

	      if (node.parentNode && node.parentNode.nodeName == "TD") //borders for header are set for parent td, so we change base rules here
	        base = this._getRules(node.parentNode);
	      if (base["border-right-width"] !== "0px") rules.border.right = {
	        style: "thin",
	        color: {
	          rgb: color.rgbToHex(base["border-right-color"]) || bg
	        }
	      };
	      if (base["border-bottom-width"] !== "0px") rules.border.bottom = {
	        style: "thin",
	        color: {
	          rgb: color.rgbToHex(base["border-bottom-color"]) || bg
	        }
	      };
	      if (base["border-left-width"] !== "0px") rules.border.left = {
	        style: "thin",
	        color: {
	          rgb: color.rgbToHex(base["border-left-color"]) || bg
	        }
	      };
	      if (base["border-top-width"] !== "0px") rules.border.top = {
	        style: "thin",
	        color: {
	          rgb: color.rgbToHex(base["border-top-color"]) || bg
	        }
	      };
	      this._style_hash[name] = rules;
	      return rules;
	    }
	  },
	  _getExportDocStyle: function (css) {
	    css = exports.extend(css || {}, {
	      visibility: "hidden",
	      "white-space": "nowrap",
	      "text-align": "left"
	    });
	    var cssStr = "";

	    for (var i in css) {
	      cssStr += i + ":" + css[i] + ";";
	    }

	    var node = create("div", {
	      style: cssStr
	    });

	    this._body.appendChild(node);

	    var style = this._getExportCellStyle(node, cssStr);

	    remove(node);
	    return style;
	  },
	  _getRules: function (node) {
	    var style = {};
	    if (window.getComputedStyle) style = window.getComputedStyle(node);else style = node.currentStyle;
	    return style;
	  }
	};

	var temp$5 = {};

	var temp$6 = {};

	var temp$7 = {};

	var api$1d = {
	  name: "datatable",
	  defaults: {
	    leftSplit: 0,
	    rightSplit: 0,
	    topSplit: 0,
	    columnWidth: 100,
	    minColumnWidth: 20,
	    minColumnHeight: 26,
	    prerender: false,
	    autoheight: false,
	    autowidth: false,
	    header: true,
	    fixedRowHeight: true,
	    scrollAlignY: true,
	    scrollX: true,
	    scrollY: true,
	    datafetch: 50,
	    navigation: true
	  },
	  $skin: function () {
	    this.defaults.rowHeight = $active.rowHeight;
	    this.defaults.headerRowHeight = $active.barHeight - $active.borderWidth * 2;
	  },
	  on_click: {
	    webix_richfilter: function () {
	      return false;
	    },
	    webix_table_checkbox: function (e, id) {
	      id = this.locate(e);
	      var item = this.getItem(id.row);
	      var col = this.getColumnConfig(id.column);
	      var trg = e.target || e.srcElement; //read actual value from HTML tag when possible
	      //as it can be affected by dbl-clicks

	      var check = trg.type == "checkbox" ? trg.checked : item[id.column] != col.checkValue;
	      var value = check ? col.checkValue : col.uncheckValue;
	      var update = {};
	      update[id.column] = value;
	      this.updateItem(id.row, update, this._settings.checkboxRefresh ? "update" : "save");
	      this.callEvent("onCheck", [id.row, id.column, value]);
	      return false;
	    },
	    webix_table_radio: function (e) {
	      var id = this.locate(e);
	      var item = this.getItem(id.row);
	      var col = this.getColumnConfig(id.column);
	      this.eachRow(function (rowid) {
	        var item = this.data.pull[rowid];
	        if (item && item[id.column] == col.checkValue) item[id.column] = col.uncheckValue;
	      });
	      item[id.column] = col.checkValue;
	      this.callEvent("onCheck", [id.row, id.column, true]);
	      this.refresh();
	      return false;
	    }
	  },
	  on_dblclick: {
	    webix_table_checkbox: function () {
	      return this.on_click.webix_table_checkbox.apply(this, arguments);
	    }
	  },
	  on_context: {},
	  $init: function (config) {
	    this.on_click = exports.extend({}, this.on_click);
	    var html = "<div class='webix_ss_header'><div class='webix_hs_left'></div><div class='webix_hs_center'></div><div class='webix_hs_right'></div></div><div class='webix_ss_body'><div class='webix_ss_left'><div class='webix_ss_center_scroll'></div></div>";
	    html += "<div class='webix_ss_center'><div class='webix_ss_center_scroll' role='rowgroup'></div></div>";
	    html += "<div class='webix_ss_right'><div class='webix_ss_center_scroll'></div></div></div>";
	    html += "<div class='webix_ss_hscroll' role='scrollbar' aria-orientation='horizontal'></div><div class='webix_ss_footer'><div class='webix_hs_left'></div><div class='webix_hs_center'></div><div class='webix_hs_right'></div></div><div class='webix_ss_vscroll_header'></div><div class='webix_ss_vscroll' role='scrollbar' aria-orientation='vertical'></div><div class='webix_ss_vscroll_footer'></div>";
	    this._contentobj.innerHTML = html;
	    this._top_id = this._contentobj.id = this.name + uid();
	    this._contentobj.className += " webix_dtable";
	    this._dataobj = this._contentobj;
	    this._header = this._contentobj.firstChild;
	    this._body = this._header.nextSibling;
	    this._footer = this._body.nextSibling.nextSibling;

	    this._viewobj.setAttribute("role", "grid");

	    if (!config.editable) this._viewobj.setAttribute("aria-readonly", "true");
	    this.data.provideApi(this, true);
	    this.data.attachEvent("onParse", bind(this._call_onparse, this));
	    this.$ready.push(this._first_render);
	    this._columns = [];
	    this._hidden_column_order = [];
	    this._headers = [];
	    this._footers = [];
	    this._rows_cache = [];
	    this._active_headers = {};
	    this._filter_elements = {};
	    this._header_height = this._footer_height = 0; //component can create new view

	    this._destroy_with_me = [];
	    this.data.attachEvent("onServerConfig", bind(this._config_table_from_file, this));
	    this.data.attachEvent("onServerOptions", bind(this._config_options_from_file, this));
	    this.attachEvent("onViewShow", function () {
	      this._restore_scroll_state();

	      this._refresh_any_header_content();
	    });
	    this.data.attachEvent("onClearAll", bind(function (soft) {
	      if (!soft) {
	        this._scrollLeft = this._scrollTop = 0;
	        if (this._x_scroll) this._x_scroll.reset();
	        if (this._y_scroll) this._y_scroll.reset();

	        this._setLeftScroll(0);
	      }
	    }, this));
	    this.attachEvent("onDestruct", this._clean_config_struct);
	    this.attachEvent("onKeyPress", this._onKeyPress);
	    this.attachEvent("onScrollY", this._adjust_rows);
	    callEvent("onDataTable", [this, config]);
	  },
	  _render_initial: function () {
	    this._scrollSizeX = this._scrollSizeY = env.scrollSize;
	    addStyle("#" + this._top_id + " .webix_cell { height:" + this._settings.rowHeight + "px; line-height:" + (this._settings.rowLineHeight || this._settings.rowHeight) + "px;" + (this._settings.fixedRowHeight ? "" : "white-space:normal;") + " }");
	    addStyle("#" + this._top_id + " .webix_hcell { height:" + this._settings.headerRowHeight + "px; line-height:" + this._settings.headerRowHeight + "px;}");

	    this._render_initial = function () {};
	  },
	  _first_render: function () {
	    this.data.attachEvent("onStoreLoad", bind(this._refresh_any_header_content, this));
	    this.data.attachEvent("onSyncApply", bind(this._refresh_any_header_content, this));
	    this.data.attachEvent("onStoreUpdated", bind(function () {
	      return this.render.apply(this, arguments);
	    }, this));
	    this.data.attachEvent("onStoreUpdated", bind(this._refresh_tracking_header_content, this));
	    this.render();
	  },
	  refresh: function () {
	    this.render();
	  },
	  render: function (id, data, mode) {
	    //pure data saving call
	    if (mode == "save") return; //during dnd we must not repaint anything in mobile webkit

	    if (mode == "move") {
	      var context = DragControl.getContext();
	      if (context && context.fragile) return;
	    }

	    if (!this._columns.length) {
	      var cols = this._settings.columns;

	      if (!cols || !cols.length) {
	        if (this._settings.autoConfig && this.data.order.length && !this._hidden_column_order.length) {
	          this._dtable_fully_ready = 0;

	          this._autoDetectConfig();
	        } else return;
	      }

	      this._define_structure();
	    }

	    if (!this.isVisible(this._settings.id) || this.$blockRender) return this._render_initial(); //Chrome 34, Custom Font loading bug

	    var fast_mode = this.config.experimental && !this._settings.subview; //replace multiple atomic updates by single big repaint

	    if (id && data != -1 && (mode == "paint" || mode == "update") && !fast_mode) {
	      if (this._render_timer) clearTimeout(this._render_timer);

	      if (!this._render_timer || this._render_timer_id == id) {
	        this._render_timer_id = id;
	        this._render_timer = delay(function () {
	          //if only one call - repaint single item
	          this.render(id, -1, mode);
	        }, this);
	      } else {
	        this._render_timer_id = null;
	        this._render_timer = delay(function () {
	          //if ther was a serie of calls - replace them with single full repaint
	          this.render();
	        }, this);
	      }

	      return;
	    } else if (this._render_timer) {
	      clearTimeout(this._render_timer);
	      this._render_timer = 0;
	    }

	    if (this.callEvent("onBeforeRender", [this.data])) {
	      this._render_initial();

	      if (!this._dtable_fully_ready) this._apply_headers();

	      if (this._content_width) {
	        if (fast_mode && (mode == "paint" || mode == "update") && id) this._repaint_single_row(id);else this._check_rendered_cols(true, true);
	      }

	      if (!id || mode != "update") {
	        this._dtable_height = this._get_total_height();

	        this._set_split_sizes_y();
	      } //don't depend on hidden rows/rolumns


	      this._viewobj.setAttribute("aria-colcount", Math.max(this._hidden_column_order.length, this._columns.length));

	      this._viewobj.setAttribute("aria-rowcount", this.data.count());

	      this.callEvent("onAfterRender", [this.data]);
	      return true;
	    }
	  },
	  getColumnConfig: function (id) {
	    return this._columns_pull[id] || this._hidden_column_hash[id];
	  },
	  _config_options_from_file: function (colls) {
	    for (var key in colls) {
	      var column = this.getColumnConfig(key);
	      assert(column, "Orphan collection: " + key);
	      var temp = new DataCollection({
	        data: colls[key]
	      });

	      this._destroy_with_me.push(temp);

	      this._bind_collection(temp, column);
	    }
	  },
	  //xml has different configuration structure, fixing
	  _config_table_from_file: function (config) {
	    if (config.columns && this._dtable_fully_ready) this.refreshColumns(null, true);
	  },
	  _define_structure: function () {
	    if (this._settings.columns) {
	      this._columns = this._settings.columns;
	      this._columns_pull = {};

	      for (var i = 0; i < this._columns.length; i++) {
	        var col = this._columns[i];
	        this._columns_pull[col.id] = col;
	        var format = col.cssFormat;
	        if (format) col.cssFormat = toFunctor(format, this.$scope);
	        col.width = col.width || this._settings.columnWidth;
	        if (typeof col.format == "string") col.format = i18n[col.format] || window[col.format];

	        if (col.numberFormat) {
	          var nformat = col.numberFormat;
	          if (typeof nformat === "string") col.numberFormat = nformat = Number$1.getConfig(nformat);
	          col.format = Number$1.numToStr(nformat);

	          col.editFormat = col.editFormat || function (val) {
	            return Number$1.format(val, nformat);
	          };

	          col.editParse = col.editParse || function (val) {
	            return Number$1.parse(val, nformat);
	          };
	        } //default settings for checkboxes and radios


	        if (isUndefined(col.checkValue)) col.checkValue = 1;
	        if (isUndefined(col.uncheckValue)) col.uncheckValue = 0;
	        if (col.css && _typeof(col.css) == "object") col.css = createCss(col.css);
	        var rawTemplate = col.template;

	        if (rawTemplate) {
	          if (typeof rawTemplate == "string") rawTemplate = rawTemplate.replace(/#\$value#/g, "#" + col.id + "#");
	          col.template = template(rawTemplate);
	        }
	      }

	      this._normalize_headers("header", this._headers);

	      this._normalize_headers("footer", this._footers);

	      this.callEvent("onStructureLoad", []);
	    }
	  },
	  _define_structure_and_render: function () {
	    this._apply_headers();
	  },
	  _clean_config_struct: function () {
	    //remove column technical info from the column
	    //it allows to reuse the same config object for new grid
	    for (var i = 0; i < this._columns.length; i++) {
	      delete this._columns[i].attached;
	      delete this._columns[i].node;
	    }
	  },
	  _apply_headers: function () {
	    this._rightSplit = this._columns.length - this._settings.rightSplit;
	    this._dtable_width = 0;

	    for (var i = 0; i < this._columns.length; i++) {
	      if (!this._columns[i].node) {
	        var temp = create("DIV");
	        temp.style.width = this._columns[i].width + "px";
	        this._columns[i].node = temp;
	      }

	      if (i >= this._settings.leftSplit && i < this._rightSplit) this._dtable_width += this._columns[i].width;
	    }

	    var marks = [];

	    if (this._settings.rightSplit) {
	      var nr = this._columns.length - this._settings.rightSplit;
	      marks[nr] = " webix_first";
	      marks[nr - 1] = " webix_last";
	    }

	    if (this._settings.leftSplit) {
	      var nl = this._settings.leftSplit;
	      marks[nl] = " webix_first";
	      marks[nl - 1] = " webix_last";
	    }

	    marks[0] = (marks[0] || "") + " webix_first webix_select_mark";
	    var last_index = this._columns.length - 1;
	    marks[last_index] = (marks[last_index] || "") + " webix_last";

	    for (var _i = 0; _i < this._columns.length; _i++) {
	      var node = this._columns[_i].node;
	      node.setAttribute(
	      /*@attr*/
	      "column", _i);
	      node.className = "webix_column " + (this._columns[_i].css || "") + (marks[_i] || "");
	    }

	    this._create_scrolls();

	    this._set_columns_positions();

	    this._set_split_sizes_x();

	    this._render_header_and_footer();

	    this._dtable_fully_ready = true;
	  },
	  _set_columns_positions: function () {
	    var left = 0;

	    for (var i = 0; i < this._columns.length; i++) {
	      var column = this._columns[i];
	      if (i == this._settings.leftSplit || i == this._rightSplit) left = 0;

	      if (column.node) {
	        column.node.style.left = left + "px";

	        if (this._settings.leftSplit || this._settings.rightSplit) {
	          remove(column.node);
	          column.attached = false;
	        }
	      }

	      left += column.width;
	    }
	  },
	  _render_header_and_footer: function () {
	    if (!this._header_fix_width) this._header_fix_width = 0;
	    this._header_height = this._footer_height = 0;

	    if (this._settings.header) {
	      this._refreshHeaderContent(this._header, 0, 1);

	      this._normalize_headers("header", this._headers);

	      this._header_height = this._headers._summ;

	      this._render_header_section(this._header, "header", this._headers);
	    }

	    if (this._settings.footer) {
	      this._refreshHeaderContent(this._footer, 0, 1);

	      this._normalize_headers("footer", this._footers);

	      this._footer_height = this._footers._summ;

	      this._render_header_section(this._footer, "footer", this._footers);
	    }

	    this.refreshHeaderContent(false, false);

	    this._size_header_footer_fix();

	    if (this._last_sorted) this.markSorting(this._last_sorted, this._last_order);
	  },
	  _getHeaderHeight: function (header, column, ind) {
	    var width = 0;
	    var colspan = header.colspan || 1;
	    var css = "webix_hcell " + (header.css || "");
	    if (header.rotate) css += " webix_measure_rotate";else for (var i = 0; i < colspan; i++) {
	      width += this._columns[ind + i] ? this._columns[ind + i].width : this.config.columnWidth;
	    }
	    var size = getTextSize([header.text], css, width); //+1 to compensate for scrollHeight rounding

	    return (header.rotate ? size.width : size.height) + 1;
	  },
	  _normalize_headers: function (collection, heights) {
	    var rows = 0;

	    for (var i = 0; i < this._columns.length; i++) {
	      var data = this._columns[i][collection];

	      if (!data || _typeof(data) != "object" || !data.length) {
	        if (isUndefined(data)) {
	          if (collection == "header") data = this._columns[i].id;else data = "";
	        }

	        data = [data];
	      }

	      for (var j = 0; j < data.length; j++) {
	        if (_typeof(data[j]) != "object") data[j] = {
	          text: data[j]
	        };
	        if (data[j] && data[j].height) heights[j] = data[j].height;
	        if (data[j] && data[j].autoheight) heights[j] = this._getHeaderHeight(data[j], this._columns[i], i);
	      }

	      rows = Math.max(rows, data.length);
	      this._columns[i][collection] = data;
	    }

	    heights._summ = rows;

	    for (var _i2 = rows - 1; _i2 >= 0; _i2--) {
	      heights[_i2] = heights[_i2] || this._settings.headerRowHeight;
	      heights._summ += heights[_i2] * 1;
	    } //set null to cells included in col|row spans


	    for (var _i3 = 0; _i3 < this._columns.length; _i3++) {
	      var col = this._columns[_i3][collection];

	      for (var _j = 0; _j < col.length; _j++) {
	        if (col[_j] && col[_j].rowspan) for (var z = 1; z < col[_j].rowspan; z++) {
	          col[_j + z] = null;
	        }
	        if (col[_j] && col[_j].colspan) for (var _z = 1; _z < col[_j].colspan; _z++) {
	          this._columns[_i3 + _z][collection][_j] = null;
	        }
	      }
	    } //auto-rowspan cells, which has not enough header lines


	    for (var _i4 = 0; _i4 < this._columns.length; _i4++) {
	      var _data = this._columns[_i4][collection];

	      if (_data.length < rows) {
	        var end = _data.length - 1;
	        _data[end].rowspan = rows - _data.length + 1;

	        for (var _j2 = end + 1; _j2 < rows; _j2++) {
	          _data[_j2] = null;
	        }
	      }
	    }

	    return rows;
	  },
	  _find_header_content: function (sec, id) {
	    var alltd = sec.getElementsByTagName("TD");

	    for (var i = 0; i < alltd.length; i++) {
	      if (alltd[i].getAttribute(
	      /*@attr*/
	      "active_id") == id) return alltd[i];
	    }
	  },
	  getHeaderContent: function (id) {
	    var obj = this._find_header_content(this._header, id);

	    if (!obj) obj = this._find_header_content(this._footer, id);

	    if (obj) {
	      var config = this._active_headers[id];
	      var type$$1 = datafilter[config.content];
	      var _base = {
	        type: type$$1,
	        getValue: function (text) {
	          return type$$1.getValue(obj, text);
	        },
	        setValue: function (value) {
	          return type$$1.setValue(obj, value);
	        }
	      };
	      if (type$$1.getHelper) exports.extend(_base, type$$1.getHelper(obj, config));
	      return _base;
	    }
	  },
	  _summ_next: function (heights, start, i) {
	    var summ = i ? -1 : 0;
	    i += start;

	    for (start; start < i; start++) {
	      summ += heights[start] + 1;
	    }

	    return summ;
	  },
	  _render_subheader: function (start, end, width, name, heights) {
	    if (start == end) return "";
	    var html = "<table role='presentation' style='width:" + width + "px' cellspacing='0' cellpadding='0'>";
	    html += "<tr class='webix_size_row'>";

	    for (var i = start; i < end; i++) {
	      html += "<td style='width:" + this._columns[i].width + "px;'></td>";
	    }

	    html += "</tr>";
	    var count = this._columns[0][name].length;

	    for (var j = 0; j < count; j++) {
	      html += "<tr " +
	      /*@attr*/
	      "section" + "='" + name + "' role='row'>";

	      for (var _i5 = start; _i5 < end; _i5++) {
	        var header = this._columns[_i5][name][j];
	        if (header === null) continue;

	        if (header.content) {
	          header.contentId = header.contentId || uid();
	          header.columnId = this._columns[_i5].id;
	          header.format = this._columns[_i5].format;
	          assert(datafilter, "Filtering extension was not included");
	          assert(datafilter[header.content], "Unknown content type: " + header.content);
	          header.text = datafilter[header.content].render(this, header);
	          this._active_headers[header.contentId] = header;
	          this._has_active_headers = true;
	        }

	        html += "<td  role='presentation' " +
	        /*@attr*/
	        "column" + "='" + (header.colspan ? header.colspan - 1 + _i5 : _i5) + "'";
	        var hcss = "";
	        if (_i5 == start) hcss += "webix_first";
	        var column_pos = _i5 + (header.colspan ? header.colspan - 1 : 0);
	        if (column_pos >= end - 1) hcss += " webix_last";
	        if (header.rowspan && j + header.rowspan === count || j === count - 1) hcss += " webix_last_row";
	        if (hcss) html += " class=\"" + hcss + "\"";
	        var cell_height = heights[j];
	        var sheight = "";
	        if (header.contentId) html += " " +
	        /*@attr*/
	        "active_id" + "='" + header.contentId + "'";
	        if (header.colspan) html += " colspan='" + header.colspan + "'";

	        if (header.rowspan) {
	          html += " rowspan='" + header.rowspan + "'";
	          cell_height = this._summ_next(this._headers, j, header.rowspan);
	        }

	        if (cell_height != this._settings.headerRowHeight) sheight = " style='line-height:" + cell_height + "px; height:" + cell_height + "px;'";
	        var css = "webix_hcell";
	        var header_css = header.css;

	        if (header_css) {
	          if (_typeof(header_css) == "object") header.css = header_css = createCss(header_css);
	          css += " " + header_css;
	        }

	        if (this._columns[_i5].$selected) css += " webix_sel_hcell";
	        html += "><div role='columnheader' class='" + css + "'" + sheight + ">";
	        var text = header.text === "" ? "&nbsp;" : header.text;
	        if (header.rotate) text = "<div class='webix_rotate' style='width:" + (cell_height - 10) + "px; transform-origin:center " + (cell_height - 15) / 2 + "px;-webkit-transform-origin:center " + (cell_height - 15) / 2 + "px;'>" + text + "</div>";
	        html += text + "</div></td>";
	      }

	      html += "</tr>";
	    }

	    html += "</tr></table>";
	    return html;
	  },
	  showItemByIndex: function (row_ind, column_ind) {
	    var pager = this._settings.pager;

	    if (pager) {
	      var target = Math.floor(row_ind / pager.size);
	      if (target != pager.page) $$(pager.id).select(target);
	    } //parameter will be set to -1, to mark that scroll need not to be adjusted


	    var scroll = this.getScrollState();

	    if (row_ind != -1) {
	      var state = this._get_y_range();

	      if (row_ind < state[0] + 1 || row_ind >= state[1] - 1) {
	        //not visible currently
	        var summ = this._getHeightByIndexSumm(pager ? this.data.$min : 0, row_ind);

	        var itemHeight = this._getHeightByIndex(row_ind);

	        if (row_ind < state[0] + 1) {
	          //scroll top - show row at top of screen
	          summ = Math.max(0, summ - 1) - this._top_split_height;
	        } else if (summ + itemHeight > this._dtable_offset_height) {
	          //scroll bottom - show row at bottom of screen
	          summ += itemHeight - this._dtable_offset_height; //because of row rounding we neet to scroll some extra
	          //TODO: create a better heuristic

	          if (row_ind > 0) summ += this._getHeightByIndex(row_ind - 1) - 1;
	        }

	        scroll.y = summ;
	      }
	    }

	    if (column_ind != -1) {
	      //ignore split columns - they are always visible
	      if (column_ind < this._settings.leftSplit) return;
	      if (column_ind >= this._rightSplit) return; //very similar to y-logic above

	      var _state = this._get_x_range();

	      if (column_ind < _state[0] + 1 || column_ind >= _state[1] - 1) {
	        //not visible currently
	        var _summ = 0;

	        for (var i = this._settings.leftSplit; i < column_ind; i++) {
	          _summ += this._columns[i].width;
	        }
	        /*jsl:ignore*/


	        if (column_ind < _state[0] + 1) ; else {
	          //scroll to right border
	          _summ += this._columns[column_ind].width - this._center_width;
	        }
	        /*jsl:end*/


	        scroll.x = _summ;
	      }
	    }

	    this.scrollTo(scroll.x, scroll.y);
	  },
	  showCell: function (row, column) {
	    if (!column || !row) {
	      //if column or row not provided - take from current selection
	      var t = this.getSelectedId(true);

	      if (t.length == 1) {
	        column = column || t[0].column;
	        row = row || t[0].row;
	      }
	    } //convert id to index


	    column = column ? this.getColumnIndex(column) : -1;
	    row = row ? this.getIndexById(row) : -1;
	    this.showItemByIndex(row, column);
	  },
	  scrollTo: function (x, y) {
	    if (!this._x_scroll) return;
	    if (this._scrollTo_touch) return this._scrollTo_touch(x, y);
	    if (x !== null) this._x_scroll.scrollTo(x);
	    if (y !== null) this._y_scroll.scrollTo(y);
	  },
	  getScrollState: function () {
	    if (this._getScrollState_touch) return this._getScrollState_touch();
	    var diff = this._render_scroll_shift ? 0 : this._render_scroll_diff || 0;
	    return {
	      x: this._scrollLeft || 0,
	      y: this._scrollTop + diff
	    };
	  },
	  showItem: function (id) {
	    this.showItemByIndex(this.getIndexById(id), -1);
	  },
	  _render_header_section: function (sec, name, heights) {
	    var header = sec.childNodes;
	    header[0].innerHTML = this._render_subheader(0, this._settings.leftSplit, this._left_width, name, heights);
	    header[1].innerHTML = this._render_subheader(this._settings.leftSplit, this._rightSplit, this._dtable_width, name, heights);
	    header[2].innerHTML = this._render_subheader(this._rightSplit, this._columns.length, this._right_width, name, heights);
	    if (this._dtable_column_refresh) header[1].scrollLeft = this.getScrollState().x;
	    header[1].onscroll = bind(this._scroll_with_header, this);
	  },
	  _scroll_with_header: function () {
	    var active = this.getScrollState().x;
	    var header = this._header.childNodes[1].scrollLeft;
	    if (header != active) this.scrollTo(header, null);
	  },
	  _refresh_tracking_header_content: function () {
	    this.refreshHeaderContent(true, true);
	  },
	  _refresh_any_header_content: function () {
	    this.refreshHeaderContent(false, true);
	  },
	  //[DEPRECATE] - v3.0, move to private
	  refreshHeaderContent: function (trackedOnly, preserve, id) {
	    if (this._settings.header) {
	      if (preserve) this._refreshHeaderContent(this._header, trackedOnly, 1, id);

	      this._refreshHeaderContent(this._header, trackedOnly, 0, id);
	    }

	    if (this._settings.footer) {
	      if (preserve) this._refreshHeaderContent(this._footer, trackedOnly, 1, id);

	      this._refreshHeaderContent(this._footer, trackedOnly, 0, id);
	    }
	  },
	  refreshFilter: function (id) {
	    this.refreshHeaderContent(false, true, id);
	  },
	  _refreshHeaderContent: function (sec, cellTrackOnly, getOnly, byId) {
	    if (this._has_active_headers && sec) {
	      var alltd = sec.getElementsByTagName("TD");

	      for (var i = 0; i < alltd.length; i++) {
	        if (alltd[i].getAttribute(
	        /*@attr*/
	        "active_id")) {
	          var obj = this._active_headers[alltd[i].getAttribute(
	          /*@attr*/
	          "active_id")];

	          if (byId && byId != obj.columnId) continue;
	          var content = datafilter[obj.content];

	          if (getOnly) {
	            if (content.getValue) {
	              obj.value = content.getValue(alltd[i]);
	            }
	          } else if (!cellTrackOnly || content.trackCells) {
	            content.refresh(this, alltd[i], obj);
	          }
	        }
	      }
	    }
	  },
	  headerContent: [],
	  _set_size_scroll_area: function (obj, height, hdx) {
	    if (this._scrollSizeY) {
	      obj.style.height = Math.max(height, 1) - 1 + "px";
	      obj.style.width = (this._rightSplit ? 0 : hdx) + this._scrollSizeY - 1 + "px"; // temp. fix: Chrome [DIRTY]

	      if (env.isWebKit) var w = obj.offsetWidth; //eslint-disable-line
	    } else obj.style.display = "none";
	  },
	  _size_header_footer_fix: function () {
	    if (this._settings.header) this._set_size_scroll_area(this._header_scroll, this._header_height, this._header_fix_width);
	    if (this._settings.footer) this._set_size_scroll_area(this._footer_scroll, this._footer_height, this._header_fix_width);
	  },
	  _update_scroll: function () {
	    var hasX = !(this._settings.autowidth || this._settings.scrollX === false);
	    this._scrollSizeX = hasX ? env.scrollSize : 0;
	    var hasY = !(this._settings.autoheight || this._settings.scrollY === false);
	    this._scrollSizeY = hasY ? env.scrollSize : 0;
	    if (env.touch) hasX = hasY = false;

	    if (this._x_scroll) {
	      this._x_scroll._settings.scrollSize = this._scrollSizeX;
	      this._x_scroll._settings.scrollVisible = hasX;
	    }

	    if (this._y_scroll) {
	      this._y_scroll._settings.scrollSize = this._scrollSizeY;
	      this._y_scroll._settings.scrollVisible = hasY;
	    }
	  },
	  _create_scrolls: function () {
	    this._scrollTop = 0;
	    this._scrollLeft = 0;
	    var scrx, scry;
	    scrx = scry = 1;
	    if (this._settings.autoheight || this._settings.scrollY === false) scry = this._scrollSizeY = 0;
	    if (this._settings.autowidth || this._settings.scrollX === false) scrx = this._scrollSizeX = 0;
	    if (env.touch) scrx = scry = 0;

	    if (!this._x_scroll) {
	      this._x_scroll = ui({
	        view: "vscroll",
	        container: this._footer.previousSibling,
	        scrollWidth: this._dtable_width,
	        scrollSize: this._scrollSizeX,
	        scrollVisible: scrx
	      }); //fix for scroll space on Mac

	      if (scrx && !this._scrollSizeX && !env.$customScroll) this._x_scroll._viewobj.style.position = "absolute";

	      this._x_scroll.attachEvent("onScroll", bind(this._onscroll_x, this));
	    }

	    if (!this._y_scroll) {
	      this._header_scroll = this._footer.nextSibling;
	      var vscroll_view = this._header_scroll.nextSibling;
	      this._footer_scroll = vscroll_view.nextSibling;
	      this._y_scroll = ui({
	        view: "vscroll",
	        container: vscroll_view,
	        scrollHeight: 100,
	        scroll: "y",
	        scrollSize: this._scrollSizeY,
	        scrollVisible: scry
	      });

	      this._y_scroll.activeArea(this._body);

	      this._x_scroll.activeArea(this._body, true);

	      this._y_scroll.attachEvent("onScroll", bind(this._onscroll_y, this));
	    }

	    if (this._content_width) this.callEvent("onResize", [this._content_width, this._content_height]);
	    if (env.$customScroll) temp$1.enable(this);

	    this._create_scrolls = function () {};
	  },
	  columnId: function (index$$1) {
	    return this._columns[index$$1].id;
	  },
	  getColumnIndex: function (id) {
	    for (var i = 0; i < this._columns.length; i++) {
	      if (this._columns[i].id == id) return i;
	    }

	    return -1;
	  },
	  _getNodeBox: function (rid, cid) {
	    var xs = 0,
	        xe = 0,
	        ye = 0,
	        ys = 0;
	    var i;
	    var zone = 0;

	    for (i = 0; i < this._columns.length; i++) {
	      if (this._rightSplit == i || this._settings.leftSplit == i) {
	        xs = 0;
	        zone++;
	      }

	      if (this._columns[i].id == cid) break;
	      xs += this._columns[i].width;
	    }

	    xe += this._columns[i].width;

	    for (i = 0; i < this.data.order.length; i++) {
	      if (this.data.order[i] == rid) break;
	      ys += this._getHeightByIndex(i);
	    }

	    ye += this._getHeightByIndex(i);
	    return [xs, xe, ys - this._scrollTop, ye, this._body.childNodes[zone]];
	  },
	  _id_to_string: function () {
	    return this.row;
	  },
	  locate: function (node, idOnly) {
	    if (this._settings.subview && this != $$(node)) return null;
	    node = node.target || node.srcElement || node;

	    while (node && node.getAttribute) {
	      if (node === this.$view) break;

	      var cs = _getClassName(node).toString();

	      var pos$$1 = null;

	      if (cs.indexOf("webix_cell") != -1) {
	        pos$$1 = this._locate(node);
	        if (pos$$1) pos$$1.row = this.data.order[pos$$1.rind];
	      }

	      if (cs.indexOf("webix_hcell") != -1) {
	        pos$$1 = this._locate(node);
	        if (pos$$1) pos$$1.header = true;
	      }

	      if (cs.indexOf("webix_drop_area") != -1) {
	        pos$$1 = this._locate(node);
	        if (pos$$1) pos$$1.row = pos$$1.rind = "$webix-drop";
	      }

	      if (pos$$1) {
	        if (idOnly) return pos$$1.header ? null : pos$$1.row;
	        pos$$1.column = this._columns[pos$$1.cind].id;
	        pos$$1.toString = this._id_to_string;
	        return pos$$1;
	      }

	      node = node.parentNode;
	    }

	    return null;
	  },
	  _locate: function (node) {
	    var cdiv = node.parentNode;
	    if (!cdiv) return null;
	    var column = (node.getAttribute(
	    /*@attr*/
	    "column") || cdiv.getAttribute(
	    /*@attr*/
	    "column")) * 1;
	    var rind = node.getAttribute("aria-rowindex");
	    var row = node.getAttribute(
	    /*@attr*/
	    "row") || (rind ? rind - 1 : 0);
	    var span = (node.getAttribute("colspan") || cdiv.getAttribute("colspan")) * 1;
	    return {
	      rind: row,
	      cind: column,
	      span: span
	    };
	  },
	  _correctScrollSize: function () {
	    var center = -this._center_width;

	    for (var i = 0; i < this._columns.length; i++) {
	      center += this._columns[i].width;
	    }

	    this._scrollLeft = Math.min(this._scrollLeft, Math.max(0, center));
	  },
	  _updateColsSizeSettings: function (silent) {
	    if (!this._dtable_fully_ready) return;

	    this._correctScrollSize();

	    this._set_columns_positions();

	    this._set_split_sizes_x();

	    this._render_header_and_footer();

	    if (!silent) this._check_rendered_cols(false, false);
	  },
	  setColumnWidth: function (col, width, skip_update) {
	    return this._setColumnWidth(this.getColumnIndex(col), width, skip_update);
	  },
	  _setColumnWidth: function (col, width, skip_update, by_user) {
	    if (isNaN(width) || col < 0) return;
	    var column = this._columns[col];
	    if (column.minWidth && width < column.minWidth) width = column.minWidth;else if (width < this._settings.minColumnWidth) width = this._settings.minColumnWidth;
	    var old = column.width;

	    if (old != width) {
	      if (col >= this._settings.leftSplit && col < this._rightSplit) this._dtable_width += width - old;
	      column.width = width;
	      if (column.node) //method can be called from onStructLoad
	        column.node.style.width = width + "px";else return false;
	      if (!skip_update) this._updateColsSizeSettings();
	      this.callEvent("onColumnResize", [column.id, width, old, !!by_user]);
	      return true;
	    }

	    return false;
	  },
	  _getRowHeight: function (row) {
	    return (row.$height || this._settings.rowHeight) + (row.$subopen ? row.$subHeight : 0);
	  },
	  _getHeightByIndex: function (index$$1) {
	    var id = this.data.order[index$$1];
	    if (!id) return this._settings.rowHeight;
	    return this._getRowHeight(this.data.pull[id]);
	  },
	  _getHeightByIndexSumm: function (index1, index2) {
	    if (this._settings.fixedRowHeight) return (index2 - index1) * this._settings.rowHeight;else {
	      var summ = 0;

	      for (; index1 < index2; index1++) {
	        summ += this._getHeightByIndex(index1);
	      }

	      return summ;
	    }
	  },
	  _cellPosition: function (row, column) {
	    var top;

	    if (arguments.length == 1) {
	      column = row.column;
	      row = row.row;
	    }

	    var item = this.getItem(row);
	    var config = this.getColumnConfig(column);
	    var left = 0;
	    var parent = 0;

	    for (var index$$1 = 0; index$$1 < this._columns.length; index$$1++) {
	      if (index$$1 == this._settings.leftSplit || index$$1 == this._rightSplit) left = 0;
	      var leftcolumn = this._columns[index$$1];

	      if (leftcolumn.id == column) {
	        var split_column = index$$1 < this._settings.leftSplit ? 0 : index$$1 >= this._rightSplit ? 2 : 1;
	        parent = this._body.childNodes[split_column].firstChild;
	        break;
	      }

	      left += leftcolumn.width;
	    }

	    if (this.getIndexById(row) < this._settings.topSplit) top = this._getHeightByIndexSumm(0, this.getIndexById(row));else top = this._getHeightByIndexSumm((this._render_scroll_top || 0) - this._settings.topSplit, this.getIndexById(row)) + (this._render_scroll_shift || 0);
	    return {
	      parent: parent,
	      top: top,
	      left: left,
	      width: config.width,
	      height: item.$height || this._settings.rowHeight
	    };
	  },
	  _get_total_height: function () {
	    var pager = this._settings.pager;
	    var start = 0;
	    var max = this.data.order.length;

	    if (pager) {
	      start = pager.size * pager.page;
	      max = Math.min(max, start + pager.size);

	      if (pager.level) {
	        start = this.data.$min;
	        max = this.data.$max;
	      }
	    }

	    return this._getHeightByIndexSumm(start, max);
	  },
	  setRowHeight: function (rowId, height) {
	    if (isNaN(height)) return;
	    if (height < this._settings.minColumnHeight) height = this._settings.minColumnHeight;
	    var item = this.getItem(rowId);
	    var old_height = item.$height || this._settings.rowHeight;

	    if (old_height != height) {
	      item.$height = height;
	      this.config.fixedRowHeight = false;
	      this.render();
	      this.callEvent("onRowResize", [rowId, height, old_height]);
	    }
	  },
	  _onscroll_y: function (value) {
	    var scrollChange = this._scrollTop !== value;
	    this._scrollTop = value;

	    if (!this._settings.prerender) {
	      this._check_rendered_cols();
	    } else {
	      var conts = this._body.childNodes;

	      for (var i = 0; i < conts.length; i++) {
	        conts[i].scrollTop = value;
	      }
	    }

	    if (env.$customScroll) temp$1._update_scroll(this._body);

	    if (scrollChange) {
	      this.callEvent("onScrollY", []);
	      this.callEvent("onAfterScroll", []);
	    }
	  },
	  _setLeftScroll: function (value) {
	    this._body.childNodes[1].scrollLeft = this._scrollLeft = value;
	    if (this._settings.header) this._header.childNodes[1].scrollLeft = value;
	    if (this._settings.footer) this._footer.childNodes[1].scrollLeft = value;
	  },
	  _onscroll_x: function (value) {
	    var scrollChange = this._scrollLeft !== value;

	    this._setLeftScroll(value);

	    if (this._settings.prerender === false) this._check_rendered_cols(this._minimize_dom_changes ? false : true);
	    if (env.$customScroll) temp$1._update_scroll(this._body);

	    if (scrollChange) {
	      this.callEvent("onScrollX", []);
	      this.callEvent("onAfterScroll", []);
	    }
	  },
	  _get_x_range: function (full) {
	    if (full) return [0, this._columns.length];
	    var t = this._scrollLeft;
	    var xind = this._settings.leftSplit;

	    while (t > 0 && this._columns.length - 1 > xind) {
	      t -= this._columns[xind].width;
	      xind++;
	    }

	    var xend = xind;
	    if (t && xind > 0) xind--;
	    t += this._center_width;

	    while (t > 0 && xend < this._rightSplit) {
	      t -= this._columns[xend].width;
	      xend++;
	    }

	    return [xind, xend];
	  },
	  getVisibleCount: function () {
	    return Math.floor(this._dtable_offset_height / this.config.rowHeight);
	  },
	  //returns info about y-scroll position
	  _get_y_range: function (full) {
	    var t = this._scrollTop;
	    var start = 0;
	    var end = this.count(); //apply pager, if defined

	    var pager = this._settings.pager;

	    if (pager) {
	      start = pager.page * pager.size;
	      end = Math.min(end, start + pager.size);

	      if (pager.level) {
	        start = this.data.$min;
	        end = this.data.$max;
	      }
	    } //in case of autoheight - request full rendering


	    if (this._settings.autoheight) return [start, end, 0];
	    if (full) return [start, end, 0];
	    var xind = start;
	    var topSplit = this._settings.topSplit || 0;
	    if (topSplit) xind += topSplit;
	    var rowHeight = this._settings.fixedRowHeight ? this._settings.rowHeight : 0;

	    if (rowHeight) {
	      var dep = Math.ceil(t / rowHeight);
	      t -= dep * rowHeight;
	      xind += dep;
	    } else while (t > 0) {
	      t -= this._getHeightByIndex(xind);
	      xind++;
	    } //how much of the first cell is scrolled out


	    var xdef = xind > 0 && t ? -(this._getHeightByIndex(xind - 1) + t) : 0;
	    var xend = xind;
	    if (t) xind--;
	    t += (this._dtable_offset_height || this._content_height) - (this._top_split_height || 0);

	    if (rowHeight) {
	      var _dep = Math.ceil(t / rowHeight);

	      t -= _dep * rowHeight;
	      xend += _dep;
	    } else {
	      while (t > 0 && xend < end) {
	        t -= this._getHeightByIndex(xend);
	        xend++;
	      }
	    }

	    if (xend > end) xend = end;
	    return [xind, xend, xdef];
	  },
	  _repaint_single_row: function (id) {
	    var item = this.getItem(id);
	    var rowindex = this.getIndexById(id);

	    var state = this._get_y_range();

	    var freeze = this.config.topSplit;
	    var freezeCss = "";

	    if (rowindex >= freeze) {
	      //row not visible
	      if (rowindex < state[0] || rowindex >= state[1]) return;
	      rowindex -= state[0] - freeze;
	    } else {
	      freezeCss = rowindex == freeze - 1 ? " webix_topcell webix_last_topcell" : " webix_topcell";
	    } //get visible column


	    var x_range = this._get_x_range();

	    for (var i = 0; i < this._columns.length; i++) {
	      var column = this._columns[i]; //column not visible

	      if (i < this._rightSplit && i >= this._settings.leftSplit && (i < x_range[0] || i > x_range[1])) column._yr0 = -999; //ensure that column will not be reused

	      if (column.attached && column.node) {
	        var node = column.node.childNodes[rowindex];
	        if (!node) continue;

	        var value = this._getValue(item, this._columns[i], 0);

	        node.innerHTML = value;
	        node.className = this._getCss(this._columns[i], value, item, id) + freezeCss;
	      }
	    }
	  },
	  _check_rendered_cols: function (x_scroll, force) {
	    if (!this._columns.length) return;
	    if (force) this._clearColumnCache();

	    var xr = this._get_x_range(this._settings.prerender);

	    var yr = this._get_y_range(this._settings.prerender === true);

	    if (x_scroll) {
	      for (var i = this._settings.leftSplit; i < xr[0]; i++) {
	        this._hideColumn(i, force);
	      }

	      for (var _i6 = xr[1]; _i6 < this._rightSplit; _i6++) {
	        this._hideColumn(_i6, force);
	      }
	    }

	    this._render_full_rows = [];

	    for (var _i7 = 0; _i7 < this._settings.leftSplit; _i7++) {
	      this._renderColumn(_i7, yr, force);
	    }

	    for (var _i8 = xr[0]; _i8 < xr[1]; _i8++) {
	      this._renderColumn(_i8, yr, force, _i8 == xr[0]);
	    }

	    for (var _i9 = this._rightSplit; _i9 < this._columns.length; _i9++) {
	      this._renderColumn(_i9, yr, force);
	    }

	    this._check_and_render_full_rows(yr[0], yr[1], force);

	    this._check_load_next(yr);
	  },
	  _delete_full_rows: function (start, end) {
	    this._rows_cache_start = start;
	    this._rows_cache_end = end;
	    remove(this._rows_cache);
	    this._rows_cache = [];
	  },
	  _adjust_rows: function () {
	    if (this._settings.prerender && this._rows_body) {
	      var state = this.getScrollState();
	      this._rows_body.style.top = "-" + (state.y || 0) + "px";
	    }
	  },
	  _check_and_render_full_rows: function (start, end, force) {
	    if (this._rows_body) this._rows_body.style.top = this._render_scroll_shift + "px";

	    if (!force && start == this._rows_cache_start && end == this._rows_cache_end) {
	      if (this.config.topSplit) {
	        //don't move split rows
	        for (var i = 0; i < this._render_full_rows.length; i++) {
	          var row = this._rows_cache[i];

	          if (this._render_full_rows[i].index < this.config.topSplit) {
	            row.style.top = this._render_full_rows[i].top - this._render_scroll_shift + "px";
	          }
	        }
	      }

	      return;
	    }

	    this._delete_full_rows(start, end);

	    if (this._render_full_row_some) this._render_full_row_some = false;else return;

	    for (var _i10 = 0; _i10 < this._render_full_rows.length; _i10++) {
	      var info = this._render_full_rows[_i10];
	      var item = this.getItem(info.id);
	      var value;

	      if (typeof item.$row == "function") {
	        value = item.$row.call(this, item, this.type);
	      } else {
	        value = this._getValue(item, this.getColumnConfig(item.$row), _i10);
	      }

	      var _row = this._rows_cache[_i10] = create("DIV", null, value);

	      _row.className = "webix_cell " + (item.$sub ? "webix_dtable_sub" + (this._settings.subview ? "view" : "row") : "webix_dtable_colrow" + (item.$row ? " webix_topcell" + (this.data.getMark(item.id, "webix_selected") ? " webix_selected" : "") : ""));

	      _row.setAttribute(
	      /*@attr*/
	      "column", 0);

	      _row.setAttribute(
	      /*@attr*/
	      "row", info.index);

	      var height = item.$height || this._settings.rowHeight;
	      if (item.$subopen) _row.style.height = item.$subHeight + "px";else _row.style.height = height + "px";
	      _row.style.paddingRight = env.scrollSize + "px";
	      var topDelta = this._render_full_rows[_i10].index < this.config.topSplit ? -this._render_scroll_shift : 0;
	      _row.style.top = topDelta + info.top + (item.$subopen ? height - 1 : 0) + "px";

	      if (!this._rows_body) {
	        this._rows_body = create("DIV");
	        this._rows_body.style.position = "relative";
	        this._rows_body.style.top = this._render_scroll_shift + "px";

	        this._body.appendChild(this._rows_body);
	      }

	      this._rows_body.appendChild(_row);

	      this.attachEvent("onSyncScroll", function (x, y, t) {
	        Touch._set_matrix(this._rows_body, 0, y, t);
	      });
	      if (this._settings.subview) this.callEvent("onSubViewRender", [item, _row]);
	    }
	  },
	  _check_load_next: function (yr) {
	    var paging = this._settings.pager;
	    var fetch = this._settings.datafetch;
	    var direction = !this._last_valid_render_pos || yr[0] >= this._last_valid_render_pos;
	    this._last_valid_render_pos = yr[0];

	    if (this._data_request_flag) {
	      if (paging && (!fetch || fetch >= paging.size)) if (this._check_rows([0, paging.size * paging.page], Math.max(fetch, paging.size), true)) return this._data_request_flag = null;

	      this._run_load_next(this._data_request_flag, direction);

	      this._data_request_flag = null;
	    } else {
	      if (this._settings.loadahead) this._check_rows(yr, this._settings.loadahead, direction);
	    }
	  },
	  _check_rows: function (view, count, dir) {
	    var start = view[1];
	    var end = start + count;

	    if (!dir) {
	      start = view[0] - count;
	      end = view[0];
	    }

	    if (start < 0) start = 0;
	    end = Math.min(end, this.data.order.length - 1);
	    var result = false;

	    for (var i = start; i < end; i++) {
	      if (!this.data.order[i]) {
	        if (!result) result = {
	          start: i,
	          count: end - start
	        };else {
	          result.last = i;
	          result.count = i - start;
	        }
	      }
	    }

	    if (result) {
	      this._run_load_next(result, dir);

	      return true;
	    }
	  },
	  _run_load_next: function (conf, direction) {
	    var count = Math.max(conf.count, this._settings.datafetch || this._settings.loadahead || 0);
	    var start = direction ? conf.start : conf.last - count + 1;
	    if (this._maybe_loading_already(conf.count, conf.start)) return;
	    this.loadNext(count, start);
	  },
	  // necessary for safari only
	  _preserveScrollTarget: function (columnNode) {
	    if (env.isSafari) {
	      var i,
	          node,
	          newNode,
	          scroll,
	          dir = [this._x_scroll, this._y_scroll];

	      for (i = 0; i < 2; i++) {
	        scroll = dir[i];

	        if (scroll && scroll._scroll_trg && scroll._scroll_trg.parentNode == columnNode) {
	          node = scroll._scroll_trg;
	        }
	      }

	      if (node) {
	        if (this._scrollWheelTrg) remove(this._scrollWheelTrg);
	        this._scrollWheelTrg = node;
	        newNode = node.cloneNode(true); // required for _hideColumn

	        node.parentNode.insertBefore(newNode, node);
	        this._scrollWheelTrg.style.display = "none";

	        this._body.appendChild(this._scrollWheelTrg);
	      }
	    }
	  },
	  _hideColumn: function (index$$1) {
	    var col = this._columns[index$$1]; // preserve target node for Safari wheel event

	    this._preserveScrollTarget(col.node);

	    remove(col.node);
	    col.attached = false;
	  },
	  _clearColumnCache: function () {
	    for (var i = 0; i < this._columns.length; i++) {
	      this._columns[i]._yr0 = -1;
	    }

	    if (this._rows_cache.length) {
	      remove(this._rows_cache);
	      this._rows_cache = [];
	    }
	  },
	  getText: function (row_id, column_id) {
	    return this._getValue(this.getItem(row_id), this.getColumnConfig(column_id), 0);
	  },
	  getCss: function (row_id, column_id) {
	    var item = this.getItem(row_id);
	    return this._getCss(this.getColumnConfig(column_id), item[column_id], item, row_id);
	  },
	  _getCss: function (config, value, item, id) {
	    var css = "webix_cell";

	    if (config.cssFormat) {
	      var per_css = config.cssFormat(value, item, id, config.id);

	      if (per_css) {
	        if (_typeof(per_css) == "object") css += " " + createCss(per_css);else css += " " + per_css;
	      }
	    }

	    var row_css = item.$css;

	    if (row_css) {
	      if (_typeof(row_css) == "object") item.$css = row_css = createCss(row_css);
	      css += " " + row_css;
	    }

	    var mark = this.data._marks[id];

	    if (mark) {
	      if (mark.$css) css += " " + mark.$css;

	      if (mark.$cellCss) {
	        var mark_marker = mark.$cellCss[config.id];
	        if (mark_marker) css += " " + mark_marker;
	      }
	    }

	    if (item.$cellCss) {
	      var css_marker = item.$cellCss[config.id];

	      if (css_marker) {
	        if (_typeof(css_marker) == "object") css_marker = createCss(css_marker);
	        css += " " + css_marker;
	      }
	    } //cell-selection


	    var selected = this.data.getMark(item.id, "webix_selected");
	    if (selected && (selected.$row || selected[config.id]) || config.$selected) css += this._select_css;
	    return css;
	  },
	  _getValue: function (item, config, i) {
	    if (!item) return "";
	    var value;
	    value = item[config.id];
	    if (value === undefined || value === null) value = "";
	    if (config.format) value = config.format(value);
	    if (config.template) value = config.template(item, this.type, value, config, i);
	    return value;
	  },
	  //we don't use render-stack, but still need a place for common helpers
	  //so creating a simple "type" holder
	  type: {
	    checkbox: function (obj, common, value, config) {
	      var checked = value == config.checkValue ? "checked=\"true\"" : "";
	      return "<input class='webix_table_checkbox' type='checkbox' " + checked + ">";
	    },
	    radio: function (obj, common, value, config) {
	      var checked = value == config.checkValue ? "checked=\"true\"" : "";
	      return "<input class='webix_table_radio' type='radio' " + checked + ">";
	    },
	    editIcon: function () {
	      return "<span class='webix_icon wxi-pencil'></span>";
	    },
	    trashIcon: function () {
	      return "<span class='webix_icon wxi-trash'></span>";
	    }
	  },
	  type_setter: function (value) {
	    if (!this.types || !this.types[value]) type(this, value);else {
	      this.type = clone(this.types[value]);
	      if (this.type.css) this._contentobj.className += " " + this.type.css;
	    }
	    if (this.type.on_click) exports.extend(this.on_click, this.type.on_click);
	    return value;
	  },
	  _renderColumn: function (index$$1, yr, force, single$$1) {
	    var col = this._columns[index$$1];

	    if (!col.attached) {
	      var split_column = index$$1 < this._settings.leftSplit ? 0 : index$$1 >= this._rightSplit ? 2 : 1;

	      this._body.childNodes[split_column].firstChild.appendChild(col.node);

	      col.attached = true;
	      col.split = split_column;
	    }

	    this._render_scroll_top = yr[0];
	    this._render_scroll_shift = 0;
	    this._render_scroll_diff = yr[2]; //if columns not aligned during scroll - set correct scroll top value for each column

	    if (this._settings.scrollAlignY) {
	      if (yr[1] == this.data.order.length || this.data.$pagesize && yr[1] % this.data.$pagesize === 0) {
	        col.node.style.top = (this._render_scroll_shift = yr[2]) + "px";
	      } else if (col._yr2) col.node.style.top = "0px";
	    } else {
	      this._render_scroll_shift = yr[2];
	      col.node.style.top = yr[2] + "px";
	    }

	    if (!force && col._yr0 == yr[0] && col._yr1 == yr[1] && (!this._settings.topSplit || col._render_scroll_shift == this._render_scroll_shift)) return 0;
	    var html = "";
	    var config = this._settings.columns[index$$1];
	    var state = {
	      row: this._settings.rowHeight,
	      total: 0,
	      single: single$$1
	    };

	    for (var i = 0; i < this._settings.topSplit; i++) {
	      html += this._render_single_cell(i, config, yr, state, -this._render_scroll_shift, index$$1);
	    } // ignore not available rows in top-split area


	    this._data_request_flag = null;

	    for (var _i11 = Math.max(yr[0], this._settings.topSplit); _i11 < yr[1]; _i11++) {
	      html += this._render_single_cell(_i11, config, yr, state, -1, index$$1);
	    } // preserve target node for Safari wheel event


	    this._preserveScrollTarget(col.node);

	    col.node.innerHTML = html;
	    col._yr0 = yr[0];
	    col._yr1 = yr[1];
	    col._yr2 = yr[2];
	    col._render_scroll_shift = this._render_scroll_shift;
	    return 1;
	  },
	  _render_single_cell: function (i, config, yr, state, top, index$$1) {
	    var id = this.data.order[i];
	    var item = this.data.getItem(id);
	    var html = "";

	    if (item) {
	      var aria = " role='gridcell' aria-rowindex='" + (i + 1) + "' aria-colindex='" + (this.getColumnIndex(config.id) + 1) + "'" + (item.$count || item.$sub ? " aria-expanded='" + (item.open || item.$subopen ? "true" : "false") + "'" : "") + (item.$level ? " aria-level='" + item.$level + "'" : "");

	      if (state.single && item.$row) {
	        this._render_full_row_some = true;

	        this._render_full_rows.push({
	          top: state.total,
	          id: item.id,
	          index: i
	        });

	        if (!item.$sub) {
	          var rowHeight = item.$height || state.row;
	          state.total += rowHeight;
	          return "<div" + aria + " class='webix_cell' style='height:" + rowHeight + "px;'></div>";
	        }
	      }

	      var value = this._getValue(item, config, i);

	      var css = this._getCss(config, value, item, id);

	      var ariaSelect = " aria-selected='true' tabindex='0'";

	      if (css.indexOf("select") !== -1) {
	        //in case of row/column selection - make only first cell focusable
	        if (css.indexOf("row") !== -1) {
	          var xr = this._get_x_range();

	          if (xr[0] === index$$1) aria += ariaSelect;
	        } else if (css.indexOf("col") !== -1) {
	          if (i === yr[0]) aria += ariaSelect;
	        } else aria += ariaSelect;
	      }

	      var isOpen = !!item.$subopen;
	      var margin = isOpen ? "margin-bottom:" + item.$subHeight + "px;" : "";

	      if (top >= 0) {
	        if (top > 0) margin += "top:" + top + "px;'";
	        css = "webix_topcell " + css;
	        if (i == this._settings.topSplit - 1) css = "webix_last_topcell " + css;
	      }

	      if (item.$height) {
	        html = "<div" + aria + " class='" + css + "' style='height:" + item.$height + "px;" + margin + "'>" + value + "</div>";
	        state.total += item.$height - state.row;
	      } else {
	        html = "<div" + aria + " class='" + css + "'" + (margin ? " style='" + margin + "'" : "") + ">" + value + "</div>";
	      }

	      if (isOpen) state.total += item.$subHeight;
	    } else {
	      html = "<div role='gridcell' class='webix_cell'></div>";
	      if (!this._data_request_flag) this._data_request_flag = {
	        start: i,
	        count: yr[1] - i
	      };else this._data_request_flag.last = i;
	    }

	    state.total += state.row;
	    return html;
	  },
	  _set_split_sizes_y: function () {
	    if (!this._columns.length || isNaN(this._content_height * 1)) return;
	    debug_size_box(this, ["y-sizing"], true);
	    var wanted_height = this._dtable_height + (this._scrollSizeX ? this._scrollSizeX : 0);
	    if ((this._settings.autoheight || this._settings.yCount) && this.resize()) return;

	    this._y_scroll.sizeTo(this._content_height, this._header_height, this._footer_height);

	    this._y_scroll.define("scrollHeight", wanted_height);

	    this._top_split_height = this._settings.topSplit ? this._getHeightByIndexSumm(0, this._settings.topSplit) : 0;
	    this._dtable_offset_height = Math.max(0, this._content_height - this._scrollSizeX - this._header_height - this._footer_height);

	    for (var i = 0; i < 3; i++) {
	      this._body.childNodes[i].style.height = this._dtable_offset_height + "px";
	      if (this._settings.prerender) this._body.childNodes[i].firstChild.style.height = this._dtable_height + "px";else this._body.childNodes[i].firstChild.style.height = this._dtable_offset_height + "px";
	    } //prevent float overflow, when we have split and very small


	    this._header.style.height = this._header_height + "px";
	  },
	  _set_split_sizes_x: function () {
	    if (!this._columns.length) return;
	    var index$$1 = 0;
	    this._left_width = 0;
	    this._right_width = 0;
	    this._center_width = 0;

	    while (index$$1 < this._settings.leftSplit) {
	      this._left_width += this._columns[index$$1].width;
	      index$$1++;
	    }

	    index$$1 = this._columns.length - 1;

	    while (index$$1 >= this._rightSplit) {
	      this._right_width += this._columns[index$$1].width;
	      index$$1--;
	    }

	    if (!this._content_width) return;
	    if (this._settings.autowidth && this.resize()) return;
	    this._center_width = this._content_width - this._right_width - this._left_width - this._scrollSizeY;
	    this._body.childNodes[1].firstChild.style.width = this._dtable_width + "px";
	    this._body.childNodes[0].style.width = this._left_width + "px";
	    this._body.childNodes[1].style.width = this._center_width + "px";
	    this._body.childNodes[2].style.width = this._right_width + "px";
	    this._header.childNodes[0].style.width = this._left_width + "px";
	    this._header.childNodes[1].style.width = this._center_width + "px";
	    this._header.childNodes[2].style.width = this._right_width + "px";
	    this._footer.childNodes[0].style.width = this._left_width + "px";
	    this._footer.childNodes[1].style.width = this._center_width + "px";
	    this._footer.childNodes[2].style.width = this._right_width + "px";
	    var delta = this._center_width - this._dtable_width;
	    if (delta < 0) delta = 0; //negative header space has not sense

	    if (delta != this._header_fix_width) {
	      this._header_fix_width = delta;

	      this._size_header_footer_fix();
	    } // temp. fix: Chrome [DIRTY]


	    if (env.isWebKit) {
	      var w = this._body.childNodes[0].offsetWidth; //eslint-disable-line

	      w = this._body.childNodes[1].offsetWidth;
	      w = this._body.childNodes[1].firstChild.offsetWidth;
	      w = this._body.childNodes[2].offsetWidth;
	    }

	    this._x_scroll.sizeTo(this._content_width - this._scrollSizeY);

	    this._x_scroll.define("scrollWidth", this._dtable_width + this._left_width + this._right_width);
	  },
	  $getSize: function (dx, dy) {
	    if ((this._settings.autoheight || this._settings.yCount) && this._settings.columns) {
	      //if limit set - use it
	      var desired = (this._settings.yCount || 0) * this._settings.rowHeight; //else try to use actual rendered size
	      //if component invisible - this is not valid, so fallback to all rows

	      if (!desired) desired = this.isVisible() ? this._dtable_height : this.count() * this._settings.rowHeight; //add scroll and check minHeight limit

	      this._settings.height = Math.max(desired + (this._scrollSizeX ? this._scrollSizeX : 0) - 1, this._settings.minHeight || 0) + this._header_height + this._footer_height;
	    }

	    if (this._settings.autowidth && this._settings.columns) this._settings.width = Math.max(this._dtable_width + this._left_width + this._right_width + this._scrollSizeY, this._settings.minWidth || 0);
	    var minwidth = this._left_width + this._right_width + this._scrollSizeY;
	    var sizes = base.api.$getSize.call(this, dx, dy);
	    sizes[0] = Math.max(sizes[0] || minwidth);
	    return sizes;
	  },
	  _restore_scroll_state: function () {
	    if (this._x_scroll && !env.touch) {
	      var state = this.getScrollState();
	      this._x_scroll._last_scroll_pos = this._y_scroll._last_scroll_pos = -1;
	      this.scrollTo(state.x, state.y);
	    }
	  },
	  $setSize: function () {
	    var oldw = this._content_width;
	    var oldh = this._content_height;

	    if (base.api.$setSize.apply(this, arguments)) {
	      if (this._dtable_fully_ready) {
	        this.callEvent("onResize", [this._content_width, this._content_height, oldw, oldh]);

	        this._set_split_sizes_x();

	        this._set_split_sizes_y();
	      }

	      this.render();
	    }
	  },
	  _on_header_click: function (column) {
	    var col = this.getColumnConfig(column);
	    if (!col.sort) return;
	    var order = "asc";
	    if (col.id == this._last_sorted) order = this._last_order == "asc" ? "desc" : "asc";

	    this._sort(col.id, order, col.sort);
	  },
	  markSorting: function (column, order) {
	    if (!this._sort_sign) this._sort_sign = create("DIV");
	    var parent = this._sort_sign.parentNode;

	    if (parent) {
	      parent.removeAttribute("aria-sort");
	      parent.removeAttribute("tabindex");
	    }

	    remove(this._sort_sign);

	    if (order) {
	      var cell = this._get_header_cell(this.getColumnIndex(column));

	      if (cell) {
	        this._sort_sign.className = "webix_ss_sort_" + order;
	        cell.style.position = "relative";
	        cell.appendChild(this._sort_sign);
	        cell.setAttribute("aria-sort", order + "ending");
	        cell.setAttribute("tabindex", "0");
	      }

	      this._last_sorted = column;
	      this._last_order = order;
	    } else {
	      this._last_sorted = this._last_order = null;
	    }
	  },
	  scroll_setter: function (mode) {
	    if (typeof mode == "string") {
	      this._settings.scrollX = mode.indexOf("x") != -1;
	      this._settings.scrollY = mode.indexOf("y") != -1;
	      return mode;
	    } else return this._settings.scrollX = this._settings.scrollY = mode;
	  },
	  _get_header_cell: function (column) {
	    var cells = this._header.getElementsByTagName("TD");

	    var maybe = null;

	    for (var i = 0; i < cells.length; i++) {
	      if (cells[i].getAttribute(
	      /*@attr*/
	      "column") == column && !cells[i].getAttribute(
	      /*@attr*/
	      "active_id")) {
	        maybe = cells[i].firstChild;
	        if ((cells[i].colSpan || 0) < 2) return maybe;
	      }
	    }

	    return maybe;
	  },
	  _sort: function (col_id, direction, type$$1) {
	    var _this = this;

	    direction = direction || "asc";
	    this.markSorting(col_id, direction);

	    if (type$$1 == "server") {
	      this.callEvent("onBeforeSort", [col_id, direction, type$$1]);
	      this.loadNext(0, 0, 0, 0, 1).then(function (data) {
	        _this.clearAll(true);

	        _this.parse(data);

	        _this.callEvent("onAfterSort", [col_id, direction, type$$1]);
	      });
	    } else {
	      if (type$$1 == "text") {
	        this.data.each(function (obj) {
	          obj.$text = this.getText(obj.id, col_id);
	        }, this);
	        type$$1 = "string";
	        col_id = "$text";
	      }

	      if (typeof type$$1 == "function") this.data.sort(type$$1, direction);else this.data.sort(col_id, direction, type$$1 || "string");
	    }
	  },
	  _mouseEventCall: function (css_call, e, id, trg) {
	    var functor, i, res;

	    if (css_call.length) {
	      for (i = 0; i < css_call.length; i++) {
	        functor = toFunctor(css_call[i], this.$scope);
	        res = functor.call(this, e, id, trg);
	        if (res === false) return false;
	      }
	    }
	  },
	  //because we using non-standard rendering model, custom logic for mouse detection need to be used
	  _mouseEvent: function (e, hash, name, pair) {
	    e = e || event;
	    var trg = e.target || e.srcElement;
	    if (this._settings.subview && this != $$(trg)) return; //define some vars, which will be used below

	    var css = "",
	        css_call = [],
	        found = false,
	        id = null,
	        res; //loop through all parents

	    while (trg && trg.parentNode && trg != this._viewobj.parentNode) {
	      var trgCss = _getClassName(trg);

	      if (css = trgCss) {
	        css = css.toString().split(" ");

	        for (var i = css.length - 1; i >= 0; i--) {
	          if (hash[css[i]]) css_call.push(hash[css[i]]);
	        }
	      }

	      if (trg.parentNode.getAttribute && !id) {
	        var column = trg.parentNode.getAttribute(
	        /*@attr*/
	        "column") || trg.getAttribute(
	        /*@attr*/
	        "column");

	        if (column) {
	          //we need to ignore TD - which is header|footer
	          var isBody = trg.parentNode.tagName == "DIV"; //column already hidden or removed

	          if (!this._columns[column]) return;
	          found = true;

	          if (isBody) {
	            var index$$1 = trg.parentNode.getAttribute(
	            /*@attr*/
	            "row") || trg.getAttribute(
	            /*@attr*/
	            "row");

	            if (!index$$1) {
	              //click event occurs on column holder, we can't detect cell
	              if (trg.getAttribute(
	              /*@attr*/
	              "column")) return;
	              index$$1 = index(trg);
	              if (index$$1 >= this._settings.topSplit && !this._settings.prerender) index$$1 += this._columns[column]._yr0 - this._settings.topSplit;
	            }

	            this._item_clicked = id = {
	              row: this.data.order[index$$1],
	              column: this._columns[column].id
	            };
	            id.toString = this._id_to_string;
	          } else this._item_clicked = id = {
	            column: this._columns[column].id
	          }; //some custom css handlers was found


	          res = this._mouseEventCall(css_call, e, id, trg);
	          if (res === false) return; //call inner handler

	          if (isBody) {
	            if (this.callEvent("on" + name, [id, e, trg]) && pair) {
	              this.callEvent("on" + pair, [id, e, trg]);
	            }
	          } else if (name == "ItemClick") {
	            var isHeader = trg.parentNode.parentNode.getAttribute(
	            /*@attr*/
	            "section") == "header";
	            if (isHeader && this.callEvent("onHeaderClick", [id, e, trg])) this._on_header_click(id.column);
	          }

	          css_call = [];
	        }
	      }

	      trg = trg.parentNode;
	    }

	    this._mouseEventCall(css_call, e, id, this.$view);

	    return found; //returns true if item was located and event was triggered
	  },
	  _get_tooltip_data: function (t, e) {
	    var id = this.locate(e);
	    if (!id) return null;
	    var tooltip = TooltipControl._tooltip;
	    var data;

	    if (id.header) {
	      var node = e.target || e.srcElement;
	      var pos$$1;
	      var cind = id.cind - (id.span ? id.span - 1 : 0);
	      var rind = -1;

	      while (node && !pos$$1) {
	        node = node.parentNode;
	        pos$$1 = node.getAttribute("section");
	      }

	      while ((node = node.previousSibling) !== null) {
	        rind++;
	      }

	      var config = this._columns[cind][pos$$1][rind];
	      if (config.tooltip) tooltip.type.template = template(config.tooltip === true ? "#text#" : config.tooltip);else return null;
	      data = config;
	    } else {
	      var _config = tooltip.type.column = this.getColumnConfig(id.column);

	      var def = !_config.tooltip || _config.tooltip === true; //empty tooltip - ignoring

	      if (!_config.tooltip && _config.tooltip !== undefined) return null;

	      if (def && !this._settings.tooltip.template) {
	        data = this.getText(id.row, id.column).toString();
	      } else if (!def) {
	        var area = (e.target || e.srcElement).getAttribute("webix_area");

	        if (area) {
	          tooltip.type.template = function (obj, common) {
	            var values = obj[_config.id];
	            return template(_config.tooltip).call(this, obj, common, values[area], area);
	          };
	        } else tooltip.type.template = template(_config.tooltip);
	      } else tooltip.type.template = template(this._settings.tooltip.template);
	    }

	    return data !== undefined ? data : this.getItem(id.row);
	  },
	  $tooltipOut: function () {
	    TooltipControl._hide_tooltip();

	    delete TooltipControl._tooltip.type.column;
	    return null;
	  },
	  showOverlay: function (message) {
	    if (!this._datatable_overlay) {
	      var t = create("DIV", {
	        "class": "webix_overlay"
	      }, "");

	      this._body.appendChild(t);

	      this._datatable_overlay = t;
	    }

	    this._datatable_overlay.innerHTML = message;
	  },
	  hideOverlay: function () {
	    if (this._datatable_overlay) {
	      remove(this._datatable_overlay);
	      this._datatable_overlay = null;
	    }
	  },
	  mapCells: function (startrow, startcol, numrows, numcols, callback, getOnly) {
	    if (startrow === null && this.data.order.length > 0) startrow = this.data.order[0];
	    if (startcol === null) startcol = this.columnId(0);
	    if (numrows === null) numrows = this.data.order.length;
	    if (numcols === null) numcols = this._settings.columns.length;
	    if (!this.exists(startrow)) return;
	    startrow = this.getIndexById(startrow);
	    startcol = this.getColumnIndex(startcol);
	    if (startcol === null) return;

	    for (var i = 0; i < numrows && startrow + i < this.data.order.length; i++) {
	      var row_ind = startrow + i;
	      var row_id = this.data.order[row_ind];

	      if (row_id) {
	        //dyn loading
	        var item = this.getItem(row_id);

	        for (var j = 0; j < numcols && startcol + j < this._settings.columns.length; j++) {
	          var col_ind = startcol + j;
	          var col_id = this.columnId(col_ind);
	          var result = callback(item[col_id], row_id, col_id, i, j);
	          if (!getOnly) item[col_id] = result;
	        }
	      }
	    }
	  },
	  _call_onparse: function (driver, data) {
	    if (!this._settings.columns && driver.getConfig) this.define("columns", driver.getConfig(data));
	  },
	  _autoDetectConfig: function () {
	    var test = this.getItem(this.getFirstId());
	    var res = this._settings.columns = [];

	    for (var key in test) {
	      if (key != "id" && key[0] != "$") res.push({
	        id: key,
	        header: key[0].toUpperCase() + key.substr(1),
	        sort: "string",
	        editor: "text"
	      });
	    }

	    if (res.length) res[0].fillspace = true;
	    if (typeof this._settings.select == "undefined") this.define("select", "row");
	  }
	}; // #include ui/datatable/datatable_filter.js
	// #include ui/datatable/datatable_selection.js
	// #include ui/datatable/datatable_blockselect.js
	// #include ui/datatable/datatable_resize.js
	// #include ui/datatable/datatable_paging.js
	// #include ui/datatable/datatable_clipboard.js
	// #include ui/datatable/datatable_state.js
	// #include ui/datatable/datatable_touch.js
	// #include ui/datatable/datatable_size.js
	// #include ui/datatable/datatable_math.js
	// #include ui/datatable/datatable_edit.js
	// #include ui/datatable/datatable_columns.js
	// #include ui/datatable/datatable_keynav.js
	// #include ui/datatable/datatable_dnd.js
	// #include ui/datatable/datatable_validation.js
	// #include ui/datatable/datatable_sparklines.js
	// #include ui/datatable/datatable_print.js
	// #include ui/datatable/datatable_export.js

	var view$1d = exports.protoUI(api$1d, temp$3, DataState, TablePaste, DataMove, EditAbility, KeysNavigation, PagingAbility, CustomPrint, Mixin$c, ValidateCollection, Mixin$b, Mixin$d, DragItem, Mixin$a, Mixin$9, Mixin$8, Mixin$7, Mixin$6, temp$4, Mixin$5, Mixin$4, Mixin$3, temp$5, Mixin$2, Mixin$1, Mixin, temp$6, temp$7, AutoTooltip, Group, DataMarks, DataLoader, MouseEvents, MapCollection, base.view, EventSystem, Settings);
	var datatable = {
	  api: api$1d,
	  view: view$1d
	};

	var api$1e = {
	  name: "treetable",
	  $init: function () {
	    exports.extend(this.data, TreeStore, true);
	    exports.extend(this.type, TreeType);
	    exports.extend(this, TreeDataMove, true);

	    for (var key in TreeClick) {
	      if (!this.on_click[key]) this.on_click[key] = this._unwrap_id(TreeClick[key]);
	    }

	    this.type.treetable = template("{common.space()}{common.icon()} {common.folder()}");

	    this.type.treecheckbox = function (obj) {
	      if (obj.indeterminate && !obj.nocheckbox) return "<div class='webix_tree_checkbox webix_indeterminate'></div>";else return TreeType.checkbox.apply(this, arguments);
	    };

	    this.data.provideApi(this, true);

	    this._viewobj.setAttribute("role", "treegrid");
	  },
	  _drag_order_complex: false,
	  _unwrap_id: function (original) {
	    return function (e, id) {
	      id = id.row;
	      return original.call(this, e, id);
	    };
	  },
	  _close_branches: function (context) {
	    var source = !this._settings.prerender ? [context.start] : context.source;

	    for (var i = 0; i < source.length; i++) {
	      this.close(source[i]);
	    }
	  },
	  getState: function () {
	    var state = DataState.getState.call(this);
	    exports.extend(state, TreeAPI.getState.call(this));
	    return state;
	  },
	  setState: function (state) {
	    if (TreeAPI.setState.call(this, state)) {
	      //run grid-state only when tree component was fully loaded 
	      DataState.setState.call(this, state);
	    }
	  },
	  clipboard_setter: function (value) {
	    exports.extend(this._paste, TreeTablePaste);
	    return TablePaste.clipboard_setter.call(this, value);
	  },
	  _run_load_next: function (conf, direction) {
	    for (var i = 0; i < conf.start; i++) {
	      var id = this.data.order[i];
	      if (id && this.getItem(id).$level != 1) conf.start--;
	    }

	    return datatable.api._run_load_next.call(this, conf, direction);
	  }
	};
	var view$1e = exports.protoUI(api$1e, TreeAPI, TreeStateCheckbox, TreeDataLoader, datatable.view);

	/* eslint no-constant-condition: 0 */

	/* eslint no-inner-declarations: 0 */

	{
	  var get_inspector_config = function (view) {
	    var values = {};
	    var options = [];
	    view = $$(view);

	    for (var key in view.config) {
	      if (ignore[key]) continue;
	      if (_typeof(view.config[key]) == "object") continue;
	      if (typeof view.config[key] == "undefined") continue;
	      if (typeof view.config[key] == "function") continue;
	      if (key == "view" || key == "id") options.push({
	        label: key,
	        id: key
	      });else options.push({
	        label: key,
	        type: "text",
	        id: key
	      });
	      if (view.defaults[key] == view.config[key]) options[options.length - 1].css = {
	        "color": "#888"
	      };
	      values[key] = view.config[key];
	    }

	    options.sort(function (a, b) {
	      if (!a.css && b.css) return -1;
	      if (a.css && !b.css) return 1;
	      return a.id > b.id ? 1 : a.id == b.id ? 0 : -1;
	    });
	    return {
	      elements: options,
	      data: values,
	      head: " [" + view.name + "] <strong>" + view._settings.id + "</strong>"
	    };
	  };

	  var create_inspector = function () {
	    if (!$$("webix_debug_inspector_win")) ui({
	      id: "webix_debug_inspector_win",
	      view: "window",
	      top: 2,
	      left: 0,
	      width: 350,
	      height: 350,
	      head: false,
	      autofit: false,
	      body: {
	        cols: [{
	          width: 10
	        }, {
	          type: "clean",
	          rows: [{
	            view: "toolbar",
	            elements: [{
	              view: "label",
	              value: "",
	              id: "webix_debug_inspector_head"
	            }, {
	              view: "button",
	              width: 100,
	              value: "Hide",
	              type: "custom",
	              click: function () {
	                show_inspector();
	              }
	            }]
	          }, {
	            id: "webix_debug_inspector",
	            nameWidth: 150,
	            view: "property",
	            scroll: "y",
	            elements: [],
	            on: {
	              onaftereditstop: function (state, editor) {
	                if (state.old == state.value) return;
	                var value = state.value;

	                if (value === "true" || value === "false") {
	                  value = value === "true";
	                } else {
	                  var intvalue = parseInt(value, 10);
	                  if (intvalue == value) value = intvalue;
	                }

	                var view = $$(this.config.view);
	                view.define(editor.id, value);
	                if (view.refreshColumns) view.refreshColumns();else if (view.refresh) view.refresh();
	                view.resize();
	              }
	            }
	          }]
	        }]
	      }
	    });
	  };

	  var show_inspector = function (view, ev) {
	    create_inspector();
	    var win = $$("webix_debug_inspector_win");

	    if (view) {
	      var config = get_inspector_config(view);
	      var winx = document.body.offsetWidth;
	      var winy = document.body.offsetHeight;
	      var pos$$1 = ev ? pos(ev) : {
	        x: 0,
	        y: 0
	      };
	      win.define("height", Math.max(350, winy - 4));
	      win.resize();
	      var props = $$("webix_debug_inspector");
	      props.define("elements", config.elements);
	      props.define("view", view);
	      win.show({
	        x: pos$$1.x > winx / 2 ? 0 : winx - 370,
	        y: 0
	      });
	      $$("webix_debug_inspector").setValues(config.data);
	      $$("webix_debug_inspector_head").setValue(config.head);
	    } else win.hide();
	  };

	  var infi = function (value) {
	    if (value >= 100000) return "Any";
	    return value;
	  };

	  var log_level = function (data, prefix, now) {
	    window.console.log((data == now ? ">>" : "  ") + prefix + data.name + " / " + data.config.id);
	    prefix += "  ";
	    if (data._cells) for (var i = 0; i < data._cells.length; i++) {
	      log_level(data._cells[i], prefix, now);
	    }
	    if (data._head_cell) log_level(data._head_cell, prefix, now);
	    if (data._body_cell) log_level(data._body_cell, prefix, now);
	  };

	  attachEvent("onLoadError", function (xhttp, owner) {
	    var text; //xhttp can be of blob or arraybuffer type, or just empty in case of no data

	    try {
	      text = xhttp.responseText;
	    } catch (e) {
	      text = "";
	    }

	    text = text || "[EMPTY DATA]";
	    var error_text = "Data loading error, check console for details";
	    if (text.indexOf("<?php") === 0) error_text = "PHP support missed";else if (text.indexOf("WEBIX_ERROR:") === 0) error_text = text.replace("WEBIX_ERROR:", "");
	    message({
	      type: "debug",
	      text: error_text,
	      expire: -1
	    });

	    if (window.console) {
	      var logger = window.console;
	      logger.log("Data loading error");
	      logger.log("Object:", owner);
	      logger.log("Response:", text);
	      if (xhttp) logger.log("XHTTP:", xhttp);
	    }
	  });
	  ready(function () {
	    var path = document.location.href;

	    if (path.indexOf("file:") === 0) {
	      message({
	        type: "error",
	        text: "Please open sample by http,<br>not as file://",
	        expire: -1
	      });
	    }
	  });
	  var ignore = {
	    "_inner": true,
	    "awidth": true,
	    "cheight": true,
	    "bheight": true,
	    "aheight": true
	  };
	  var uiConfig = {
	    view: "contextmenu",
	    id: "webix:debugmenu",
	    on: {
	      onBeforeShow: function (e) {
	        if (!e.ctrlKey) return false;
	        var view = locate(e,
	        /*@attr*/
	        "view_id");
	        if (!view) return false;
	        this.config.lastTarget = view;
	        blockEvent();
	        delay(function () {
	          unblockEvent();
	        });
	      },
	      onShow: function () {
	        var view = $$(this.config.lastTarget);
	        var info = "<span style='color:#888'>" + view._settings.id + "<sup style='float:right'>[" + view.name + "]</sup></span>";
	        document.getElementById("webix_debug_cmx").innerHTML = info;
	      }
	    },
	    data: ["<div id='webix_debug_cmx'></div>", {
	      id: "inspect",
	      value: "Inspect"
	    }, {
	      id: "docs",
	      value: "Documentation"
	    }, {
	      value: "Log to Console",
	      submenu: [{
	        id: "size",
	        value: "Sizes"
	      }, {
	        id: "tree",
	        value: "Tree"
	      }, {
	        id: "dump",
	        value: "Dump"
	      }]
	    }],
	    click: function (id, ev) {
	      //mixing two object result in confusion
	      var obj = $$(this.config.lastTarget);

	      if (id == "dump") {
	        window.console.info("\n" + obj.name + " / " + obj.config.id);
	        window.console.log("\nView: ", obj, ", Config: ", obj.config, ", Data: ", obj.data);
	        window.console.log(obj.$view);
	      }

	      if (id == "tree") {
	        var now = obj;

	        while (obj.getParentView()) {
	          obj = obj.getParentView();
	        }

	        window.console.log("");
	        log_level(obj, "", now);
	      }

	      if (id == "size") {
	        window.console.info("");
	        window.console.info("\n" + obj.name + " / " + obj.config.id);
	        window.console.info("\n[min]   ", obj.config.width, " x ", obj.config.height);
	        var sizes = obj.$getSize(0, 0);
	        window.console.info("[max]    ", infi(sizes[1]), " x ", infi(sizes[3]) + (obj.config.autoheight ? ", auto height" : ""));
	        window.console.info("[gravity]   ", obj.config.gravity);
	        window.console.info("\n[content]    ", obj._content_width, " x ", obj._content_height);
	        window.console.info("[last set]   ", obj._last_size[0], " x ", obj._last_size[1]);
	        if (obj._settings._inner) window.console.info("\n[borders]   ", "left:", !obj._settings._inner.left, "\ttop:", !obj._settings._inner.top, "\tright:", !obj._settings._inner.right, "\tbottom:", !obj._settings._inner.bottom);else window.console.info("\n[borders]   none");
	      }

	      if (id == "docs") window.open("http://docs.webix.com/api__refs__ui." + obj.name + ".html", "__blank");

	      if (id == "inspect") {
	        show_inspector(this.config.lastTarget, ev);
	      }
	    }
	  };
	  ready(function () {
	    uiConfig.master = document.body;
	    ui(uiConfig);
	  });
	}

	var DataRecord = exports.proto({
	  name: "DataRecord",
	  isVisible: function () {
	    return true;
	  },
	  $init: function (config) {
	    this.data = config || {};
	    var id = config && config.id ? config.id : uid();
	    this._settings = {
	      id: id
	    };
	    ui.views[id] = this;
	  },
	  getValues: function () {
	    return this.data;
	  },
	  setValues: function (data, update) {
	    this.data = update ? exports.extend(this.data, data, true) : data;
	    this.callEvent("onChange", [data]);
	  },
	  refresh: function () {
	    this.callEvent("onBindRequest");
	  }
	}, EventSystem, BaseBind, AtomDataLoader, Settings);

	var DataValue = exports.proto({
	  name: "DataValue",
	  isVisible: function () {
	    return true;
	  },
	  $init: function (config) {
	    if (!config || isUndefined(config.value)) this.data = config || "";
	    var id = config && config.id ? config.id : uid();
	    this._settings = {
	      id: id
	    };
	    ui.views[id] = this;
	  },
	  setValue: function (value) {
	    this.data = value;
	    this.callEvent("onChange", [value]);
	  },
	  getValue: function () {
	    return this.data;
	  },
	  refresh: function () {
	    this.callEvent("onBindRequest");
	  }
	}, EventSystem, BaseBind);

	var TreeCollection = exports.proto({
	  name: "TreeCollection",
	  $init: function () {
	    exports.extend(this.data, TreeStore, true);
	    this.data.provideApi(this, true);
	    exports.extend(this, TreeDataMove, true);
	  }
	}, TreeDataLoader, DataCollection);

	//----------------

	exports.Date = wDate;
	exports.Number = Number$1;
	exports.promise = Deferred;
	exports.DataCollection = DataCollection;
	exports.DataRecord = DataRecord;
	exports.DataValue = DataValue;
	exports.TreeCollection = TreeCollection;
	exports.ActiveContent = ActiveContent;
	exports.AtomDataLoader = AtomDataLoader;
	exports.AtomRender = AtomRender;
	exports.AutoScroll = AutoScroll;
	exports.AutoTooltip = AutoTooltip;
	exports.BaseBind = BaseBind;
	exports.BindSource = BindSource;
	exports.Canvas = Canvas;
	exports.CodeParser = CodeParser;
	exports.CollectionBind = CollectionBind;
	exports.ContextHelper = ContextHelper;
	exports.CopyPaste = CopyPaste;
	exports.CustomPrint = CustomPrint;
	exports.CustomScroll = temp$1;
	exports.DataMarks = DataMarks;
	exports.DataMove = DataMove;
	exports.DataLoader = DataLoader;
	exports.DataState = DataState;
	exports.DataStore = DataStore;
	exports.Destruction = Destruction;
	exports.DragControl = DragControl;
	exports.DragItem = DragItem;
	exports.DragOrder = DragOrder;
	exports.EditAbility = EditAbility;
	exports.EventSystem = EventSystem;
	exports.FlexLayout = FlexLayout;
	exports.Group = Group;
	exports.GroupMethods = GroupMethods;
	exports.GroupStore = GroupStore;
	exports.HTMLOptions = HTMLOptions;
	exports.HtmlMap = HtmlMap;
	exports.IdSpace = IdSpace;
	exports.KeysNavigation = KeysNavigation;
	exports.MapCollection = MapCollection;
	exports.Modality = Modality;
	exports.MouseEvents = MouseEvents;
	exports.Movable = Movable;
	exports.NavigationButtons = NavigationButtons;
	exports.OverlayBox = OverlayBox;
	exports.PagingAbility = PagingAbility;
	exports.ProgressBar = ProgressBar;
	exports.RecordBind = RecordBind;
	exports.RenderStack = RenderStack;
	exports.ResizeArea = ResizeArea;
	exports.SingleRender = SingleRender;
	exports.Scrollable = Scrollable;
	exports.SelectionModel = SelectionModel;
	exports.Settings = Settings;
	exports.Sparklines = Sparklines;
	exports.TablePaste = TablePaste;
	exports.TooltipControl = TooltipControl;
	exports.Touch = Touch;
	exports.TreeAPI = TreeAPI;
	exports.TreeClick = TreeClick;
	exports.TreeDataLoader = TreeDataLoader;
	exports.TreeDataMove = TreeDataMove;
	exports.TreeRenderStack = TreeRenderStack;
	exports.TreeStateCheckbox = TreeStateCheckbox;
	exports.TreeStore = TreeStore;
	exports.TreeTablePaste = TreeTablePaste;
	exports.TreeType = TreeType;
	exports.UIManager = UIManager;
	exports.Undo = Undo;
	exports.UploadDriver = UploadDriver;
	exports.ValidateCollection = ValidateCollection;
	exports.ValidateData = ValidateData;
	exports.ValueBind = ValueBind;
	exports.Values = Values;
	exports.VirtualRenderStack = VirtualRenderStack;
	exports.VRenderStack = VRenderStack;
	exports.html = html$1;
	exports.skin = skin$5;
	exports.debug = debug;
	exports.i18n = i18n;
	exports.ready = ready;
	exports.env = env;
	exports.color = color;
	exports.csv = csv$1;
	exports.clipbuffer = clipbuffer;
	exports.history = history;
	exports.storage = storage;
	exports.template = template;
	exports.type = type;
	exports.markup = markup;
	exports.editors = editors;
	exports.animate = animate;
	exports.print = print;
	exports.rules = rules;
	exports.patterns = patterns;
	exports.fullscreen = fullscreen;
	exports.version = version$1;
	exports.name = name$1;
	exports.level_in = level_in;
	exports.level_out = level_out;
	exports.clone = clone;
	exports.copy = copy;
	exports.single = single;
	exports.bind = bind;
	exports.exec = exec;
	exports.wrap = wrap;
	exports.isUndefined = isUndefined;
	exports.delay = delay;
	exports.once = once;
	exports.uid = uid;
	exports.toNode = toNode;
	exports.toArray = toArray;
	exports.toFunctor = toFunctor;
	exports.isArray = isArray;
	exports.isDate = isDate;
	exports.PowerArray = PowerArray;
	exports._events_final_destructor = _events_final_destructor;
	exports._event = _event;
	exports.event = event$1;
	exports.eventRemove = eventRemove;
	exports.callEvent = callEvent;
	exports.attachEvent = attachEvent;
	exports.detachEvent = detachEvent;
	exports.blockEvent = blockEvent;
	exports.unblockEvent = unblockEvent;
	exports.mapEvent = mapEvent;
	exports.hasEvent = hasEvent;
	exports.stringify = stringify;
	exports.toPNG = toPNG;
	exports.toCSV = toCSV;
	exports.toPDF = toPDF;
	exports.toExcel = toExcel;
	exports.alert = alert;
	exports.confirm = confirm;
	exports.modalbox = modalbox;
	exports.message = message;
	exports.editStop = editStop;
	exports.ajax = ajax;
	exports.jsonp = jsonp;
	exports.DataDriver = DataDriver;
	exports.dp = dp;
	exports.DataProcessor = DataProcessor;
	exports.remote = remote;
	exports.require = require;
	exports.proxy = proxy$a;
	exports.send = send;
	exports.ui = ui;
	exports.$$ = $$;

	Object.defineProperty(exports, '__esModule', { value: true });

})));
