/**
 * @license
 * webix UI v.8.4.0
 * 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';

  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);
  }

  function _slicedToArray(arr, i) {
    return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
  }

  function _toConsumableArray(arr) {
    return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
  }

  function _arrayWithoutHoles(arr) {
    if (Array.isArray(arr)) {
      for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];

      return arr2;
    }
  }

  function _arrayWithHoles(arr) {
    if (Array.isArray(arr)) return arr;
  }

  function _iterableToArray(iter) {
    if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
  }

  function _iterableToArrayLimit(arr, i) {
    var _arr = [];
    var _n = true;
    var _d = false;
    var _e = undefined;

    try {
      for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
        _arr.push(_s.value);

        if (i && _arr.length === i) break;
      }
    } catch (err) {
      _d = true;
      _e = err;
    } finally {
      try {
        if (!_n && _i["return"] != null) _i["return"]();
      } finally {
        if (_d) throw _e;
      }
    }

    return _arr;
  }

  function _nonIterableSpread() {
    throw new TypeError("Invalid attempt to spread non-iterable instance");
  }

  function _nonIterableRest() {
    throw new TypeError("Invalid attempt to destructure non-iterable instance");
  }

  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 () {
        var d = new Deferred();
        if (state == 4) return d.reject(_value);else return d.resolve(_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);
  };

  // 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) {
      _power_array.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;
  } //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;
  } //adds extra methods for the array

  function _to_array(array) {
    return exports.extend(array || [], _power_array, true);
  } //can be used by _to_array()

  var _power_array = {
    //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 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 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] || _to_array(); //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] = _to_array();
        }

        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 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
      };
    },
    //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
      };
    },
    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);
      }, 0);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 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,
    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$2 = {
    $proxy: true,
    load: function () {
      return ajax(this.source);
    },
    save: function (view, update) {
      return proxy$2._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$3 = {
    $proxy: true,
    load: function () {
      return ajax(this.source);
    },
    save: function (view, update) {
      var xhr = ajax().headers({
        "Content-Type": "application/json"
      });
      return proxy$2._save_logic.call(this, update, xhr);
    }
  };

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

  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$5(name, source, extra) {
    assert(proxy$5[name], "Invalid proxy name: " + name);
    var copy$$1 = copy(proxy$5[name]);
    copy$$1.source = source;
    if (extra) exports.extend(copy$$1, extra, true);
    if (copy$$1.init) copy$$1.init();
    return copy$$1;
  }

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

    return value;
  };

  proxy$5.binary = proxy;
  proxy$5.debug = proxy$1;
  proxy$5.json = proxy$3;
  proxy$5.post = proxy$4;
  proxy$5.rest = proxy$2;
  proxy$5.GraphQL = GraphQL;

  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 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 (!env.touch && navigator.platform === "MacIntel" && navigator.maxTouchPoints > 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");
  }();

  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;
    }

    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]; //we need to add 1 to offsetWidth/Height because these methods return value as an integer
      //we can use ie9+ d.getBoundingClientRect().width

      width = Math.max(width, d.offsetWidth + 1);
      height = Math.max(height, d.offsetHeight + 1);
    }

    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 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).then(callback2, function () {
            return promise.reject();
          });
        }
      } else {
        // [ file, other ]
        var _callback = function () {
          if (count) {
            count--;

            require(module[module.length - count - 1]).then(_callback, function () {
              return promise.reject();
            });
          } else {
            promise.resolve();
          }
        };

        _callback();
      }

      return promise;
    }

    if (_modules[module] !== true) {
      var fullpath = module;

      if (callback === true) {
        //sync mode
        exec(ajax().sync().get(fullpath).responseText);
        _modules[module] = true;
        return promise.resolve();
      }

      if (!_modules[module]) //first call
        _modules[module] = [promise];else {
        _modules[module].push(promise);

        return promise;
      }

      var onerror = function () {
        var calls = _modules[module];
        _modules[module] = false;

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

      var onload = function () {
        var calls = _modules[module];
        _modules[module] = true;

        for (var i = 0; i < calls.length; i++) {
          calls[i].resolve();
        }
      }; //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
        });
        link.onload = onload;
        link.onerror = onerror;
        document.getElementsByTagName("head")[0].appendChild(link);
      } else {
        var newScript = document.createElement("script");
        newScript.onload = onload;
        newScript.onerror = onerror;
        document.getElementsByTagName("head")[0].appendChild(newScript);
        newScript.src = fullpath;
      }
    } 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 = [];
      var types = [];
      var hidden = [];
      var cellTypes = {
        n: "number",
        d: "date",
        s: "string",
        b: "boolean"
      };

      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]);
              if (cell.t) types.push([row - yCorrection, col - xCorrection, cellTypes[cell.t]]);
            }
          }

          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++) {
            var item = widths[_i];

            if (item) {
              var index = _i - xCorrection;
              sizes.push(["column", index, Math.round(item.wch / (8.43 / 70))]); //mode, colind, value

              if (item.hidden) hidden.push(["column", index]);
            }
          }
        }

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

          for (var _i2 = 0; _i2 < heights.length; _i2++) {
            var _item = heights[_i2];

            if (_item) {
              var _index = _i2 - yCorrection;

              sizes.push(["row", _index, _item.hpx]); //mode ("row", "column"), rowind, value

              if (_item.hidden) hidden.push(["row", _index]);
            }
          }
        }
      }

      return {
        data: all,
        spans: spans,
        styles: styles,
        sizes: sizes,
        types: types,
        hidden: hidden,
        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 silentErrorMarker = {};
  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$5.$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 type,
          details = arguments[2] || null;

      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
        type = call;
        call = arguments[2];
      }

      var d = this._fetch(url, type, details);

      if (d && d.then) return d.then(function (data) {
        _this._onLoad(data);

        if (call) ajax.$callback(_this, call, "", data, -1);
        return data;
      }, function (x) {
        return _this._onLoadError(x);
      });
    },
    _fetch: function (url, type, details) {
      var _this2 = this;

      var result;
      if (type || !this.data.driver) this.data.driver = DataDriver[type || "json"];
      if (!this.callEvent("onBeforeLoad", [])) return Deferred.reject(); //proxy	

      url = proxy$5.$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) {
        return result.then(function (data) {
          // component destroyed, or clearAll was issued
          if (_this2.$destructed || gen && _this2._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(silentErrorMarker);
          return data;
        });
      }

      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 {
        if (type || !this.data.driver) this.data.driver = DataDriver[type || "json"];

        this._onLoad(data);
      }
      return Deferred.resolve();
    },
    _syncData: function (data) {
      if (this.data && this.data.attachEvent) 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, false, "auto");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 _this3 = this;

      // webix loading object or uploaded file structure
      if (data && typeof data.text === "function" && !data.name) {
        data = data.text();
      }

      data = this.data.driver.toObject(data);
      if (data && data.then) data.then(function (data) {
        return _this3._onLoadContinue(data);
      });else this._onLoadContinue(data);
    },
    _onLoadError: function (xhttp) {
      if (xhttp !== silentErrorMarker) {
        //ignore error for dead components
        if (!this.$destructed) {
          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]), false, "auto");else this._onLoadError(loader);
        this._ignore_feed = false;
        this.callEvent("onAfterLoad", []);
      }, this);
      return false;
    }
  };

  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)) {
          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 "";
  };

  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.data.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, xScroll;
      var mode = this._settings.dragscroll;
      if (typeof mode !== "string") mode = this._settings.layout || "xy";
      xScroll = mode.indexOf("x") !== -1;
      yScroll = mode.indexOf("y") !== -1;
      var data = this._body || this.$view;
      var box = offset(data);
      var sense = Math.max((this._settings.rowHeight || (this.type && !isNaN(parseFloat(this.type.height)) ? this.type.height : 0)) + 5, 40); //dnd auto-scroll sensivity

      var reset = false;
      if (yScroll && this._auto_y_scroll(pos$$1, box, sense)) reset = true;
      if (xScroll && this._auto_x_scroll(pos$$1, box, sense)) reset = true;
      if (reset) this._auto_scroll_delay = delay(this._auto_scroll, this, [pos$$1], 100);
    },
    _auto_scroll_column: function (pos$$1) {
      var mode = this._settings.dragscroll;
      if (typeof mode === "string" && mode.indexOf("x") === -1) return;
      var data = this._header || this.$view;
      var box = offset(data);
      var sense = Math.max(this._settings.headerRowHeight || 0, 40);
      if (this._auto_x_scroll(pos$$1, box, sense)) this._auto_scroll_delay = delay(this._auto_scroll_column, this, [pos$$1], 100);
    },
    _auto_y_scroll: function (pos$$1, box, sense) {
      var top = box.y;
      var bottom = top + box.height;
      var scroll = this.getScrollState();
      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) {
        return this._auto_scrollTo(scroll.x, scroll.y - sense * 2, pos$$1, "y");
      } else if (pos$$1.y > bottom - sense) {
        return this._auto_scrollTo(scroll.x, scroll.y + sense * 2, pos$$1, "y");
      }

      return false;
    },
    _auto_x_scroll: function (pos$$1, box, sense) {
      var left = box.x;
      var right = left + box.width;
      var scroll = this.getScrollState();

      if (pos$$1.x < left + sense) {
        return this._auto_scrollTo(scroll.x - sense * 2, scroll.y, pos$$1, "x");
      } else if (pos$$1.x > right - sense) {
        return this._auto_scrollTo(scroll.x + sense * 2, scroll.y, pos$$1, "x");
      }

      return false;
    },
    _auto_scrollTo: function (x, y, pos$$1, mode) {
      if (this.callEvent("onBeforeAutoScroll", [pos$$1])) {
        this.scrollTo(x, y);
        this.callEvent("onAfterAutoScroll", []);
        var scroll = this.getScrollState();
        return scroll[mode] === (mode === "x" ? x : y);
      }

      return false;
    }
  };

  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
  }

  /*
  	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 state = {
    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: [],
    _noselect_element: null,
    _modality: [],
    _popups: _to_array(),
    _wait_animate: null
  };

  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 _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;
    }

    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;
      if (element.config.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 (popup && !element._settings.readonly) {
        if (_typeof(popup) == "object" && !popup.name) {
          popup = element._settings.popup = ui(popup)._settings.id;

          element._destroy_with_me.push($$(popup));
        }

        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 TooltipControl = {
    _tooltip_masters: _to_array(["dummy"]),
    _tooltip_exist: 0,
    overflow: false,
    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._tooltip._css_name = "webix_tooltip webix_global_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 () {
          return _this._hide_tooltip();
        });
        this._click_event = attachEvent("onClick", function () {
          return _this._hide_tooltip();
        });
      }
    },
    getTooltip: function () {
      return this._tooltip;
    },
    _move_tooltip: function (e) {
      var c = {};
      var node = e.target;

      while (node instanceof Element && node.tagName != "HTML") {
        // find `webix_tooltip` marker
        if (!c.first || !c.overflow) {
          var text = node.getAttribute("webix_tooltip");
          c.first = c.first || text;
          if (text && node.scrollWidth > node.clientWidth) c.overflow = text;
        } // find tooltip master


        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);
          return this.$tooltipMove(node, e, c);
        }

        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;
      var tip = node.webix_tooltip;

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

          this._last = null;
        }

        delete node.webix_tooltip;
        this._tooltip_exist--;
        this._tooltip_masters[tip] = null;
      }

      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 = _to_array(["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: "",
        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, c) {
      var m = this._tooltip_masters[t.webix_tooltip];
      if (m.$tooltipMove && m != this) return m.$tooltipMove(t, e, c);
      var overflow = isUndefined(m.overflow) ? this.overflow : m.overflow;
      var time = isUndefined(m.delay) ? this.delay : m.delay;
      var text = overflow ? c.overflow : c.first;
      if (time > 0) this._hide_tooltip();
      this._before_show_delay = delay(this._tooltip.show, this._tooltip, [text || {}, pos(e)], time);
    }
  };

  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(index) {
    if (!isUndefined(index)) {
      env.zIndexBase = Math.max(env.zIndexBase, index + 1);
      return index;
    }

    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 || window.event]);
    });
  });

  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;
    }
  };

  function callback(config, result) {
    // prompt
    if (config.type.indexOf("prompt") != -1) {
      if (result === false) {
        config._promise.reject();
      } else {
        var inputBox = config._box.querySelector(".webix_popup_input");

        var _input = inputBox.querySelector("input");

        if (config.input.required && !_input.value) {
          config.input.invalid = true;
          addCss(inputBox, "webix_popup_invalid");
          return;
        } else {
          result = _input.value || "";

          config._promise.resolve(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 order = modalbox.order;
    var count = order.length;
    var source = e.target;

    if (count > 0 && message.keyboard) {
      e = e || window.event;
      var code = e.which || e.keyCode;
      if (code != 13 && code != 32 && code != 27) return;
      var activeBox;

      for (var i = count - 1; i >= 0; i--) {
        var box = modalbox.pull[order[i]];

        if (box.container && box.container.contains(source)) {
          var insideModalbox = box._box != source && box._box.contains(source);

          if (code == 32 && insideModalbox) // ignore space inside input
            return;else {
            activeBox = box;
            break;
          }
        }
      }

      if (!activeBox) activeBox = modalbox.pull[order[order.length - 1]];
      if (code == 13 || code == 32) callback(activeBox, true);else if (code == 27) callback(activeBox, false);
      preventEvent(e);
      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) {
      cover = cover || Array.from(node.querySelectorAll(".webix_modal_cover")).find(function (el) {
        return el.parentNode == node;
      });

      if (cover) {
        if (!node.modality) {
          cover.style.display = "none";
          removeCss(node, "webix_modalbox_inside");
        } else if (node.modality == 1) {
          cover.style.display = "inline-block";
          addCss(node, "webix_modalbox_inside");
        }
      }
    }

    return cover;
  }

  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 input(config) {
    return "<div tabindex='0' class='webix_popup_input webix_el_text" + (config.required ? " webix_required" : "") + "'><input value='" + template.escape(config.value || "") + "' placeholder='" + template.escape(config.placeholder || "") + "'></input></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 () {
      if (text) 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, hasInput) {
    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" + (hasInput ? " webix_popup_label" : "") + "\"><span>" + (config.content ? "" : config.text || "") + "</span></div>";
    inner += "<div  class=\"webix_popup_controls\">";
    if (hasInput) inner += input(config.input);
    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);
    }

    if (config.type.indexOf("prompt") != -1) {
      var inputBox = box.querySelector(".webix_popup_input");

      var _input2 = inputBox.querySelector("input");

      _input2.oninput = function () {
        if (config.input.invalid) {
          removeCss(inputBox, "webix_popup_invalid");
          config.input.invalid = false;
        }
      };
    }

    box.onclick = function (e) {
      e = e || window.event;
      var source = e.target;
      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, hasInput) {
    var box = config.tagName ? config : _boxStructure(config, ok, cancel, hasInput);
    var container = config.container;
    var containerWidth = container ? container.offsetWidth : window.innerWidth || document.documentElement.offsetWidth;
    var containerHeight = container ? container.offsetHeight : window.innerHeight || document.documentElement.offsetHeight;
    var containerLeft = container ? container.scrollLeft : 0;
    var containerTop = container ? container.scrollTop : 0;
    if (config.container) box.style.position = "absolute";
    toNode((config.container || document.body).appendChild(box));
    var cover = modality(true, config.container);
    var x = config.left || Math.abs(containerLeft + Math.floor((containerWidth - box.offsetWidth) / 2));
    var y = config.top || Math.abs(containerTop + Math.floor((containerHeight - box.offsetHeight) / 2));
    if (config.position == "top") box.style.top = "-3px";else {
      box.style.top = y + "px";

      if (cover) {
        cover.style.top = containerTop + "px";
        cover.style.left = containerLeft + "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 promptPopup(config) {
    return _createBox(config, true, true, true);
  }

  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);
  }
  function prompt() {
    var text = box_params.apply(this, arguments);
    text.type = text.type || "prompt";
    text.input = text.input || {};
    return promptPopup(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);

      case "prompt":
        return promptPopup(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);

  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;
      this._pos = this._setPosition();
      var viewConfig = view.config;

      if (view.setPosition) {
        viewConfig.fullscreen = true;
        view.resize();
        return view;
      } else {
        this._fullscreen = ui({
          view: "window",
          head: this._getHeadConfig(config),
          css: config.css || "",
          fullscreen: true,
          borderless: 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);
            this._pos.active = this._parent.getActiveId ? this._parent.getActiveId() == viewConfig.id : false;
          }
        } else {
          this._parent = view.$view.parentNode;
          this._pos.node = create("div"); //save old position

          this._parent.replaceChild(this._pos.node, view.$view);
        }

        this._fullscreen.getBody().addView(view.$html ? {
          view: "template",
          content: view.$view,
          css: "webix_fullscreen_html"
        } : view);

        this._fullscreen.show();

        this._setSizes(view);

        return this._fullscreen;
      }
    },
    exit: function () {
      if (this._view) {
        var viewConfig = this._view.config;

        this._setPosition(true);

        if (this._view.setPosition) {
          viewConfig.fullscreen = false;

          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.node);
          } else {
            if (!isUndefined(this._pos.index)) {
              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;
      delete this._fullscreen;
    },
    _setPosition: function (restore) {
      var _this = this;

      var view = this._view;
      var oldStyles = {};

      if (view.setPosition) {
        if (restore) view.setPosition(this._pos.left, this._pos.top);else {
          oldStyles.left = view.config.left;
          oldStyles.top = view.config.top;
          view.setPosition(0, 0);
        }
      } else {
        var rules = ["position", "top", "bottom", "left", "right"];
        var style = view.$view.style;
        rules.forEach(function (rule) {
          if (restore) style[rule] = _this._pos[rule];else {
            oldStyles[rule] = style[rule];
            style[rule] = rule == "position" ? "relative" : 0;
          }
        });
      }

      return oldStyles;
    },
    _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 _this2 = 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 () {
              _this2.exit();
            }
          }]
        };
      }
    }
  };

  var UIManager = {
    _view: null,
    _hotkeys: {},
    _focus_time: 0,
    _tab_time: 0,
    _mouse_time: 0,
    _controls: {
      "esc": "escape",
      "up": "arrowup",
      "down": "arrowdown",
      "left": "arrowleft",
      "right": "arrowright",
      "pgdown": "pagedown",
      "pgup": "pageup",
      "space": " ",
      "multiply": "*",
      "add": "+",
      "subtract": "-",
      "decimal": ".",
      "divide": "/",
      "pausebreak": "pause",
      "5numlocked": "clear"
    },
    _inputs: {
      "input": 1,
      "button": 1,
      "textarea": 1,
      "select": 1
    },
    _enable: function () {
      // attaching events here
      event$1(document, "keydown", this._keypress, {
        bind: this
      });
      event$1(document.body, "click", this._focus_click, {
        capture: true,
        bind: this
      });
      event$1(document.body, "mousedown", function () {
        this._mouse_time = new Date();
      }, {
        bind: this
      });
      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) {
      for (var i = 0; i < modalbox.order.length; i++) {
        if (modalbox.pull[modalbox.order[i]]._box.contains(e.target)) return;
      }

      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 true;

      if (view) {
        if (this.canFocus(view)) {
          // keep form focus
          if (this._view && this._view.getFormView() == view && this._view.focus) this._view.focus();else this.setFocus(view);
        } //remove focus from an unreachable view
        else if (view.$view.contains(e.target)) e.target.blur();
      } else 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);
    },
    _top_modal: function (view) {
      var modality = state._modality;
      if (!modality.length) return true;
      var top = view.queryView(function (a) {
        return !a.getParentView();
      }, "parent") || view;
      return (top.$view.style.zIndex || 0) >= Math.max.apply(Math, _toConsumableArray(modality));
    },
    canFocus: function (view) {
      if (document.body.modality || view.$view.modality || view.queryView(function (view) {
        return view.$view.modality;
      }, "parent")) //modalbox
        return false;
      return view.isVisible() && view.isEnabled() && !view.config.disabled && this._top_modal(view) && !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;
    },
    _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", []);
    },
    _keypress: function (e) {
      var code = e.which || e.keyCode; // numpad keys

      if (code > 95 && code < 106) code -= 48;
      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._check_keycode(e) === false) {
        preventEvent(e);
        return false;
      }
    },
    // dir - getNext or getPrev
    _focus_logic: function (dir, focus) {
      var next = focus || this.getFocus();

      if (next) {
        dir = dir || "getNext";
        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) {
            if (focus) document.activeElement.blur();
            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 () {
        view = $$(document.activeElement);
        if (view && !UIManager.canFocus(view)) return UIManager._focus_logic(mode ? "getNext" : "getPrev", view);
        UIManager.setFocus(view, true, true);
      });
    },
    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) for (var i = 0; i < cells.length; i++) {
        if (this.canFocus(cells[i])) return cells[i];
      } //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 = _power_array.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) for (var i = cells.length - 1; i >= 0; i--) {
        if (this.canFocus(cells[i])) return this.getPrev(cells[i], true);
      }
      if (_inner_call && this.canFocus(view)) return view; //fallback from top to bottom

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

      if (p_cells) {
        var index$$1 = _power_array.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 this.getPrev(parent, true);
    },
    addHotKey: function (keys, handler, view) {
      assert(handler, "Hot key handler is not defined");

      var code = this._parse_keys(keys);

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

      this._hotkeys[code].push({
        handler: handler,
        view: view
      });

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

      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 (key, ctrl, shift, alt, meta) {
      //key can be undefined (browser autofill)
      return (key || "").toLowerCase() + "_" + ["", ctrl ? "1" : "0", shift ? "1" : "0", alt ? "1" : "0", meta ? "1" : "0"].join("");
    },
    _check_keycode: function (e) {
      var keyCode = e.which || e.keyCode;
      var is_any = !e.ctrlKey && !e.altKey && !e.metaKey && keyCode != 9 && keyCode != 27 && keyCode != 13;

      var code = this._keycode(e.key, e.ctrlKey, e.shiftKey, e.altKey, e.metaKey);

      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 {
          letter = controls[parts[i]] || parts[i];
        }
      }

      return this._keycode(letter, ctrl, shift, alt, meta);
    },
    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, "auto");
        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) {
          if (fullscreen._fullscreen == top) fullscreen.exit();

          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
      //submenu can trigger this handler two times, preserve a single destructor reference
      var t = this._destructor_handler = 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._parent_cell = null;
      this._viewobj = this.$view = this._contentobj = this._dataobj = null;
      this._evs_events = this._evs_handlers = this._evs_map = {}; //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 = _to_array();

    _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, "auto");else if (!target.filter) {
        var pod = {};
        pod[rule] = data;
        if (target._check_data_feed(data)) target.setValues(pod, false, "auto");
      } 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, "auto");else if (this.setValues) this.setValues(data, false, "auto");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, "auto");else if (!target.filter) {
        if (!data && target.clear) target.clear("auto");else {
          if (target._check_data_feed(data)) target.setValues(clone(data), false, "auto");
        }
      } 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;
      this._bind_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(this._bind_id);
      });
      if (this.refresh && this.isVisible(this._bind_id)) this.refresh();
    },
    unbind: function () {
      if (this._bind_source) {
        var target = $$(this._bind_source);
        if (target) target.removeBind(this._bind_id);
        this.detachEvent(this._bind_refresh_handler);
        this._bind_source = this._bind_refresh_handler = 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;
    },
    // needed only to maintain the deprecated ActiveContent module
    // do not use it anywhere else
    $setNode: function (node) {
      this._viewobj = this._dataobj = this.$view = node;
    },
    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;
    },
    _fix_cover: function () {
      if (this._disable_cover && !this._disable_cover.parentNode) this._viewobj.appendChild(this._disable_cover);
    },
    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();

            if (this.callEvent) {
              this.callEvent("onViewShow", []);
              if (this._signal_hidden_cells) _each(this, this._signal_hidden_cells);
            }

            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.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 base = {
    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 = base.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 base.api.$setSize.call(this, x, y);
    }
  };
  var view$1 = exports.protoUI(api$1, base.view);
  var base$1 = {
    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._viewobj.className = this._css_name;
      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_name: "webix_tooltip",
    css_setter: function (value) {
      if (_typeof(value) === "object") value = createCss(value);
      this._viewobj.className = this._css_name + " " + 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 * 2 - this._contentobj.offsetHeight;
          if (positionY < 0) positionY = 0;
        }
        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$1.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 = {};
        if (value.overflow && isUndefined(value.template)) value.template = "";

        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, c) {
      var tooltip = this._settings.tooltip;
      var overflow = !tooltip || isUndefined(tooltip.overflow) ? TooltipControl.overflow : tooltip.overflow;
      var time = !tooltip || isUndefined(tooltip.delay) ? TooltipControl.delay : tooltip.delay;
      var text = overflow ? c.overflow : c.first;
      if (time > 0) TooltipControl._hide_tooltip();
      TooltipControl._before_show_delay = delay(this._show_tooltip, this, [t, e, text], time);
    },
    _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);

      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 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) {
            var master = settings.master || window;
            node._has_animation = null;
            if (!master.$destructed && settings.callback) settings.callback.call(master, 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,
    timelineItemHeight: 70,
    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,
    dataPadding: 12,
    calendarWeekHeaderHeight: 18,
    padding: 0,
    accordionType: "accordion",
    optionHeight: 32,
    organogramLineColor: "#CCD7E6",
    timelineColor: "#1CA1C1",
    backColor: "#ffffff",
    //colorboard
    colorPadding: 4
  };

  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,
    timelineItemHeight: 50,
    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,
    dataPadding: 12,
    calendarWeekHeaderHeight: 18,
    padding: 0,
    accordionType: "accordion",
    optionHeight: 24,
    organogramLineColor: "#CCD7E6",
    timelineColor: "#1CA1C1",
    backColor: "#ffffff",
    //colorboard
    colorPadding: 4
  };

  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,
    timelineItemHeight: 70,
    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,
    dataPadding: 10,
    calendarWeekHeaderHeight: 24,
    padding: 0,
    accordionType: "accordion",
    optionHeight: 32,
    timelineColor: "#3498db",
    backColor: "#ffffff",
    //colorboard
    colorPadding: 4
  };

  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,
    timelineItemHeight: 50,
    inputHeight: 30,
    buttonHeight: 30,
    inputPadding: 3,
    menuHeight: 28,
    labelTopHeight: 16,
    inputSpacing: 4,
    borderWidth: 1,
    sliderHandleWidth: 10,
    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,
    dataPadding: 10,
    calendarWeekHeaderHeight: 24,
    padding: 0,
    accordionType: "accordion",
    optionHeight: 23,
    timelineColor: "#3498db",
    backColor: "#ffffff",
    //colorboard
    colorPadding: 4
  };

  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,
    timelineItemHeight: 70,
    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,
    dataPadding: 10,
    calendarWeekHeaderHeight: 24,
    padding: 0,
    accordionType: "accordion",
    optionHeight: 32,
    timelineColor: "#b300b3",
    backColor: "#393939",
    //colorboard
    colorPadding: 4
  };

  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
    },
    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);
      });

      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 = Touch._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 (!Touch._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 {
          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

        } else if (env.isMac) {
          var _view = $$(Touch._start_context);

          if (_view && _view.$hasYScroll && _view.$hasYScroll() && e.cancelable) {
            return preventEvent(e);
          }
        }
      }

      if (Touch._scroll_mode && e.cancelable) 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 (!speed) {
        node.style[env.transform] = "";
        return;
      }

      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(function () {
        Touch._set_scroll_pos();

        if (Touch._scroll_stat && !Touch._scroll_stat.hidden) {
          if (Touch._scroll[0]) Touch._scroll[0].style.visibility = "visible";
          if (Touch._scroll[1]) Touch._scroll[1].style.visibility = "visible";
        }
      }, 0);
    },
    _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.visibility = "hidden";
      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";
      }

      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;
      if (Touch._disabled) 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._long_touched = true;

        Touch._translate_event("onLongTouch");

        callEvent("onClick", [Touch._start_context]); //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,
        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 (env.isSafari) addStyle(".webix_view{ -webkit-overflow-scrolling: touch; }");
      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: _to_array(["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);

        if (ctrl.attachEvent) ctrl.attachEvent("onDestruct", function () {
          return DragControl.unlink(ctrl);
        });
      }

      return index$$1;
    },
    unlink: function (ctrl) {
      var index$$1 = this._drag_masters.find(ctrl);

      if (index$$1 > -1) {
        // if active view was destroyed, stop dnd
        if (DragControl._active && DragControl._active.webix_drag == index$$1) DragControl._stopDrag(); // if last target was destroyed, reset it and continue dnd

        if (DragControl._last && DragControl._last.webix_drop == index$$1) DragControl._last = null;
        this._drag_masters[index$$1] = null;
      }
    },
    _createTouchDrag: function (e) {
      var dragCtrl = DragControl;

      var master = this._getActiveDragMaster(); // for data items only


      if (master && master.$longTouchLimit) {
        if (!dragCtrl._html && !dragCtrl.createDrag(e)) return;
        e.longtouch_drag = true;
        var pos$$1 = {
          x: e.x,
          y: e.y
        };
        var customPos = dragCtrl.$dragPos(pos$$1, e);
        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";
      }
    },

    /*
    	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(e);

        DragControl.destroyDrag(e);
      }

      DragControl._active = this;
      var evobj = env.mouse.context(e);
      DragControl._start_pos = evobj;
      DragControl._saved_event = e;
      var passive = env.touch ? {
        passive: false
      } : null;
      DragControl._webix_drag_mm = event$1(document.body, env.mouse.move, DragControl._startDrag, passive);
      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 (e) {
      DragControl._clean_dom_after_drag();

      DragControl._touch_animation = !e.cancelable;
    },
    //mouse was moved without button released - dnd started, update event handlers
    _startDrag: function (e) {
      // check touch scroll animation
      DragControl._touch_animation = !e.cancelable;

      if (env.touch && DragControl._touch_animation) {
        DragControl._clean_dom_after_drag();

        return DragControl.destroyDrag(e);
      } //prevent unwanted dnd


      var pos$$1 = env.mouse.context(e);

      var master = DragControl._getActiveDragMaster(); // only long-touched elements can be dragged


      var longTouchLimit = env.touch && master && master.$longTouchLimit && !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;
      if (!DragControl._html && !DragControl.createDrag(DragControl._saved_event)) return DragControl._clean_dom_after_drag();

      DragControl._clean_dom_after_drag(true);

      DragControl.sendSignal("start"); //useless for now

      var passive = env.touch ? {
        passive: false
      } : null;
      DragControl._webix_drag_mm = event$1(document.body, env.mouse.move, DragControl._moveDrag, passive);
      DragControl._webix_drag_mu = event$1(document, env.mouse.up, DragControl._stopDrag);

      DragControl._moveDrag(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 && e) {
        //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);
      }
      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 (master && master._auto_scroll_delay) master._auto_scroll_delay = window.clearTimeout(master._auto_scroll_delay);
      if (DragControl._dropHTML) remove(DragControl._dropHTML);
      DragControl._landing = DragControl._active = DragControl._last = DragControl._html = DragControl._dropHTML = null;
      DragControl._drag_context = 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._touch_animation) DragControl._createTouchDrag(ev);
  });

  var Movable = {
    move_setter: function (value) {
      if (value) {
        exports.extend(this, Move, true);
        DragControl.addDrag(this._headobj ? this._headobj : this.$view, this);
        delete this.move_setter; //prevent double initialization
      }

      return value;
    }
  };
  var Move = {
    $dragCreate: function (object, e) {
      if (this.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._viewobj);
      }
    },
    $dragDestroy: function (node, drag) {
      var view = this;

      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.callEvent("onViewMoveEnd", []);
      return;
    },
    $dragPos: function (pos$$1, e) {
      this.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._modality = zIndex(this._settings.zIndex);

          state._modality.push(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);
          this._modal_cover = null;
          var modality = state._modality;
          modality.splice(modality.indexOf(this._modality), 1);
          if (!modality.length) document.body.style.overflow = "";
        }
      }

      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,
          y: elPos.y - this._wsReady.y
        };
        if (this.$resizeMove) this.$resizeMove(progress);else {
          var config = this.config;
          var minWidth = config.minWidth || 100;
          var minHeight = config.minHeight || 100;
          if (progress.x < minWidth) progress.x = minWidth;else if (progress.x > config.maxWidth) progress.x = config.maxWidth;
          if (progress.y < minHeight) progress.y = minHeight;else if (progress.y > config.maxHeight) progress.y = config.maxHeight;
        }
        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", []);
    }
  };

  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;
      this._settings._inner = {
        top: false,
        left: false,
        right: false,
        bottom: false
      }; //set border flags

      if (!config.id) config.id = uid();

      _event(this._contentobj, "click", this._ignore_clicks, {
        bind: this
      });

      _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.headHeight) this._settings.headHeight = config.headHeight;
      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;
    },
    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 = zIndex(this._settings.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) {
            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,
              delta_y = 6,
              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();

        this.$view.style.position = fixed ? "fixed" : "absolute";

        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._settings.escHide || this._hide_timer) return; //do not hide submenu when clicking on menu folder

      if (e && e.showpopup && (e.showpopup == this._settings.id || this.getTopMenu && this.getTopMenu()._settings.id == e.showpopup)) return; //do not hide popup, when starting dnd with a long touch

      if (e && env.touch && e.longtouch_drag) return; //do not hide popup, when we have modal layer above the popup

      if (state._modality.length && this._viewobj.style.zIndex <= Math.max.apply(Math, _toConsumableArray(state._modality))) return; //ignore inside clicks and clicks in child-popups

      if (e) {
        var index$$1 = 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_single();
    },
    hidden_setter: function (value) {
      if (value) this.hide();else this.show();
      return !!value;
    },
    hide: function () {
      var index$$1 = this._hide_single();

      this._hide_sub_popups(index$$1);
    },
    _hide_single: function () {
      if (this.$destructed || this._settings.hidden) return;
      if (this._settings.modal) this._modal_set(false);

      this._hiding_process();

      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;
        }
      } // clear state


      var index$$1 = state._popups.find(this);

      if (index$$1 > -1) state._popups.removeAt(index$$1);
      return index$$1;
    },
    _hiding_process: function () {
      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();
    },
    //hide all child-popups
    _hide_sub_popups: function (index$$1) {
      if (index$$1 > -1) {
        var order = state._popups;

        for (var i = order.length - 1; i >= index$$1; i--) {
          if (order[i]._hide_point) //hide only popups, skip windows
            order[i]._hide_single();
        }
      }
    },
    destructor: function () {
      this.hide();
      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 height = this._settings.headHeight;
      var text = typeof value == "string";
      var config = {
        height: height,
        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, {
            height: height,
            view: "icon",
            icon: "wxi-close",
            click: function () {
              _this.hide();
            }
          }]
        };
      } else exports.extend(value, {
        borderless: true
      });

      state._parent_cell = this;
      this._head_cell = ui._view(value);

      var template = this._head_cell._viewobj.querySelector(".webix_win_title>div");

      if (template) template.style.lineHeight = height + "px";

      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 () {
      base.api.adjust.call(this);
      callEvent("onResize", []);

      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 = {
            left: left,
            top: top,
            width: width,
            height: height,
            maxWidth: maxWidth,
            maxHeight: maxHeight
          };

          this._settings.position.call(this, _state);

          if (_state.width != width || _state.height != height) this.$setSize(_state.width, _state.height);
          this.setPosition(_state.left, _state.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$1.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$1.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,
      escHide: true
    }
  }; //global longtouch handler

  attachEvent("onLongTouch", function (ev) {
    if (!ev || !ev.target) return;
    var view = $$(ev.target);

    if (view) {
      view = view.queryView(function (a) {
        return !a.getParentView();
      }, "parent") || view;
      var popups = state._popups;
      var index$$1 = popups.find(view);
      if (index$$1 !== -1) ev.click_view = index$$1;
    }
  });
  var view$3 = exports.protoUI(api$3, base$1.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 || window$1.event);
      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; // ignore contexmenu clicks for the popup or its body

      var view = $$(e);

      if (view) {
        var top = view.queryView(function (a) {
          return !a.getParentView();
        }, "parent") || view;
        if (top._ignore_clicks) top._ignore_clicks(e);
      } //event forced to close other popups|context menus


      callEvent("onClick", [e]);
      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;

      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 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 CopyPaste = {
    clipboard_setter: function (value) {
      if (env.touch) return value;
      if (value === true || value === 1) value = "modify";
      this.attachEvent("onAfterSelect", this._sel_to_clip);
      this.attachEvent("onAfterEditStop", function (v, ed) {
        var sel = this.getSelectedId(true);
        if (sel.length == 1 && ed.id == sel[0]) this._sel_to_clip();
      });
      this.attachEvent("onPaste", 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);
        }
      });
      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;
    },
    _sel_to_clip: function () {
      var _this = this;

      delay(function () {
        //wait until editor is closed
        if (!_this.getEditor || !_this.getEditor()) {
          var sel = _this.getSelectedId(true);

          var data = [];

          for (var i = 0; i < sel.length; i++) {
            var id = sel[i];

            var item = _this.getItem(id);

            data.push([_this.type.templateCopy(item)]);
          }

          var text = data.length === 1 ? data[0][0] : csv$1.stringify(data, _this._settings.delimiter);
          clipbuffer.set(text, _this);
          clipbuffer.focus();
          UIManager.setFocus(_this);
        }
      });
    },
    _paste: {
      // insert new item with pasted value
      insert: function (text) {
        var _this2 = this;

        text.forEach(function (value) {
          return _this2.add({
            value: value
          });
        });
      },
      // change value of each selected item
      modify: function (text) {
        var sel = this.getSelectedId(true);

        for (var i = 0; i < sel.length; i++) {
          if (isUndefined(text[i])) return;
          this.getItem(sel[i]).value = text[i];
          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) {
      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 = options.fit == "page" ? Infinity : 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) {
      var index = this.getIndexById(id) - (step || 1);
      return this.move(id, index < 0 ? 0 : index);
    },
    //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 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$5(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$5.$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 = _to_array(); //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);
      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 = _to_array(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 = _to_array(); //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 = _to_array([].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 = {};

          for (var _key2 in data.pull) {
            this.pull[_key2] = data.pull[_key2];
          }
        }

        if (data.branch && this.branch) {
          this.branch = copy(data.branch);
          this._filter_branch = null;
        }
      } else {
        this.order = _to_array();
        this.pull = {};
        var id, obj;
        if (isArray(target)) for (var _key3 = 0; _key3 < target.length; _key3++) {
          obj = id = target[_key3];
          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 _key4 in data) {
          this.order.push(_key4);
          this.pull[_key4] = {
            id: _key4,
            value: data[_key4]
          };
        }
      }

      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" && source.name != "TreeStore") {
        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) && !this.count()) //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.name != "TreeStore" && (!source.data || source.data.name != "DataStore" || source.data.name != "TreeStore")) {
          //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 = _to_array(); //this.feed = null;

      this._filter_order = null;
      if (!soft) this.url = null;
      this.callEvent("onClearAll", [soft]);
      this.refresh();
    },
    //converts index to id
    getIdByIndex: function (index) {
      assert(index >= 0, "DataStore::getIdByIndex Incorrect index");
      return this.order[index];
    },
    //converts id to index
    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 - array of settings
    		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 _this = this;

      var parameters;
      var sort = by;

      if (isArray(sort)) {
        sort = sort.map(function (a) {
          return _this._sort_init(a);
        });
        parameters = [sort];
      } else {
        sort = this._sort_init(by, dir, as);
        parameters = [sort.by, sort.dir, sort.as, sort];
      }

      if (!this.callEvent("onBeforeSort", parameters)) return;
      var sorter = this.sorting.create(sort);
      this.order = this._sort_core(sorter, this.order);
      if (this._filter_order) this._filter_order = this._sort_core(sorter, this._filter_order); //repaint self

      this.refresh();
      this.callEvent("onAfterSort", parameters);
    },
    _sort_init: function (by, dir, as) {
      var sort = by;
      if (typeof by == "function") sort = {
        as: by,
        dir: dir
      };else if (typeof by == "string") sort = {
        by: by,
        dir: dir,
        as: as
      };
      if (typeof sort.by == "string") sort.by = sort.by.replace(/#/g, "");
      return sort;
    },
    _sort_core: function (sorter, order) {
      if (this.order.length) {
        var pre = order.splice(0, this.$freeze); //get array of IDs

        var neworder = _to_array();

        for (var i = order.length - 1; i >= 0; i--) {
          neworder[i] = this.pull[order[i]];
        }

        neworder.sort(sorter);
        return _to_array(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 = _to_array();

      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) {
        if (isArray(config)) return this._multi(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;
          if (isNaN(b)) return 1;
          if (isNaN(a)) return -1;
          return a > b ? 1 : a < b ? -1 : 0;
        },
        "int": function (a, b) {
          a = a * 1;
          b = b * 1;
          if (isNaN(b)) return 1;
          if (isNaN(a)) return -1;
          return a > b ? 1 : a < b ? -1 : 0;
        },
        "string_strict": function (a, b) {
          if (!b) return 1;
          if (!a) return -1;
          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;
        }
      },
      _multi: function (methods) {
        var _this2 = this;

        methods = methods.map(function (c) {
          return _this2._dir(c.dir, _this2._by(c.by, c.as));
        });
        return function (a, b) {
          var result,
              i = 0;

          do {
            result = methods[i](a, b);
          } while (!result && methods[++i]);

          return result;
        };
      },
      _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) {
            var sort = isArray(state.sort) ? state.sort : [state.sort];

            for (var i = 0; i < sort.length; i++) {
              params.push("sort[" + sort[i].id + "]=" + encodeURIComponent(sort[i].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$5.$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$5.$parse(value);
      this.data.attachEvent("onBeforeFilter", bind(function (text, filtervalue) {
        var _this4 = this;

        var result; //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;
          var url = this._settings.dataFeed; //url data feed

          if (typeof url == "string") {
            var urldata = "filter[" + text + "]=" + encodeURIComponent(filtervalue);
            result = this._fetch(url + (url.indexOf("?") < 0 ? "?" : "&") + urldata, this._settings.datatype);
          } //js data feed
          else {
              var filter = {};
              filter[text] = filtervalue;

              if (typeof url == "function") {
                result = url.call(this, filtervalue, filter);
              } else if (url.$proxy && url.load) {
                result = url.load(this, {
                  filter: filter
                });
              }
            }

          if (result) {
            if (!result.then) result = Deferred.resolve(result);
            result.then(function (data) {
              _this4.clearAll();

              _this4._onLoad(data);

              _this4.data.callEvent("onAfterFilter", []);
            }, 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 _this = this;

      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_order.length) {
        var sort = this._last_order.map(function (id) {
          return {
            id: id,
            dir: _this._last_sorted[id].dir
          };
        });

        settings.sort = sort.length == 1 ? sort[0] : sort;
      } //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.markSorting();
      this._last_order = [];
      this._last_sorted = {};
      this.blockEvent();

      if (obj.order && obj.order.length) {
        this._hidden_column_order = _to_array([].concat(obj.order));
        var rs = obj.order.length - this._settings.rightSplit;
        this._hidden_split = [this._settings.leftSplit, rs, 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;

        for (var _i2 = 0; _i2 < columns.length; _i2++) {
          if (columns[_i2].id != obj.ids[_i2]) reorder = true;
        }

        if (reorder) {
          for (var _i3 = 0; _i3 < obj.ids.length; _i3++) {
            columns[_i3] = this.getColumnConfig(obj.ids[_i3]) || columns[_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 sort = obj.sort,
            multi = true;

        if (!isArray(sort)) {
          sort = [sort];
          multi = false;
        }

        for (var _i5 = 0; _i5 < sort.length; _i5++) {
          var _col = this.getColumnConfig(sort[_i5].id);

          if (_col) this._sort(_col.id, sort[_i5].dir, _col.sort, multi);
        }
      }

      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 _i6 = 0; _i6 < select.length; _i6++) {
          if (!select[_i6].row || this.exists(select[_i6].row)) this._select(select[_i6], 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);
      var fragile = this.addRowCss && env.touch && !this._settings.prerender;
      if (this._settings.dragscroll !== false && !fragile) 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_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) {
      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);
      }
    },
    $longTouchLimit: true,
    _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 && _power_array.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.from = this;

        if (this.callEvent("onBeforeDrag", [context, e])) {
          if (env.touch && this._touch_scroll == "touch") delay(function () {
            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

      if (this._marked && this._marked != target) {
        this._remove_css([this._marked], "webix_drag_over", true);

        this._marked = null;
      }

      if (!this._marked && target) {
        this._marked = target;

        this._add_css([target], "webix_drag_over", true);

        return target;
      }

      if (context.to) {
        return true;
      } else return false;
    },
    _add_css: function (source, css) {
      for (var i = 0; i < source.length; i++) {
        this.addCss(source[i], css);
      }
    },
    _remove_css: function (source, css) {
      for (var i = 0; i < source.length; i++) {
        this.removeCss(source[i], css);
      }
    },
    // 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)) {
        DragControl._setDragOffset(e);

        this._add_css(context.source, "webix_invisible");
      }

      return html;
    },
    $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 = box.x + 8 + (this._drag_order_stored_left || 0);
        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)) this._remove_css(context.source, "webix_invisible");
      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([], _power_array, 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.pull[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);
      return boxobj;
    }
  }

  function attach_editend(suggest) {
    if (suggest && suggest.setMasterValue && !suggest._editor_initialized) {
      suggest._editor_initialized = true;
      suggest.attachEvent("onValueSuggest", function () {
        delay(function () {
          callEvent("onEditEnd", []);
        });
      });
    }
  }

  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;
        var suggest = init_suggest(this, input);
        attach_editend(suggest);
      },
      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;
          }

          attach_editend(pobj);
          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) {
      var suggest, id;

      if (this.config.popup) {
        suggest = this.config.popup;
        id = suggest.config.id;
      } else if (config) {
        id = create_suggest(config);
        suggest = $$(id);
      } else id = this._shared_suggest(config);

      attach_editend(suggest);
      return id;
    },
    _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);
      attach_editend(popup);
    },
    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
      var e1 = attachEvent("onEditEnd", bind(function () {
        if (this._in_edit_mode) this.editStop();
      }, this));
      var e2 = 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)) this.editStop();
        }
      }, this));
      this.attachEvent("onDestruct", function () {
        detachEvent(e1);
        detachEvent(e2);
      }); //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), false, "auto");
      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, null, true);

      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;
        var input = editor.getInputNode();

        if ((rule || all) && !input._viewobj) {
          //only for html inputs
          var obj = this.data.getItem(editor.row || editor.id);
          var value = editor.getValue();
          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.$view.style.height = "";
      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 () {
      var _this = this;

      this.attachEvent("onClearAll", function () {
        return _this._not_grouped_order = null;
      });
      this.attachEvent("onSyncApply", function () {
        return _this._not_grouped_order = null;
      });
    },
    ungroup: function (target) {
      if (this.getBranchIndex) {
        if (!this._ungroupLevel(target)) return;
      } else {
        if (!this._not_grouped_order) return;
        this.order = this._not_grouped_order;
        this.pull = this._not_grouped_pull;
      }

      this.callEvent("onStoreUpdated", []);
    },
    _ungroupLevel: function (target) {
      var _this2 = this;

      var parent = target || 0;
      var level = parent == "0" ? 1 : this.getItem(parent).$level + 1;
      var changed = false;
      var top = this.branch[parent];
      var order = [];

      for (var i = 0; i < top.length; i++) {
        var id = top[i];

        if (this.pull[id].$group) {
          changed = true;
          var group = this.branch[id];
          if (group) this.branch[id] = group.filter(function (a) {
            if (!_this2.pull[a].$footer) return a;

            _this2._unregisterItem(a);
          });
          order = order.concat(this.branch[id] || []);

          this._unregisterItem(id);
        } else order.push(id);
      }

      if (!changed) return false;
      this.branch[parent] = order;

      this._fix_group_levels(this.branch[parent], parent, level);

      if (typeof target === "undefined") this._ungroupLevel();
      return true;
    },
    _unregisterItem: function (id) {
      delete this.pull[id];
      delete this.branch[id];
    },
    _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);

      if (!this._not_grouped_order) {
        this._not_grouped_order = this.order;
        this._not_grouped_pull = this.pull;
      }

      var groups = {};
      var labels = [];
      var missed = [];
      var misGroup = config.missing;
      this.each(function (data) {
        var current = config.by(data);

        if (!current && current !== 0) {
          if (misGroup === false) return;

          if (misGroup === true) {
            missed.push(data);
            return;
          }

          current = misGroup;
        }

        if (!groups[current]) {
          labels.push({
            id: current,
            value: current,
            $group: true,
            $row: config.row
          });
          groups[current] = _to_array();
        }

        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]]);
      }

      this.order = _to_array();
      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; //run

      var topbranch = [];
      var labels = [];
      var missed = [];
      var order = this.branch[parent];
      var groups = {};

      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 ancestor = groups[current];

        if (!ancestor) {
          var id = uid();
          var newitem = this.pull[id] = {
            id: id,
            value: current,
            $group: true,
            $row: config.row
          };
          if (this._scheme_init) this._scheme_init(newitem);
          labels.push(newitem);
          ancestor = groups[current] = this.branch[id] = [];
          ancestor._formath = [];
          topbranch.push(id);
        }

        ancestor.push(data.id);

        ancestor._formath.push(data);
      }

      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", []);
    },
    _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.$blockRender = true;
        this.data.ungroup();
        this.$blockRender = false;
      }

      this.data.group(config, target);
    },
    ungroup: function (target) {
      this.data.ungroup(target);
    }
  };

  /*aria-style handling for options of multiple-value controls (radio, segmented, tabbar)*/

  var HTMLOptions = {
    $init: function () {
      var _this = this;

      this.$ready.push(function () {
        if (!_this.customRadio_setter || _this.config.customRadio) _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") return 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") return input[i].blur();
      }
    },
    _moveSelection: function (e) {
      var code = e.which || e.keyCode;

      if (code > 34 && code < 41) {
        var inp = this._getInputNode();

        var index$$1 = false;
        if (!inp.length) return;
        preventEvent(e);
        var dir = code === 37 || code === 38 || code === 35 ? -1 : 1;
        if (code === 35) index$$1 = inp.length - 1;else if (code === 36) index$$1 = 0;else {
          for (var i = 0; i < inp.length; i++) {
            if (inp[i].getAttribute("tabindex") == "0") {
              index$$1 = i + dir;
              break;
            }
          }
        }

        if (index$$1 !== false) {
          var _i = index$$1;

          do {
            if (_i >= inp.length) _i = 0;
            if (_i < 0) _i = inp.length - 1;

            if (!inp[_i].getAttribute("webix_disabled")) {
              var id = inp[_i].getAttribute(
              /*@attr*/
              "button_id");

              this.setValue(id, "user");

              inp[_i].focus();

              _i = "success";
            } else _i += dir;
          } while (_i !== "success" && _i !== index$$1);
        }
      }
    },
    _get_tooltip_data: function (t, e) {
      var id,
          node = e.target;

      while (node && !node.webix_tooltip) {
        id = node.getAttribute("webix_t_id");
        if (id) return this.getOption(id);
        node = node.parentNode;
      }

      return null;
    },
    optionIndex: function (id) {
      var options = this._settings.options;

      for (var i = 0; i < options.length; i++) {
        if (options[i].id == id) return i;
      }

      return -1;
    },
    getOption: function (id) {
      var index$$1 = this.optionIndex(id);
      if (index$$1 !== -1) return this._settings.options[index$$1];
      return null;
    },
    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) === -1) {
        _power_array.insertAt.call(this._settings.options, obj, index$$1);

        this.refresh();
        this.callEvent("onOptionAdd", [id, obj]);
      }

      if (show) {
        if (this._settings.options.length === 1) this._settings.value = "";
        this.setValue(id, "auto");
      }
    },
    removeOption: function (id) {
      var index$$1 = this.optionIndex(id);

      if (index$$1 !== -1) {
        var options = this._settings.options;

        _power_array.removeAt.call(options, index$$1);

        this.refresh(); // if we remove a selected option

        if (this._settings.value == id) this._setNextVisible(options, index$$1);
        this.callEvent("onOptionRemove", [id, this._settings.value]);
      }
    },
    _setNextVisible: function (options, index$$1) {
      var size = options.length;

      if (size && !this.customRadio_setter) {
        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, "auto");
        } //backward search


        for (var _i2 = index$$1; _i2 >= 0; _i2--) {
          if (!options[_i2].hidden) return this.setValue(options[_i2].id, "auto");
        }
      } //nothing found		


      this.setValue("", "auto");
    },
    _getFirstActive: function (first) {
      var options = this._settings.options;

      if (options.length) {
        for (var i = 0; i < options.length; i++) {
          if (!options[i].hidden && !options[i].disabled) return options[i].id;
        }

        if (first) return options[0].id;
      }

      return "";
    },
    _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;
    },
    _setOptionState: function (id, field, state) {
      var options = this._settings.options;
      var index$$1 = this.optionIndex(id);

      if (options[index$$1] && state != !!options[index$$1][field]) {
        //new state differs from previous one
        options[index$$1][field] = state;
        if (state && field === "hidden" && this._settings.value == id) //switch to next visible one
          this._setNextVisible(options, index$$1);
        this.refresh();
      }
    },
    hideOption: function (id) {
      this._setOptionState(id, "hidden", true);
    },
    showOption: function (id) {
      this._setOptionState(id, "hidden", false);
    },
    disableOption: function (id) {
      this._setOptionState(id, "disabled", true);
    },
    enableOption: function (id) {
      this._setOptionState(id, "disabled", false);
    }
  };

  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.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,
            e: e
          });
        }
      };
    },
    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;
        }
        var dir = mode == "up" || mode == "left" || mode == "pgdown" || mode == "bottom" ? -1 : 1;
        if (this._skip_item) selected = this._skip_item(selected, prev, dir);
        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 col = columns[i];

        if (!col.id) {
          col.id = "i" + uid();
          if (!col.header) col.header = "";
        }

        if (col.map) this._scheme_init_order.push(this._process_single_map(col.id, col.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);
      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 = delay(function (e) {
        //called only when we have at least 100ms after previous event
        if (e.type == "mousemove") this._onMouseMove(e);else this._onMouseOut(e);
      }, this, [e], this._settings.mouseEventDelay || 500);
    },
    //inner mousemove object handler
    _onMouseMove: function (e) {
      if (!this._mouseEvent(e, this.on_mouse_move, "MouseMove")) this._onMouseOut(e);
    },
    //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; //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) {
            // prevent clicking on disabled items
            if (trg.getAttribute("webix_disabled")) {
              this._item_clicked = null;
              return;
            }

            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.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) {
        // reset topSplit - since now the pager is responsible for rendering
        if (this.config.topSplit) this.config.topSplit = 0;

        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 || this._touch_scroll == "native") {
          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 (this.callEvent && (!env.touch || this._touch_scroll == "native")) _event(this._viewobj, "scroll", function () {
        delay(function () {
          this.callEvent("onAfterScroll", []);
        }, this);
      }, {
        bind: this
      });
    },
    _touch_scroll: "native",
    scroll_setter: function (value) {
      if (!value) return false;
      var auto = value === "auto";
      var marker = value == "x" ? "x" : value == "xy" ? "xy" : auto ? "xy" : "y";

      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 () {
      return {
        x: this._dataobj.parentNode.scrollLeft,
        y: this._dataobj.parentNode.scrollTop
      };
    },
    scrollTo: function (x, y) {
      this._dataobj.parentNode.scrollLeft = x;
      this._dataobj.parentNode.scrollTop = y;
    }
  };

  /*
  	Behavior:SelectionModel - manage selection states
  	@export
  		select
  		unselect
  		selectAll
  		unselectAll
  		isSelected
  		getSelectedId
  */

  var SelectionModel = {
    $init: function () {
      //collection of selected IDs
      this._selected = _to_array();
      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 || window.event).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 && !this.data._filter_branch) {
        //remove selection for clearAll
        this._selected = _to_array();
      }
    },
    _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 = _to_array();

      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 TablePaste = {
    clipboard_setter: function (value) {
      if (env.touch) return value;
      if (value === true || value === 1) value = "block";
      clipbuffer.init();
      this.attachEvent("onSelectChange", this._sel_to_clip);
      this.attachEvent("onAfterEditStop", function (v, ed) {
        var sel = this.getSelectedId(true);
        if (sel.length == 1 && ed.row == sel[0].row) 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 _this = this;

      delay(function () {
        //wait until editor is closed
        if (!_this.getEditor || !_this.getEditor()) {
          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 : 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 (from, count, callback, url, details, defer) {
      // branch loading
      details = count === 0 ? {
        parent: encodeURIComponent(from)
      } : null;
      return DataLoader.prototype._feed_common.call(this, from, count, callback, url, details, defer);
    },
    //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); // check parent only when moving locally (source == this)

        return new_index + (source == this && this.getParentId(nid) == this.getParentId(next) && 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 (_power_array.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 = _power_array.find.call(sbranch, sid);

        if (tindex < 0) tindex = tbranch.length; //in the same branch and same position

        if (sbranch === tbranch && tindex === sindex) return new_id; //return ID

        _power_array.removeAt.call(sbranch, sindex);

        _power_array.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 = _to_array(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 = _power_array.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 = _power_array.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 = _power_array.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 _power_array.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 = _to_array(); // send current order to prevent simultaneous use in synс mode

      this._sync_each_child(this.order, 0, master);
    },
    _sync_each_child: function (order, start, master) {
      var branch = this.branch[start];

      for (var i = 0; i < branch.length; i++) {
        var id = branch[i];
        order.push(id);
        var item = this.pull[id];

        if (item && item.open) {
          if (item.$count == -1) master.loadBranch(id);else if (item.$count) this._sync_each_child(order, 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 _to_array([].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 (sorter, order) {
      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 = _to_array(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 = _to_array(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 _to_array(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 = {
    $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; // non-file drop

      if (!files.length) return;

      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" || mode == "save")) 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, config) {
      var _this = this;

      if (this._settings.complexData) data = CodeParser.collapseNames(data, "", {}, function (v) {
        return !_this.elements[v];
      });

      this._inner_setValues(data, update, config);
    },
    _inner_setValues: function (data, update, config) {
      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], config);else if (!update && input.$allowsClear) input.setValue("", config);
          this._values[_name] = input.getValue();
        }
      }

      this.unblockEvent();
      this.callEvent("onValues", []);
    },
    isDirty: function () {
      return !!this._is_form_dirty || this.getDirtyValues(true) === true;
    },
    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 view = this.elements[name];
          var value = view.getValue();
          var defaultValue = this._values[name];
          var isDirty = view.$compareValue ? !view.$compareValue(defaultValue, value) : defaultValue != value;

          if (isDirty) {
            result[name] = value;
            if (arguments[0]) return true;
          }
        }
      }

      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 (config) {
      this._is_form_dirty = false;
      var data = {};

      for (var name in this.elements) {
        if (this.elements[name].$allowsClear) data[name] = "";
      }

      this._inner_setValues(data, false, config);
    },
    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)); //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 tpadding = this._tilesPadding || 0;
      var width = this._content_width - tpadding;
      var height = this._content_height - tpadding / 2; //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 = {};

      _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$1.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 = "8.4.0";
  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 getHeaderText(view, header) {
    var text = header.text;

    if (header.contentId) {
      var content = view.getHeaderContent(header.contentId);
      if (content && !content.type.$icon) text = content.getValue(true);
    }

    return (text || "").toString().replace(/<[^>]*>/gi, "");
  }

  function getStyles(r, c, styles) {
    //row index, column index, styles array
    if (styles[r] && styles[r][c]) return styles[r][c];
    return {};
  }
  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 (options.hidden || options.hide) {
      scheme.hiddenCols = {};
      scheme.hiddenRows = {};
    }

    if (!columns) {
      columns = [];

      if (isTable) {
        var order = view._hidden_column_order;

        if (options.hidden && order.length) {
          for (var i = 0; i < order.length; i++) {
            var col = view.getColumnConfig(order[i]);
            if (!view.isColumnVisible(col.id)) scheme.hiddenCols[col.id] = 1;
            columns.push(col);
          }
        } else columns = columns.concat(view._columns);
      } else {
        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) {
      (function () {
        var flatKey = options.flatTree.id;
        var copy$$1 = [].concat(options.flatTree.columns);
        var fill = [];
        var fillMode = !!options.flatTree.fill;

        for (var _i2 = 1; _i2 <= copy$$1.length; _i2++) {
          copy$$1[_i2 - 1].template = function (i) {
            return function (obj) {
              return obj.$level == i ? fill[i] = obj[flatKey] : fillMode && i < obj.$level ? fill[i] : "";
            };
          }(_i2);
        }

        var index = 0;

        for (var _i3 = columns.length - 1; _i3 >= 0; _i3--) {
          if (columns[_i3].id === flatKey) index = _i3;
        }

        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.getColumnConfig(_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 (column.hidden) {
          if (!scheme.hiddenCols) scheme.hiddenCols = {};
          scheme.hiddenCols[column.id] = 1;
        }
      }

      if (typeof record.header === "string") record.header = [{
        text: record.header
      }];else record.header = [].concat(record.header);

      for (var _i4 = 0; _i4 < record.header.length; _i4++) {
        record.header[_i4] = record.header[_i4] ? getHeaderText(view, record.header[_i4]) : "";
      }

      h_count = Math.max(h_count, record.header.length);

      if (view.config.footer) {
        var footer = column.footer || "";
        if (typeof footer == "string") footer = [{
          text: footer
        }];else footer = [].concat(footer);

        for (var _i5 = 0; _i5 < footer.length; _i5++) {
          footer[_i5] = footer[_i5] ? getHeaderText(view, footer[_i5]) : "";
        }

        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 _i6 = 0; _i6 < scheme.length; _i6++) {
      var diff = h_count - scheme[_i6].header.length;

      for (var d = 0; d < diff; d++) {
        scheme[_i6].header.push("");
      }

      if (view.config.footer) {
        diff = f_count - scheme[_i6].footer.length;

        for (var _d = 0; _d < diff; _d++) {
          scheme[_i6].footer.push("");
        }
      }
    }

    return scheme;
  }
  function getFileName(name, extension) {
    if (name) name = name.replace(/[/?\\<>:*|"]/g, "").substring(0, 150);
    return "".concat(name || "Data", ".").concat(extension);
  }
  function getExportData(view, options, scheme) {
    var filterHTML = !!options.filterHTML;
    var htmlFilter = /<[^>]*>/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, index) {
      if (!options.filter || options.filter(item)) {
        var reallyHidden = options.hidden && view.data._filter_order && view.getIndexById(item.id) == -1;

        if (options.hide && options.hide(item) || reallyHidden) {
          var _header = (options.docHeader ? 2 : 0) + (options.header === false ? 0 : scheme[0].header.length);

          scheme.hiddenRows[_header + index] = 1;
        }

        if (this.data._scheme_export) {
          item = view.data._scheme_export(item);
        }

        var line = [];

        for (var _i7 = 0; _i7 < scheme.length; _i7++) {
          var column = scheme[_i7],
              cell = null; //spreadsheet use muon to store data, get value via $getExportValue

          if (view.$getExportValue) cell = view.$getExportValue(item.id, column.id, options); //datatable math
          else if (options.math && item["$" + column.id] && item["$" + column.id].charAt(0) == "=") 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, _i7);
            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, options.hidden);

    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 _i8 = 0; _i8 < scheme.length; _i8++) {
          var footer = scheme[_i8].footer[f];
          if (filterHTML) footer = scheme[_i8].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 || {};
      options.export_mode = "png";
      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 = getFileName(options.filename, "png");
      window.html2canvas(node, {
        background: "#fff",
        logging: false,
        useCORS: true
      }).then(function (canvas) {
        var callback = function (data) {
          if (options.download !== false) download(data, filename);
          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 = getFileName(options.filename, "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) {
    options = options || {};
    options.export_mode = "pdf";
    options.fontName = options.fontName || "pt-sans.regular";
    options.boldFontName = options.boldFontName || "pt-sans.bold";
    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);

      if (view) {
        var viewOptions = exports.extend(id[i].options || {}, options);
        var display = viewOptions.display || "table";
        if (viewOptions.display == "image") delete viewOptions.styles;
        if (view.$exportView) view = view.$exportView(viewOptions); //$exportView returns array

        if (isArray(view)) {
          views = views.concat(view);
          if (options.autowidth) getAutowidth(viewOptions, options);
        } else {
          //display table should be first (in case of styles:true $exportView adds styles to the first view)
          if (display == "table" || display == "all") {
            if (view.data && view.data.pull) {
              var scheme = getExportScheme(view, viewOptions);
              views.push({
                scheme: scheme,
                exportData: getExportData(view, viewOptions, scheme),
                viewOptions: viewOptions
              });
              if (options.autowidth) getAutowidth(view, options, scheme);
            }
          }

          if (display == "image" || display == "all") {
            var node = viewOptions._hidden ? cloneNodeWithStyles(view.$view) : view.$view;
            views.push({
              node: node,
              viewOptions: viewOptions
            });
            if (options.autowidth) getAutowidth(view, options);
          }
        }
      }

      assert(view, errorMessage);
    }

    if (options.dataOnly) return views;
    return require([env.cdn + "/extras/pdfjs.js", env.cdn + "/extras/html2canvas-1.0.min.js"]).then(function () {
      if (views.length == 0) return Deferred.reject(errorMessage);

      if (font[options.fontName] && font[options.boldFontName]) {
        options._export_font = font[options.fontName];
        options._export_font_bold = font[options.boldFontName];
        return getPdfData(views, options).then(function (pdf) {
          return getBlob(pdf, options);
        });
      } else {
        var defer = Deferred.defer();
        /* global pdfjs */

        pdfjs.load(options.fontURL || env.cdn + "/extras/" + options.fontName + ".ttf", function (err, regular) {
          if (err) return defer.reject(err);
          pdfjs.load(options.boldFontURL || env.cdn + "/extras/" + options.boldFontName + ".ttf", function (err, bold) {
            options._export_font = font[options.fontName] = new pdfjs.TTFFont(regular);
            options._export_font_bold = font[options.boldFontName] = err ? null : new pdfjs.TTFFont(bold);
            defer.resolve(getPdfData(views, options).then(function (pdf) {
              return getBlob(pdf, options);
            }));
          });
        });
        return defer;
      }
    });
  };

  function getBlob(pdf, options) {
    var filename = getFileName(options.filename, "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 {
          viewOptions._export_font_bold = options._export_font_bold;
          addPDFTable(views[_i], 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) {
    var hidden = !document.body.contains(node);

    if (hidden) {
      //node is a cloneNode of the real view, so it shouldn't be visible
      document.body.appendChild(node);
      node.style.position = "absolute";
      node.style.left = "-9999px";
    }

    return window.html2canvas(node, {
      background: "#fff",
      logging: false,
      useCORS: true
    }).then(function (canvas) {
      var image = canvas.toDataURL("image/jpeg");
      var binary_string = window.atob(image.split("base64,")[1]);
      var length = binary_string.length;
      var bytes = new Uint8Array(length);

      for (var i = 0; i < length; i++) {
        bytes[i] = binary_string.charCodeAt(i);
      }

      return new pdfjs.Image(bytes.buffer);
    }).finally(function () {
      if (hidden) document.body.removeChild(node);
    });
  }

  function getAutowidth(view, options, scheme) {
    var prop = options.orientation && options.orientation == "landscape" ? "height" : "width";
    var width;

    if (scheme) {
      width = 80; //paddings

      for (var i = 0; i < scheme.length; i++) {
        width += scheme[i].width;
      }
    } else if (view.$width) width = view.$width;else //'view' can be local settings and we need to compare them with common ones
      width = view[prop];

    options[prop] = Math.max(options[prop] || 0, width || 0);
  }

  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(view, doc) {
    var scheme = view.scheme;
    var data = view.exportData;
    var options = view.viewOptions;
    var styles = view.styles;
    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);
    var boldFont = options._export_font_bold; //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++) {
          var cellStyle = styles ? getStyles(_i2, s, styles) : {};
          if (boldFont && cellStyle.bold) cellStyle.font = boldFont;
          header.td(scheme[s].header[_i2].toString(), cellStyle);
        }
      }
    } //render table data


    for (var r = 0; r < data.length; r++) {
      var row = table.tr({});

      for (var c = 0; c < data[r].length; c++) {
        var _cellStyle = styles ? getStyles(r + h_count, c, styles) : {};

        if (boldFont && _cellStyle.bold) _cellStyle.font = boldFont;
        row.td(data[r][c], _cellStyle);
      }
    } //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 beforeCount = h_count + data.length;
        var footer = table.tr(footerOps);

        for (var _s = 0; _s < scheme.length; _s++) {
          var _cellStyle2 = styles ? getStyles(_i3 + beforeCount, _s, styles) : {};

          if (boldFont && _cellStyle2.bold) _cellStyle2.font = boldFont;
          footer.td(scheme[_s].footer[_i3].toString(), _cellStyle2);
        }
      }
    }
  }

  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());
  }

  function cloneNodeWithStyles(node) {
    var clone$$1 = node.cloneNode(false);

    if (node.tagName) {
      var styles = window.getComputedStyle(node);
      clone$$1.style.cssText = styles.cssText;
    }

    for (var i = 0; i < node.childNodes.length; i++) {
      clone$$1.appendChild(cloneNodeWithStyles(node.childNodes[i]));
    }

    return clone$$1;
  }

  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++) {
      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);
      assert(view, errorMessage); //$exportView returns array

      if (isArray(view)) views = views.concat(view);else if (view.data && view.data.pull) {
        //spreadsheet and excelviewer require plain data output first
        var scheme = getExportScheme(view, viewOptions);
        views.push({
          scheme: scheme,
          exportData: getExportData(view, viewOptions, scheme),
          spans: viewOptions.spans ? getSpans(view, viewOptions) : [],
          viewOptions: viewOptions
        });
      }
    }

    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: []
        }
      };

      for (var _i = 0; _i < views.length; _i++) {
        var _viewOptions = views[_i].viewOptions;
        var _scheme = views[_i].scheme;
        var result = views[_i].exportData;
        var spans = views[_i].spans;
        var ranges = views[_i].ranges || [];
        var styles = views[_i].styles || [];
        var data = getExcelData(result, _scheme, spans, styles, _viewOptions);

        var sname = (_viewOptions.name || "Data" + (_i + 1)).replace(/[*?:[\]\\/]/g, "").replace(/&/g, "&amp;").substring(0, 31); //avoid name duplication


        var k = _i;

        while (wb.SheetNames.indexOf(sname) != -1) {
          sname = "Data" + ++k;
        }

        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 = getFileName(options.filename, "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) === "=";

        if (styles) {
          var cellStyle = getStyles(R, C, styles);

          if (cellStyle.format) {
            cell.z = cellStyle.format;
            delete cellStyle.format;
          }

          if (cellStyle.type) {
            cell.t = types[cellStyle.type];
            delete cellStyle.type;
          }

          cell.s = cellStyle;
        } // 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) cell.t = types[column.type] || "";
          if (column.format && !cell.z) 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 (isFormula) {
          cell.t = cell.t || "n";
          cell.f = cell.v.substring(1);
          delete 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;
            cell.t = "s";
          }
        }

        ws[cell_ref] = cell;
      }
    }

    if (range.s.c < 10000000) ws["!ref"] = XLSX.utils.encode_range(range);
    ws["!rows"] = getRowHeights(scheme);
    ws["!cols"] = getColumnsWidths(scheme);
    if (spans.length) ws["!merges"] = spans;
    return ws;
  }

  function getRowHeights(scheme) {
    var heights = scheme.heights;

    for (var i in heights) {
      heights[i] = {
        hpx: heights[i],
        hpt: heights[i] * 0.75
      };
    }

    var hidden = scheme.hiddenRows;
    if (hidden) for (var _i2 in hidden) {
      if (!heights[_i2]) heights[_i2] = {};
      heights[_i2].hidden = 1;
    }
    return heights;
  }

  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 colIndex = view.getColumnIndex(col);
            var rowId = view.getItem(row).id;
            var rowIndex = view.getIndexById(rowId);

            if (options.hidden) {
              var hiddenColsOrder = view._hidden_column_order;

              if (hiddenColsOrder.length) {
                var colId = view.getColumnConfig(col).id;
                colIndex = hiddenColsOrder.indexOf(colId);
              }

              var hiddenRowsOrder = view.data._filter_order;
              if (hiddenRowsOrder && hiddenRowsOrder.length) rowIndex = hiddenRowsOrder.indexOf(rowId);
            }

            var sc = colIndex - xc;
            var sr = rowIndex - 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) {
    var returnDateTime = 25569 + (date.getTime() - date.getTimezoneOffset() * 60 * 1000) / (1000 * 60 * 60 * 24);
    return returnDateTime.toString().substr(0, 20);
  }

  function getColumnsWidths(scheme) {
    var wscols = [];

    for (var i = 0; i < scheme.length; i++) {
      var col = scheme[i];
      wscols.push({
        wch: col.width,
        hidden: scheme.hiddenCols ? scheme.hiddenCols[col.id] : 0
      });
    }

    return wscols;
  }

  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"
    },
    filter: {
      less: "less",
      lessOrEqual: "less or equal",
      greater: "greater",
      greaterOrEqual: "greater or equal",
      contains: "contains",
      notContains: "not contains",
      equal: "equal",
      notEqual: "not equal",
      beginsWith: "begins with",
      notBeginsWith: "not begins with",
      endsWith: "ends with",
      notEndsWith: "not ends with",
      between: "between",
      notBetween: "not between"
    },
    timeboard: {
      seconds: "Seconds"
    }
  };

  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) {
          if (!date) return "";
          if (!date.getMonth) date = i18n.parseFormatDate(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(/[\\s\\./\\-\\:\\,]+/g); if(!temp.join('')){return ''}";
      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(/[\s./\-:,]+/g);
          if (!temp.join("")) return "";
          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 "quarter":
          date.setMonth(date.getMonth() + inc * 3);

          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);
      delete i18n.calendar.monthShort_hash;
      delete i18n.calendar.monthFull_hash;
    }

    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");

  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";
      }
    }
  };

  env.printPPI = 96;
  env.printMargin = 0.75 * env.printPPI;
  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 : {};
    var margin = isNaN(options.margin * 1) ? env.printMargin : 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 filters = {
    number: {
      greater: function (a, b) {
        return a > b;
      },
      less: function (a, b) {
        return a < b;
      },
      greaterOrEqual: function (a, b) {
        return a >= b;
      },
      lessOrEqual: function (a, b) {
        return a <= b;
      },
      equal: function (a, b) {
        return a == b;
      },
      notEqual: function (a, b) {
        return a != b;
      },
      contains: function (a, b) {
        return a.toString().toLowerCase().indexOf(b.toString().toLowerCase()) !== -1;
      },
      notContains: function (a, b) {
        return a.toString().toLowerCase().indexOf(b.toString().toLowerCase()) === -1;
      }
    },
    text: {
      equal: function (a, b) {
        return a.toLowerCase() === b.toLowerCase();
      },
      notEqual: function (a, b) {
        return a.toLowerCase() !== b.toLowerCase();
      },
      contains: function (a, b) {
        return a.toLowerCase().indexOf(b.toLowerCase()) !== -1;
      },
      notContains: function (a, b) {
        return a.toLowerCase().indexOf(b.toLowerCase()) === -1;
      },
      beginsWith: function (a, b) {
        return a.toLowerCase().lastIndexOf(b.toLowerCase(), 0) === 0;
      },
      notBeginsWith: function (a, b) {
        return a.toLowerCase().lastIndexOf(b.toLowerCase(), 0) !== 0;
      },
      endsWith: function (a, b) {
        return a.toLowerCase().indexOf(b.toLowerCase(), a.length - b.length) !== -1;
      },
      notEndsWith: function (a, b) {
        return a.toLowerCase().indexOf(b.toLowerCase(), a.length - b.length) === -1;
      }
    },
    date: {
      greater: function (a, b) {
        return a > b;
      },
      less: function (a, b) {
        return a < b;
      },
      greaterOrEqual: function (a, b) {
        return a >= b;
      },
      lessOrEqual: function (a, b) {
        return a <= b;
      },
      equal: function (a, b) {
        if (!a || !b) return false;
        return a.valueOf() === b.valueOf();
      },
      notEqual: function (a, b) {
        if (!a || !b) return true;
        return a.valueOf() !== b.valueOf();
      },
      between: function (a, b) {
        return (!b.start || a > b.start) && (!b.end || a < b.end);
      },
      notBetween: function (a, b) {
        return !b.start || a <= b.start || !b.end || a >= b.end;
      }
    }
  };

  var patterns = {
    phone: {
      mask: "+# (###) ###-####",
      allow: /[0-9]/g
    },
    card: {
      mask: "#### #### #### ####",
      allow: /[0-9]/g
    },
    date: {
      mask: "####-##-## ##:##",
      allow: /[0-9]/g
    }
  };

  /*
  	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 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");
    if (!env.isMac) addStyle("body.webix_full_screen{ overflow-y: auto; }");

    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.innerText;
      },
      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.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.querySelector("input") || {
          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 = (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.querySelector("select") || {
          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.innerHTML = "";
        node.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 = (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.zIndex = zIndex;
  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: "von"
    },
    PDFviewer: {
      of: "von",
      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"
    },
    filter: {
      less: "weniger",
      lessOrEqual: "weniger oder gleich",
      greater: "mehr",
      greaterOrEqual: "größer oder gleich",
      contains: "enthält",
      notContains: "nicht enthält",
      equal: "gleich",
      notEqual: "ungleich",
      beginsWith: "beginnt mit",
      notBeginsWith: "nicht beginnt mit",
      endsWith: "endet mit",
      notEndsWith: "nicht endet mit",
      between: "zwischen",
      notBetween: "nicht zwischen"
    },
    timeboard: {
      seconds: "Sekunden"
    }
  };

  /*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"
    },
    filter: {
      less: "menos",
      lessOrEqual: "menor o igual",
      greater: "mayor",
      greaterOrEqual: "mayor o igual",
      contains: "contiene",
      notContains: "not contiene",
      equal: "igual",
      notEqual: "no es igual",
      beginsWith: "comienza con",
      notBeginsWith: "no comienza con",
      endsWith: "termina con",
      notEndsWith: "no termina con",
      between: "entre",
      notBetween: "no entre"
    },
    timeboard: {
      seconds: "segundos"
    }
  };

  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"
    },
    filter: {
      less: "moins",
      lessOrEqual: "inférieur ou égal",
      greater: "plus grand",
      greaterOrEqual: "supérieur ou égal",
      contains: "contient",
      notContains: "ne contient",
      equal: "égal",
      notEqual: "pas égal",
      beginsWith: "commence par",
      notBeginsWith: "ne commence par",
      endsWith: "se termine par",
      notEndsWith: "pas se termine par",
      between: "entre",
      notBetween: "pas entre"
    },
    timeboard: {
      seconds: "secondes"
    }
  };

  /*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"
    },
    filter: {
      less: "meno",
      lessOrEqual: "minore o uguale",
      greater: "maggiore",
      greaterOrEqual: "maggiore o uguale",
      contains: "contiene",
      notContains: "non contiene",
      equal: "uguale",
      notEqual: "non uguale",
      beginsWith: "inizia con",
      notBeginsWith: "non inizia con",
      endsWith: "finisce con",
      notEndsWith: "non termina con",
      between: "tra",
      notBetween: "non tra"
    },
    timeboard: {
      seconds: "secondi"
    }
  };

  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: "その他のコメント"
    },
    filter: {
      less: "レス",
      lessOrEqual: "以下",
      greater: "大きいです",
      greaterOrEqual: "以上",
      contains: "含まれています",
      notContains: "含まれていません",
      equal: "等しいです",
      notEqual: "等しくありません",
      beginsWith: "で始まります",
      notBeginsWith: "ないで始まります",
      endsWith: "で終わります",
      notEndsWith: "で終わりではありません",
      between: "間に",
      notBetween: "いない間"
    },
    timeboard: {
      seconds: "秒"
    }
  };

  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"
    },
    filter: {
      less: "menos",
      lessOrEqual: "menor ou igual",
      greater: "maior",
      greaterOrEqual: "maior ou igual",
      contains: "contém",
      notContains: "não contém",
      equal: "igual",
      notEqual: "não é igual",
      beginsWith: "começa com",
      notBeginsWith: "não começa com",
      endsWith: "termina com",
      notEndsWith: "não termina com",
      between: "entre",
      notBetween: "não entre"
    },
    timeboard: {
      seconds: "segundos"
    }
  };

  /*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: "更多评论"
    },
    filter: {
      less: "小于",
      lessOrEqual: "少于等于",
      greater: "大于",
      greaterOrEqual: "大于等于",
      contains: "包含",
      notContains: "不包含",
      equal: "等于",
      notEqual: "不等于",
      beginsWith: "开始于",
      notBeginsWith: "不以开始",
      endsWith: "结束",
      notEndsWith: "不以结束",
      between: "之间",
      notBetween: "不介于"
    },
    timeboard: {
      seconds: "秒"
    }
  };

  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: "Больше комментариев"
    },
    filter: {
      less: "меньше",
      lessOrEqual: "меньше или равно",
      greater: "больше",
      greaterOrEqual: "больше или равно",
      contains: "содержит",
      notContains: "не содержит",
      equal: "равно",
      notEqual: "не равно",
      beginsWith: "начинается с",
      notBeginsWith: "не начинается с",
      endsWith: "заканчиватся",
      notEndsWith: "не заканчиватся",
      between: "между",
      notBetween: "не между"
    },
    timeboard: {
      seconds: "Секунды"
    }
  };

  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: "Больш каментарыяў"
    },
    filter: {
      less: "менш",
      lessOrEqual: "менш або роўна",
      greater: "больш",
      greaterOrEqual: "больш або роўна",
      contains: "змяшчае",
      notContains: "не змяшчае",
      equal: "роўныя",
      notEqual: "не роўныя",
      beginsWith: "пачынаецца з",
      notBeginsWith: "не пачынаецца з",
      endsWith: "заканчваецца",
      notEndsWith: "не сканчаецца",
      between: "паміж",
      notBetween: "не паміж"
    },
    timeboard: {
      seconds: "Секунды"
    }
  };

  // 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 = _power_array.find.call(this._cells, view);

      if (this._beforeRemoveView) this._beforeRemoveView(index$$1);

      _power_array.removeAt.call(this._cells, index$$1);

      this._fix_hidden_cells(true);
    },
    _fix_hidden_cells: function (resize$$1) {
      this._hiddencells = 0;

      for (var i = 0; i < this._cells.length; i++) {
        var cell = this._cells[i];
        if (cell._settings.hidden || cell.$nospace) this._hiddencells++;
      }

      if (resize$$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;

          _power_array.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 = _power_array.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._fix_hidden_cells(true);

      var form = this.elements ? this : this.getFormView();
      if (form && !this._fill_data) 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 = _power_array.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) _each(view, function (sub) {
          if (sub.name) delete form.getCleanValues()[sub.config.name];
        }, form, true);
        view.destructor();

        this._fix_hidden_cells(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", []);

        _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;
        }

        if (!this._cells[i]._settings.hidden) {
          (this._dataobj || this._contentobj).appendChild(this._cells[i]._viewobj);
        }
      }

      this._fix_hidden_cells();

      if (this._parse_cells_ext_end) this._parse_cells_ext_end(collection);
    },
    _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";
    },
    _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 = base.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);
      base.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({
              oldIndex: i,
              view: this._cells[i]
            });
            continue;
          }
        } else {
          width = this._set_child_size_a(sizes, 0, 1);

          if (width < 0) {
            auto.push({
              oldIndex: i,
              view: this._cells[i]
            });
            continue;
          }
        }

        this._cells[i].$setSize(width, height);
      }

      for (var _i2 = 0; _i2 < auto.length; _i2++) {
        var index$$1 = auto[_i2].oldIndex;
        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;
        }

        auto[_i2].view.$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, base.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$1.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_height);
    },
    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;
    },
    content_setter: function (config) {
      if (config) {
        this._not_render_me = true;

        this.render = function () {};

        this._dataobj.appendChild(toNode(config));

        this._correct_height();
      }
    },
    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$1.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$1.api.$getSize.call(this, x, y);
    },
    _correct_height: 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();
      }
    },
    _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$1.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: "300ms"
    },
    $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);

      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$1.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$1.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._dataobj.appendChild(this._body_cell._viewobj);

      this.resizeChildren();
    },
    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$1.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$1.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$1.api.$setSize.call(this, x, y);else base.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;
          }
        }
      }
    },
    _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 () {
      layout.api.$skin.call(this);
      if ($active.accordionType) this.defaults.type = $active.accordionType;
    }
  };
  var view$a = exports.protoUI(api$a, layout.view);
  var base$2 = {
    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$2.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 = base.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$1.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 () {
      this.defaults.headerAltHeight = this.defaults.headerHeight = $active.barHeight - $active.borderWidth * 2;
    },
    defaults: {
      header: false,
      headerAlt: false,
      body: ""
    }
  };
  var view$c = exports.protoUI(api$c, MouseEvents, EventSystem, base$1.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
      });

      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 _this = this;

      var cells = this._getResizerCells(); //some sibling can block resize


      if (cells && !this._settings.disabled) {
        e = e || window.event;
        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]);

        var handler = event$1(document.body, env.mouse.up, function (e) {
          eventRemove(handler);
          return _this._rsUp(e);
        });
      }
    },
    _rsUp: function () {
      this._rs_started = false;
      this._rs_process = false;
    },
    _rsStart: function (e, cell) {
      var dir, cellOffset, pos$$1, posParent, start;
      e = e || window.event;
      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, 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];

        for (i = 0; i < 2; i++) {
          cDiff = i ? -diff : diff;
          /*if cDiff is positive, the size of i cell is increased*/

          /*max and min limits*/

          limits = cells[i].$getSize(0, 0);
          /*if size is bigger than max limit or size is smaller than min limit*/

          var min = dir == "y" ? limits[2] : limits[0];
          var max = dir == "y" ? limits[3] : limits[1]; //if size is fixed, treat it as responsive (default behavior)

          if (min === max) {
            min = cells[i]._settings[dir == "y" ? "minHeight" : "minWidth"] || 3;
            max = cells[i]._settings[dir == "y" ? "maxHeight" : "maxWidth"] || 100000;
          }

          if (cDiff > 0 && max && max <= sizes[i] || cDiff < 0 && min && min >= sizes[i]) {
            this._rsLimit[i] = cDiff > 0 ? max : min;
            /*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$1.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";
    },
    _remove: function () {
      this.body_setter({});
    },
    _replace: function (new_view) {
      this._body_cell.destructor();

      this._body_cell = new_view;

      this._viewobj.appendChild(this._body_cell._viewobj);

      this.resize();
    },
    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$1.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 = base.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$1.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;
    },
    _replace: function (view) {
      if (!view._settings.borderless) {
        var settings = clone(this._settings._inner);
        view._settings._inner = settings;
        var style = view._viewobj.style;
        style.borderTopWidth = style.borderBottomWidth = style.borderLeftWidth = style.borderRightWidth = "1px";

        this._fix_container_borders(style, settings);
      }

      baselayout.api._replace.apply(this, arguments);
    },
    _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._animation_promise = null;

          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;

        this._fix_container_borders(cell._viewobj.style, cell._settings._inner);

        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 _this = this,
          _arguments = arguments;

      var parent = this.getParentView();

      if (parent && parent.getTabbar) {
        var tabBar = parent.getTabbar();
        tabBar.blockEvent();
        tabBar.setValue(obj._settings.$id || obj._settings.id);
        tabBar.unblockEvent();
      }

      if (this._animation_promise) return this._animation_promise.then(function () {
        return _this._show.apply(_this, _arguments);
      });

      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._animation_promise = null;
          aniset.wait_animation.resolve();
          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._animation_promise = aniset.wait_animation = Deferred.defer();
      } 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 base.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 = base.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];
      base.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$1.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;
    },
    _insertBeforeView: function (view, before) {
      if (this._settings.keepViews || !before || before == this._cells[this._active_cell]) baselayout.api._insertBeforeView.call(this, view, before);
    }
  };
  var view$g = exports.protoUI(api$g, baselayout.view);

  var api$h = {
    name: "tabview",
    setValue: function (val, config) {
      this._cells[0].setValue(val, config);
    },
    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 () {
      var _this = this;

      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);
      var e1 = attachEvent("onReconstruct", function (view) {
        if (view == _this._layout) _this._setScroll();
      });
      this.attachEvent("onDestruct", function () {
        detachEvent(e1);
      });
      this._contentobj = this._viewobj.firstChild;
    },
    _onKeyPress: function (code, e) {
      if (this._settings.navigation.items && e.target.getAttribute("role") === "tab") this._moveActive(code, e);

      base.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$1.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$1.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$1.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$1.api.$setSize.call(this, x, y);

      this._body_cell.$setSize(this.$width, this.$height);
    },
    $getSize: function (dx, dy) {
      var selfSize = base$1.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$1.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";
      var clickHandler = attachEvent("onClick", function (e) {
        return _this._hide(e);
      });
      this.attachEvent("onDestruct", function () {
        detachEvent(clickHandler);
      });
      this.attachEvent("onHide", this._hide_point);
    },
    $skin: function () {
      window$1.api.$skin.call(this);
      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$1.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 = this._viewobj.style.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 || view._fill_data) 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$1.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$1.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$1.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, config) {
      if (this._body_view.setValue) this._body_view.setValue(value, config);else if (this._body_view.setValues) this._body_view.setValues(value, false, config);
    },
    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;
    },
    $init: function () {
      this.$ready.push(function () {
        var label = this._viewobj.firstChild.childNodes[0];
        var body = this._viewobj.firstChild.childNodes[1];
        var config = this._settings;

        if (config.labelPosition != "top") {
          config.labelWidth = config.label ? this._getLabelWidth(config.labelWidth, config.label) : 0;
          config.paddingX = config.labelWidth + this._inputSpacing;
        } else {
          config.paddingY = this._labelTopHeight;
          config.paddingX = this._inputSpacing;
        }

        if (!config.label || !config.labelWidth && config.labelPosition != "top") {
          label.style.display = "none";
          body.style.padding = "0 " + this._inputSpacing / 2 + "px";
          config.paddingX = this._inputSpacing;
          config.paddingY = 0;
          return;
        }

        if (config.labelPosition == "top") {
          label.style.lineHeight = this._labelTopHeight - this._inputPadding + "px";
          label.className += " " + this.defaults.$cssName + "_label_top";
          body.style.padding = "0 " + this._inputSpacing / 2 + "px";
        } else label.style.width = config.paddingX - this._inputSpacing / 2 + "px";

        label.style.textAlign = config.labelAlign;
        if (config.value) this.setValue(config.value, "auto");
      });
    },
    _getLabelWidth: function (width, label) {
      if (width == "auto") width = getTextSize(label, "webix_inp_label").width;
      return width ? Math.max(width, $active.dataPadding) : 0;
    },
    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 0; background: white; left:" + (this._inputSpacing / 2 + (config.label ? config.labelWidth : 0)) + "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$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 (!isUndefined(data.template)) this.on_render[name] = data.template;
      if (!isUndefined(data.editor)) this.on_edit[name] = data.editor;
      if (!isUndefined(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$1.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$1.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$1.view);

  var api$s = {
    name: "calendar",
    defaults: {
      date: new Date(),
      //selected date, not selected by default
      navigation: true,
      monthSelect: true,
      weekHeader: true,
      monthHeader: true,
      weekNumber: false,
      skipEmptyWeeks: false,
      calendarHeader: "%F %Y",
      //calendarTime: "%H:%i",
      events: wDate.isHoliday,
      minuteStep: 5,
      timeIcon: "wxi-clock",
      icons: false,
      timepickerHeight: 30,
      headerHeight: 30,
      dayTemplate: function (d) {
        return d.getDate();
      },
      width: 260,
      height: 250
    },
    dayTemplate_setter: template,
    calendarHeader_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);
    },
    minuteStep_setter: function (value) {
      return Math.max(Math.min(value, 60), this.defaults.minuteStep);
    },
    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$1.api.$setSize.call(this, x, y)) {
        //repaint calendar when size changed
        this.render();
      }
    },
    $getSize: function (dx, dy) {
      var s = this._settings;

      if (s.cellHeight && !s.type) {
        var state = this._getDateBoundaries(s.date);

        s.height = s.cellHeight * state._rows + s.headerHeight + (s.weekHeader ? $active.calendarWeekHeaderHeight : 0) + (s.timepicker || this._icons ? s.timepickerHeight : 0) + (this._content_padding + $active.borderWidth) * 2;
      }

      return base$1.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;
      }

      this._content_padding = $active.layoutPadding.form;
    },
    _getColumnConfigSizes: function (date) {
      var bounds = this._getDateBoundaries(date);

      var s = this._settings;
      var _columnsHeight = [];
      var _columnsWidth = [];
      var min = Infinity;
      var containerWidth = this._content_width - (this._content_padding + $active.borderWidth) * 2;
      var containerHeight = this._content_height - (s.monthHeader ? s.headerHeight : 0) - (s.weekHeader ? $active.calendarWeekHeaderHeight : 0) - (s.timepicker || this._icons ? s.timepickerHeight : 0) - (this._content_padding + $active.borderWidth) * 2;
      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, "user");
          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("", "user");
          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 cpad = this._content_padding + "px";
      var width = sizes[0];
      var height = sizes[1];
      var html = "";

      if (s.monthHeader) {
        html += "<div class='webix_cal_month' style='margin:0 " + cpad + "'><span aria-live='assertive' aria-atomic='true' class='webix_cal_month_name" + (!s.monthSelect || !s.navigation ? " webix_readonly'" : "' role='button' tabindex='0'") + ">" + 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' style='margin:0 " + cpad + "' aria-hidden='true'>" + this._week_template(width) + "</div>";
      html += "<div class='webix_cal_body' style='margin:0 " + cpad + "'>" + this._body_template(width, height, bounds, sizes[2]) + "</div>";

      if (this._settings.timepicker || this._icons) {
        html += "<div class='webix_cal_footer' style='margin:0 " + cpad + "'>";
        if (this._settings.timepicker) html += this._timepicker_template(date);
        if (this._icons) html += this._icons_template();
        html += "</div>";
      }

      this._contentobj.innerHTML = html;
      this._contentobj.firstChild.style.marginTop = cpad;

      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._fix_cover();

      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, false, "user");

          if (newdate) {
            calendar._update_zoom_level(newdate);

            calendar.selectDate(newdate, false, false, "user");
          }

          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, false, "user");
          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) {
          date = wDate.monthStart(date);

          if (date < calendar._settings.minDate) {
            date = wDate.copy(calendar._settings.minDate);
          } else if (date > calendar._settings.maxDate) {
            date = wDate.copy(calendar._settings.maxDate);
          }

          return 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");
          newdate = calendar._correctDate(newdate);

          if (!calendar._checkDate(newdate)) {
            newdate = calendar._findActive(date, mode);
          }

          if (newdate) {
            calendar._update_zoom_level(newdate);

            calendar.selectDate(newdate, false, false, "user");
          }

          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) {
          date = wDate.yearStart(date);

          if (date < calendar._settings.minDate) {
            date = wDate.copy(calendar._settings.minDate);
          } else if (date > calendar._settings.maxDate) {
            date = wDate.copy(calendar._settings.maxDate);
          }

          return 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");
          newdate = calendar._correctDate(newdate);
          if (!calendar._checkDate(newdate)) newdate = calendar._findActive(date, mode);

          if (newdate) {
            calendar._update_zoom_level(newdate);

            calendar.selectDate(newdate, false, false, "user");
          }

          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 = "";
      var cpad = this._content_padding + "px";
      config = this._settings;
      index$$1 = 2 - (config.weekHeader ? 0 : 1) - (config.monthHeader ? 0 : 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 = this._contentobj.offsetHeight - (config.monthHeader || this._zoom_in ? config.headerHeight : 0) - (this._content_padding + $active.borderWidth) * 2;
        if (type != "year" && type != "month") this._reserve_box_height -= config.timepickerHeight;
        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' style='margin:0 " + cpad + "'>" + this._timeHeaderTemplate(width, enLocale) + "</div>";
        html += "<div  class='webix_cal_body' style='height:" + this._reserve_box_height + "px; margin:0 " + cpad + ";'>"; // 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 / config.minuteStep % 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' style='margin:0 " + cpad + "'>" + this._timeButtonsTemplate() + "</div>";
        this._contentobj.innerHTML = html;
        this._contentobj.firstChild.style.marginTop = cpad;
      } else {
        //years and months
        //reset header
        if (config.monthHeader) {
          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[0].blur();

          if (config.navigation) {
            header[1].setAttribute("aria-label", labels[0]);
            header[2].setAttribute("aria-label", labels[1]);
          }
        } else //needed for "year" to set start value
          zlogic._getTitle(config.date, this);

        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 (config.weekHeader) {
          sections[index$$1 - 1].style.display = "none";
          if (index$$1 === 1) sections[index$$1].style.marginTop = cpad;
        }

        sections[index$$1].innerHTML = html;

        if (type != "year" && type != "month") {
          if (!sections[index$$1 + 1]) this._contentobj.innerHTML += "<div class='webix_time_footer' style='margin:0 " + cpad + "'>" + this._timeButtonsTemplate() + "</div>";else sections[index$$1 + 1].innerHTML = this._timeButtonsTemplate();
        } else if (sections[index$$1 + 1]) {
          sections[index$$1 + 1].style.display = "none";
        }

        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, config) {
      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, false, config);
      }
    },
    // selects date and redraw calendar
    _selectDate: function (date, add, config) {
      if (this.callEvent("onBeforeDateSelect", [date])) {
        this.selectDate(date, true, add, config);
        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, "user");
      },
      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, false, "user");
      },
      webix_cal_month_name: function () {
        if (!this._settings.navigation) return;
        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, "user");
        }
      }
    },
    _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, config) {
      this.selectDate(date, true, false, config);
    },
    getValue: function (format) {
      var date = this.getSelectedDate();
      if (format) date = wDate.dateToStr(format)(date);
      return date;
    },
    selectDate: function (date, show, add, config) {
      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, config]);
    },
    locate: function () {
      return null;
    }
  };
  var view$s = exports.protoUI(api$s, KeysNavigation, MouseEvents, base$1.view, EventSystem);

  var api$t = {
    name: "colorboard",
    defaults: {
      template: function (obj) {
        return "<div class=\"webix_color_item\" style=\"background-color:".concat(obj.val, ";\"></div>");
      },
      palette: null,
      height: 250,
      width: 260,
      cols: 11,
      rows: 10,
      minLightness: 0.15,
      maxLightness: 1,
      navigation: true,
      grayScale: true,
      type: "material" // "classic"

    },
    $init: function () {
      _event(this._viewobj, "click", bind(function (e) {
        // prevent selection the main item container
        var node = e.target.parentNode;
        var value = locate(node,
        /*@attr*/
        "webix_val"); // locate can return null in case of drag

        if (value) {
          var oldvalue = this._settings.value;
          value = this.setValue(value, "user");
          this.callEvent("onItemClick", [value, e]);
          if (value != oldvalue) this.callEvent("onSelect", [value]);
        }
      }, this));

      this.$view.setAttribute("role", "grid");

      this._viewobj.setAttribute("aria-readonly", "true");
    },
    _get_clear_palette: function () {
      return ["#F34336", "#FF9700", "#FFEA3B", "#4CB050", "#009788", "#00BCD4", "#2196F3", "#3F51B5", "#673BB7", "#9C28B1", "#EA1E63"];
    },
    _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$1.api.$setSize.call(this, x, y)) {
        this.render();
      }
    },
    getValue: function () {
      return this._settings.value;
    },
    _getBox: function () {
      return this._viewobj.firstChild;
    },
    $prepareValue: function (value) {
      value = value ? value.toString(16) : "";
      if (value && value.charAt(0) != "#" && /^[0-9a-fA-F]+$/.test(value)) value = "#" + value;
      return value;
    },
    value_setter: function (value) {
      return this.$prepareValue(value);
    },
    setValue: function (value, config) {
      value = this.$prepareValue(value);
      var oldvalue = this._settings.value;

      if (oldvalue != value) {
        this._settings.value = value;
        this.$setValue(value);
        this.callEvent("onChange", [value, oldvalue, config]);
      }

      return value;
    },
    $setValue: function (value) {
      if (this.isVisible(this._settings.id)) {
        // clear previous
        if (this._activeSelection) {
          var oldCell = this._getCell(this._activeSelection);

          this._setSelection(oldCell, false);
        }

        var ind = this._activeSelection = this._findIndex(value);

        if (ind) {
          var cell = this._getCell(ind);

          this._setSelection(cell, true);
        }
      }
    },
    _getCell: function (ind) {
      return this._viewobj.lastChild.childNodes[ind.row].childNodes[ind.col];
    },
    _setSelection: function (cell, state) {
      if (state) {
        cell.setAttribute("tabindex", "0");
        cell.setAttribute("aria-selected", "true");
        addCss(cell, "webix_color_selected");
      } else {
        cell.setAttribute("tabindex", "-1");
        cell.removeAttribute("aria-selected");
        removeCss(cell, "webix_color_selected");
      }
    },

    /* handle colors */
    _numToHex: function (n) {
      return color.toHex(n, 2);
    },
    _rgbToHex: function (r, g, b) {
      return "#" + this._numToHex(Math.floor(r)) + this._numToHex(Math.floor(g)) + this._numToHex(Math.floor(b));
    },
    _hslToRgb: function (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 = this._hue2rgb(p, q, h + 1 / 3);
        g = this._hue2rgb(p, q, h);
        b = this._hue2rgb(p, q, h - 1 / 3);
      }

      return {
        r: r * 255,
        g: g * 255,
        b: b * 255
      };
    },
    _hue2rgb: function (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;
    },
    _lightenRgb: function (rgb, lt) {
      /*	color = color * alpha + background * (1 - alpha); */
      var r = rgb[0] * lt + 255 * (1 - lt);
      var g = rgb[1] * lt + 255 * (1 - lt);
      var b = rgb[2] * lt + 255 * (1 - lt);
      return {
        r: r,
        g: g,
        b: b
      };
    },
    _getGrayScale: function (colCount) {
      var gray = [];
      var val = 255,
          step = val / colCount;

      for (var i = 0; i < colCount; i++) {
        val = Math.round(val > 0 ? val : 0);
        gray.push(this._rgbToHex(val, val, val));
        val -= step;
      }

      gray[gray.length - 1] = "#000000";
      return gray;
    },
    _initPalette: function (config) {
      /* default palette (material and custom) */
      var clearColors = this._get_clear_palette();

      config.cols = clearColors.length; // always use the same set

      var colors = [];
      var colorRows = config.rows - 1; // a row is reserved for clear colors		

      var lightStep = 1 / config.rows;
      var colorRange = null;

      if (this._settings.grayScale) {
        var grayColors = this._getGrayScale(config.cols);

        colors.push(grayColors.reverse()); // inverted order

        lightStep = 1 / colorRows;
        colorRows -= 1;
      }

      colors.push(clearColors);

      for (var step = 0, lt = config.maxLightness; step < colorRows; step++) {
        lt -= lightStep;
        colorRange = [];

        for (var col = 0; col < config.cols; col++) {
          var clearRgb = color.toRgb(clearColors[col]);

          var val = this._lightenRgb(clearRgb, lt);

          colorRange.push(this._rgbToHex(val.r, val.g, val.b));
        }

        colors.push(colorRange);
      }

      this._settings.palette = colors;
    },
    _initOldPalette: function (config) {
      /* old (classic) palette */
      var colors = [];
      var colorStep = 1 / config.cols;
      var colorRows = config.rows;
      var colorRange = null;

      if (this._settings.grayScale) {
        colors.push(this._getGrayScale(config.cols));
        colorRows -= 1;
      }

      var lightStep = (config.maxLightness - config.minLightness) / colorRows;

      for (var step = 0, lt = config.minLightness; step < colorRows; step++) {
        colorRange = [];

        for (var c = 0, col = 0; c < config.cols; c++) {
          var val = this._hslToRgb(col, 1, lt);

          colorRange.push(this._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) {
          // check if this is a last row
          var row = this._viewobj.lastChild.childNodes[ind.row];
          if (row) cell = row.childNodes[ind.col];
        }

        if (cell) {
          value = cell.getAttribute(
          /*@attr*/
          "webix_val");
          var config = details && details.e instanceof KeyboardEvent ? "user" : "auto";
          this.setValue(value, config);
          this.callEvent("onSelect", [this._settings.value]);

          if (focus !== false) {
            var sel = this._viewobj.querySelector("div[tabindex='0']");

            if (sel) sel.focus();
          }
        }
      }
    },
    _renderRow: function (row, widths, height) {
      var data = {
        width: 0,
        height: 0,
        val: 0
      };
      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 += this._renderItem(data);
      }

      rowHtml += "</div>";
      return rowHtml;
    },
    _renderItem: function (obj) {
      var colorTemplate = template(this._settings.template || "");
      return "<div role=\"gridcell\" tabindex=\"-1\" aria-label=\"".concat(obj.val, "\" style=\"width:").concat(obj.width, "px;height:").concat(obj.height, "px;\" ",
      /*@attr*/
      "webix_val", "=\"").concat(obj.val, "\">").concat(colorTemplate(obj), "</div>");
    },
    render: function () {
      if (!this.isVisible(this._settings.id)) return;
      var type = this._settings.type;

      if (!this._settings.palette) {
        if (type === "classic") this._initOldPalette(this._settings);else this._initPalette(this._settings);
      }

      var palette = this._settings.palette;
      this.callEvent("onBeforeRender", []);
      var padding = type === "classic" ? 0 : $active.colorPadding;
      var single$$1 = _typeof(palette[0]) == "object";
      var firstRow = single$$1 ? palette[0] : palette;
      var deltaWidth = padding * 2 + padding * (firstRow.length - 1);
      var deltaHeight = padding * 2 + padding * (single$$1 ? palette.length - 1 : 0);
      var width = this.$width - deltaWidth,
          height = this.$height - deltaHeight,
          widths = [];
      var html = "<div class=\"webix_color_palette ".concat("webix_palette_" + type, "\" role=\"rowgroup\">");

      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 += this._renderRow(row, widths, cellHeight);
        }
      } else html += this._renderRow(palette, widths, height);

      html += "</div>";
      this._viewobj.innerHTML = html;
      if (this._settings.value) this.$setValue(this._settings.value);else this._viewobj.lastChild.childNodes[0].childNodes[0].setAttribute("tabindex", "0");

      this._fix_cover();

      this.callEvent("onAfterRender", []);
    },
    refresh: function () {
      this.render();
    }
  };
  var view$t = exports.protoUI(api$t, KeysNavigation, base$1.view, EventSystem);

  var api$u = {
    name: "colorselect",
    defaults: {
      width: 260,
      height: 250,
      value: "#751FE0"
    },
    $init: function () {
      var _this = this;

      this._hValue = this._sValue = this._vValue = 0;

      if (env.touch) {
        this.attachEvent("onTouchStart", function (e, ctx) {
          var css = e.target.className;
          var parent = e.target.parentNode.className;
          if (css == "webix_color_block" || parent == "webix_color_block") _this._handle_dnd(ctx || e);else if (css.indexOf("webix_color_line") == 0) _this._handle_dnd(ctx || e, true);
        });
      }

      _event(this.$view, "keydown", function (e) {
        return _this._handle_move_keyboard(e);
      });

      this.attachEvent("onAfterRender", function () {
        var _this2 = this;

        if (!env.touch) {
          _event(this._colorBlock, "mousedown", function (e) {
            return _this2._handle_dnd(e);
          });

          _event(this._colorLine, "mousedown", function (e) {
            return _this2._handle_dnd(e, true);
          });
        }

        _event(this._colorOutText, "change", function () {
          return _this2.setValue(_this2._colorOutText.value, "user");
        });

        if (this._settings.button) _event(this._viewobj.querySelector(".webix_button"), "click", function () {
          _this2.callEvent("onColorSelect", [_this2.getValue()]);
        });
      });
      this.attachEvent("onDestruct", function () {
        this._colorCircle = this._colorLineCircle = this._colorBlock = null;
        this._colorLine = this._colorOutText = this._colorOutBlock = this._offset = null;
        if (this._handle_drag_events) this._detach_drag_events();
      });
    },
    $skin: function () {
      if ($name == "compact" || $name == "mini") this._inpHeight = 24;else this._inpHeight = 32;
    },
    $setSize: function (x, y) {
      if (base$1.api.$setSize.call(this, x, y)) {
        this.render();
      }
    },
    getValue: function () {
      return this._settings.value;
    },
    $prepareValue: function (value) {
      value = value ? value.toString(16) : "";
      if (value && value.charAt(0) != "#" && /^[0-9a-fA-F]+$/.test(value)) value = "#" + value;
      return value;
    },
    value_setter: function (value) {
      return this.$prepareValue(value);
    },
    setValue: function (value, config) {
      value = this.$prepareValue(value);
      var oldvalue = this._settings.value;

      if (oldvalue != value) {
        this._settings.value = value;
        this.$setValue(value);
        this.callEvent("onChange", [value, oldvalue, config]);
      }
    },
    $setValue: function (value) {
      if (this.isVisible(this._settings.id)) {
        var rgb = color.toRgb(value);

        if (value !== this._current_value) {
          //set by API
          var hsv = color.rgbToHsv.apply(color, _toConsumableArray(rgb));
          this._hValue = hsv[0];
          this._sValue = hsv[1];
          this._vValue = hsv[2];
        }

        var left = this._hValue * this._offset.width / 359;
        this._colorLineCircle.style.left = left + "px";
        var x = this._sValue * this._offset.width;
        var y = Math.abs(this._offset.height * (this._vValue - 1));
        this._colorCircle.style.left = Math.max(Math.min(x, this._offset.width), 0) + "px";
        this._colorCircle.style.top = Math.max(Math.min(y, this._offset.height), 0) + "px";

        this._colorCircle.setAttribute("aria-valuetext", value);

        this._colorLineCircle.setAttribute("aria-valuetext", value);

        this._setOutColors(rgb, value);

        this._setBlockColors();
      }
    },
    _setOutColors: function (rgb, hex) {
      if (!rgb) rgb = color.hsvToRgb(this._hValue, this._sValue, this._vValue);
      if (!hex) hex = "#" + color.rgbToHex(rgb);
      var bgColor = "rgb(".concat(rgb[0], ", ").concat(rgb[1], ", ").concat(rgb[2], ")");
      this._colorCircle.style.backgroundColor = bgColor;
      this._colorOutBlock.style.backgroundColor = bgColor;
      this._colorOutText.value = hex.toUpperCase();
      this._current_value = hex;
    },
    _setBlockColors: function () {
      var rgb = color.hsvToRgb(this._hValue, 1, 1);
      var rgbStr = "rgb(".concat(rgb[0], ", ").concat(rgb[1], ", ").concat(rgb[2], ")");
      this._colorLineCircle.style.backgroundColor = rgbStr;
      this._colorBlock.style.backgroundColor = rgbStr;
    },
    // dragging to set value
    _move_block: function (e) {
      var pos$$1 = env.touch ? {
        x: e.x,
        y: e.y
      } : pos(e);
      var x = pos$$1.x - this._offset.x;
      var y = pos$$1.y - this._offset.y;
      x = Math.max(Math.min(x, this._offset.width), 0);
      y = Math.max(Math.min(y, this._offset.height), 0);
      this._colorCircle.style.left = x + "px";
      this._colorCircle.style.top = y + "px";
      var pxX = this._offset.width / 100;
      var pxY = this._offset.height / 100;
      var s = Math.ceil(x / pxX) / 100;
      var v = Math.ceil(Math.abs(y / pxY - 100)) / 100;
      this._sValue = s;
      this._vValue = v;

      this._setOutColors();
    },
    _move_line: function (e) {
      var pos$$1 = env.touch ? {
        x: e.x,
        y: e.y
      } : pos(e);
      var x = pos$$1.x - this._offset.x;
      x = Math.max(Math.min(x, this._offset.width), 0);
      this._colorLineCircle.style.left = x + "px";
      var h = Math.round(x * 359 / this._offset.width);
      this._hValue = Math.max(Math.min(h, 359), 0);

      this._setOutColors();

      this._setBlockColors();
    },
    _handle_dnd: function (e, line) {
      var _this3 = this;

      this._offset = offset(this._colorBlock);

      if (line) {
        addCss(this._colorLine, "webix_color_area_active");

        this._move_line(e);
      } else {
        addCss(this._colorBlock, "webix_color_area_active");

        this._move_block(e);
      }

      if (env.touch) {
        this._handle_drag_events = [this.attachEvent("onTouchMove", function (e, ctx) {
          return _this3._handle_move_process(ctx || e, line);
        }), this.attachEvent("onTouchEnd", function () {
          return _this3._handle_move_stop(line);
        })];
      } else {
        this._handle_drag_events = [event$1(document.body, "mousemove", function (e) {
          return _this3._handle_move_process(e, line);
        }), event$1(window, "mouseup", function () {
          return _this3._handle_move_stop(line);
        })];
      }

      addCss(document.body, "webix_noselect");
    },
    _handle_move_process: function (e, line) {
      if (line) this._move_line(e);else this._move_block(e);
    },
    _detach_drag_events: function () {
      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 = null;
    },
    _handle_move_stop: function (line) {
      this._detach_drag_events();

      this.setValue(this._current_value, "user");

      if (line) {
        removeCss(this._colorLine, "webix_color_area_active");

        this._colorLineCircle.focus();
      } else {
        removeCss(this._colorBlock, "webix_color_area_active");

        this._colorCircle.focus();
      }

      removeCss(document.body, "webix_noselect");
    },
    _move_block_value: function (val, inc) {
      return Math.min(Math.max(val + inc / 100, 0), 1);
    },
    _move_line_value: function (val, inc) {
      return Math.min(Math.max(val + inc, 0), 359);
    },
    _handle_move_keyboard: function (e) {
      var code = e.keyCode;

      if (code > 32 && code < 41) {
        var match = /webix_color_(\w*)circle/.exec(e.target.className);
        if (!match) return;
        preventEvent(e);

        if (match[1].length) {
          //line
          if (code === 36) this._hValue = 0;else if (code === 35) this._hValue = 359;else {
            var inc = code === 37 || code === 40 || code === 34 ? -1 : 1;
            this._hValue = this._move_line_value(this._hValue, inc);
          }

          this._setBlockColors();
        } else {
          if (code === 36) {
            this._sValue = 0;
            this._vValue = 1;
          } else if (code === 35) this._sValue = this._vValue = 1;else if (code === 39 || code === 37) {
            var _inc = code === 39 ? 1 : -1;

            this._sValue = this._move_block_value(this._sValue, _inc);
          } else {
            var _inc2 = code === 33 || code === 38 ? 1 : -1;

            this._vValue = this._move_block_value(this._vValue, _inc2);
          }
        }

        this._setOutColors(); //paint value, black colors may have a bigger step


        if (this._settings.value == this._current_value) this.$setValue(this._current_value);else this.setValue(this._current_value, "user");
      }
    },
    moveSelection: function (mode) {
      if (mode == "pgup" || mode == "pgdown") {
        //line
        var inc = mode === "pgup" ? -1 : 1;
        this._hValue = this._move_line_value(this._hValue, inc);

        this._setBlockColors();
      } else if (mode != "top" && mode !== "bottom") {
        var _inc3 = mode == "up" || mode == "right" ? 1 : -1;

        if (mode == "down" || mode == "up") this._vValue = this._move_block_value(this._vValue, _inc3);else this._sValue = this._move_block_value(this._sValue, _inc3);
      }

      this._setOutColors();

      this.setValue(this._current_value, "auto");
    },
    render: function () {
      if (!this.isVisible(this._settings.id)) return;
      this.callEvent("onBeforeRender", []);
      var inpWidth = (this.$width - $active.dataPadding * 3) / 2; //24 paddings, 32 color line, 12 padding

      var bHeight = this.$height - 24 - 32 - this._inpHeight - (this._settings.button ? this._inpHeight + 12 : 0);
      var html = "<div class=\"webix_color_area\">";
      html += "\n\t\t\t<div ".concat(
      /*@attr*/
      "webix_disable_drag", "=\"true\" class=\"webix_color_block\" style=\"height:", bHeight, "px;\">\n\t\t\t\t<div class=\"webix_color_circle\" tabindex=\"0\" role=\"slider\"></div>\n\t\t\t</div>\n\t\t\t<div ",
      /*@attr*/
      "webix_disable_drag", "=\"true\" class=\"webix_color_line\">\n\t\t\t\t<div class=\"webix_color_line_circle\" tabindex=\"0\" role=\"slider\"></div>\n\t\t\t</div>\n\t\t\t<div class=\"webix_color_out\">\n\t\t\t\t<div style=\"width:").concat(inpWidth, "px\" class=\"webix_color_out_block\"></div>\n\t\t\t\t<input type=\"text\" style=\"width:").concat(inpWidth, "px\" class=\"webix_color_out_text\"></input>\n\t\t\t</div>\n\t\t");
      if (this._settings.button) html += "<div class='webix_secondary'><button class=\"webix_button\">".concat(i18n.combo.select, "</button></div>");
      html += "</div>";
      this._viewobj.innerHTML = html;

      this._collect_vars();

      this.$setValue(this._settings.value);

      this._fix_cover();

      this.callEvent("onAfterRender", []);
    },
    _collect_vars: function () {
      this._colorCircle = this._viewobj.querySelector(".webix_color_circle");
      this._colorLineCircle = this._viewobj.querySelector(".webix_color_line_circle");
      this._colorBlock = this._viewobj.querySelector(".webix_color_block");
      this._colorLine = this._viewobj.querySelector(".webix_color_line");
      this._colorOutText = this._viewobj.querySelector(".webix_color_out_text");
      this._colorOutBlock = this._viewobj.querySelector(".webix_color_out_block");
      this._offset = offset(this._colorBlock);
    },
    refresh: function () {
      this.render();
    }
  };
  var view$u = exports.protoUI(api$u, base$1.view, EventSystem);

  var api$v = {
    name: "button",
    touchable: true,
    $skin: function () {
      this.defaults.height = $active.buttonHeight || $active.inputHeight;
    },
    defaults: {
      template: function (obj, common) {
        var text = common.$renderInput(obj, common);
        if (obj.badge || obj.badge === 0) 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: "",
      value: "",
      borderless: true
    },
    $renderInput: function (obj) {
      return "<button type='button' " + (obj.popup ? "aria-haspopup='true'" : "") + " class='webix_button'>" + (obj.label || obj.value) + "</button>";
    },
    $init: function (config) {
      this._viewobj.className += " webix_control webix_el_" + (this.$cssName || this.name);
      this._destroy_with_me = [];

      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.css || !this._get_class(config.css) || this.defaults.css && !this._get_class(this.defaults.css)) {
        this._viewobj.className += " webix_secondary";
      }
    },
    _get_class: function (css) {
      if (typeof css == "string") {
        var classes = {
          webix_danger: 1,
          webix_transparent: 1,
          webix_primary: 1
        };

        for (var i in classes) {
          if (css.indexOf(i) !== -1) return true;
        }
      }

      return false;
    },
    _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._types[value]) this.$renderInput = template(this._types[value]);
      return value;
    },
    _set_inner_size: false,
    _types: {
      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'><img class='webix_image' style='max-width:#cheight#px; max-height:#cheight#px;' src = '#image#'><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();

      base.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 () {
      base.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$1.api.$setSize.call(this, x, y)) {
        this.render();
      }
    },
    setValue: function (value, config) {
      value = this.$prepareValue(value);
      var oldvalue = this._settings.value;

      if (this.$compareValue(oldvalue, value)) {
        if (this._rendered_input && value != this.$getValue()) this.$setValue(value);
        return;
      }

      this._settings.value = value;
      if (this._rendered_input) this.$setValue(value);
      this.callEvent("onChange", [value, oldvalue, config]);
    },
    $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 value === 0 ? "0" : (value || "").toString();
    },
    value_setter: function (value) {
      return this.$prepareValue(value);
    },
    //visual part of setValue
    $setValue: function (value) {
      var node = this.getInputNode();

      if (node && !this._types[this._settings.type]) {
        value = this._settings.label || value;
        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;

      var box = this._getBox();

      var isTopLabel = this._settings.labelPosition == "top" && this._dataobj.firstChild.contains(node);

      if (isTopLabel || 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) {
        var width = getTextSize(config.value || config.label || "", "webixbutton").width + (config.badge || config.badge === 0 ? getTextSize(config.badge, "webix_badge").width * 2 - 32 : 0) + (config.type === "icon" ? 24 : 0) + (config.type === "image" ? config.height - $active.inputPadding : 0);
        width = Math.max(config.minWidth || 0, width);
        config.width = Math.min(config.maxWidth || Infinity, width);
      }
    },
    _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$1.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";
              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");
        var opt = this.getOption(id);

        if (opt && !opt.disabled && this.callEvent("onBeforeTabClick", [id, ev])) {
          this.setValue(id, "user");
          this.focus();
          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(this._settings.step, "user");
      },
      webix_inp_counter_prev: function () {
        if (!this._settings.readonly) this.prev(this._settings.step, "user");
      },
      webix_input_icon: function () {
        this.getInputNode().focus();
      },
      webix_clear_icon: function () {
        if (this.$allowsClear) this.setValue("", "user");
        return false;
      },
      webix_inp_checkbox_border: function (e) {
        if (!this._settings.disabled && e.target.tagName != "DIV" && !this._settings.readonly) this.toggle("user");
      },
      webix_inp_checkbox_label: function () {
        if (!this._settings.readonly) this.toggle("user");
      },
      webix_inp_radio_border: function (e) {
        var id = locate(e,
        /*@attr*/
        "radio_id");
        var opt = this.getOption(id);

        if (opt && !opt.disabled) {
          this.setValue(id, "user");
          this.focus();
        }
      },
      webix_tab_more_icon: function (ev, obj, node) {
        var popup = this.getPopup();

        if (!popup.isVisible()) {
          popup.resize();
          popup.show(node, null, true);
        } else popup.hide();
      },
      webix_tab_close: function (e) {
        var id = locate(e,
        /*@attr*/
        "button_id");
        var opt = this.getOption(id);
        if (opt && !opt.disabled && this.callEvent("onBeforeTabClose", [id, e])) this.removeOption(id);
        return false;
      }
    },
    //method do not used by button, but  used by other child-views
    _check_options: function (opts) {
      assert(!!opts, this.name + ": options not defined");
      assert(isArray(opts), this.name + ": options must be an array");

      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$v = exports.protoUI(api$v, base$1.view, AutoTooltip, AtomRender, Settings, EventSystem);
  var button$1 = {
    api: api$v,
    view: view$v
  };

  var api$w = {
    name: "label",
    defaults: {
      template: "<div class='webix_el_box' style='width:#awidth#px;height:#aheight#px;line-height:#cheight#px'>#label#</div>"
    },
    $skin: function () {
      button$1.api.$skin.call(this);
      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;
    },
    $render: function (config) {
      if (config.align === "right") this._dataobj.firstChild.style.textAlign = "right";
    },
    _set_inner_size: false,
    _set_default_css: function () {},
    _calc_size: function (config) {
      var css = "webix_el_box webixlabel" + (this.queryView("toolbar", "parent") ? " webixtoolbarlabel" : "");
      config = config || this._settings;
      if (config.autowidth) config.width = getTextSize(config.label, css).width;
    }
  };
  var view$w = exports.protoUI(api$w, button$1.view);

  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._settings.readonly || this._custom_format) return;
          if (code > 105 && code < 112) //numpad operators
            code -= 64;

          if (nav_controls[code] && code !== 8 && code !== 46) {
            //del && bsp
            return;
          }

          preventEvent(e);

          this._on_key_pressed(e, code);
        });
        this.attachEvent("onAfterRender", this._after_render);

        this.getText = function () {
          return this.getInputNode().value;
        };

        this.$prepareValue = function (value) {
          return this._pattern(value, false);
        };

        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);
              }
            };
          }
        }
      } // initialize pattern before value_setter


      if (pattern) {
        this._settings.pattern = this.pattern_setter(pattern);
        delete config.pattern;
      }
    },
    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);
        var isCapsLock = e.getModifierState("CapsLock");
        if (!e.shiftKey && !isCapsLock || e.shiftKey && isCapsLock) 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$x = {
    name: "text",
    $allowsClear: true,
    _init_onchange: function () {
      var _this = this;

      if (this.$allowsClear) {
        var c = this._settings;
        var node = this.getInputNode(); //attach onChange handler only for controls which do not manage blur on their own
        //for example - combo

        if (!this._onBlur) _event(node, "change", this._applyChanges, {
          bind: this
        });
        if (c.suggest) $$(c.suggest).linkInput(this);

        if (c.clear && !this.addSection) {
          this._clear_icon = this.$view.querySelector(".webix_input_icon:last-child");
          if (node.tagName == "INPUT" || node.tagName == "SELECT") _event(node, "input", function (e) {
            return _this._toggleClearIcon(e.target.value);
          });
          var text = this.getText ? this.getText() : c.text || c.value;

          this._toggleClearIcon(text);
        }
      }
    },
    _applyChanges: function () {
      var value = this.getValue();
      var res = this.setValue(value, "user"); //controls with post formating, we need to repaint value

      if (this._custom_format && res === false) this.$setValue(value);
    },
    _toggleClearIcon: function (value) {
      var c = this._settings;
      if (!c.clear || !this._clear_icon) return;

      if (c.clear === "hover" || c.clear === "replace") {
        var css = value ? "webix_clear_icon " + (c.clear === "hover" ? c.icon : "wxi-close") : c.icon;
        this._clear_icon.className = "webix_input_icon " + css;
      } else {
        var state = value ? "" : "hidden";
        if (this._clear_icon.style.visibility !== state) this._clear_icon.style.visibility = state;
      }
    },
    $skin: function () {
      button$1.api.$skin.call(this);
      this.defaults.height = $active.inputHeight;
      this.defaults.inputPadding = $active.inputPadding;
      this._inputSpacing = $active.inputSpacing;
      this._labelTopHeight = $active.labelTopHeight;
    },
    $init: function (config) {
      if (config.labelPosition == "top" && isUndefined(config.height) && this.defaults.height) // textarea
        config.height = this.defaults.height + (config.label ? this._labelTopHeight : 0); // used in clear_setter

      if (!isUndefined(config.icon)) this._settings.icon = config.icon;
      if (this._onBlur) this.attachEvent("onBlur", function () {
        if (this._rendered_input) this._onBlur();
      });
      this.attachEvent("onAfterRender", this._init_onchange);
      this.attachEvent("onDestruct", function () {
        this._clear_icon = null;
      });
    },
    clear_setter: function (value) {
      if (value) {
        if (!this._settings.icon) value = true;
        if (value !== "hover" && value !== "replace") value = !!value;
      }

      return value;
    },
    $renderIcon: function (c) {
      var height = c.aheight - 2 * c.inputPadding;
      var padding = (height - 18) / 2 - 1;
      var right = this._inputSpacing / 2 - 24;
      var html = "";

      if (c.icon) {
        right += 24;
        html += "<span style='right:" + right + "px;height:" + (height - padding) + "px;padding-top:" + padding + "px;' class='webix_input_icon " + c.icon + "'></span>";
      }

      if (c.clear === true) {
        right += 24;
        html += "<span style='right:" + right + "px;height:" + (height - padding) + "px;padding-top:" + padding + "px;' class='webix_input_icon webix_clear_icon webix_icon_transparent wxi-close'></span>";
      }

      return html;
    },
    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.adjust();
        this.resize();
      } else if (!message && this._restorePadding) {
        config.bottomPadding = this._restorePadding = 0; //textarea

        if (!config.height) this.render();
        this.adjust();
        this.resize();
      } else this.render();
    },
    $getSize: function () {
      var sizes = base$1.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$1.api.$setSize.call(this, x, y)) {
        if (!x || !y) return;

        if (config.labelPosition == "top") {
          config.labelWidth = 0; // textarea

          if (!config.inputHeight) this._inputHeight = this._content_height - (config.label ? this._labelTopHeight : 0) - (this.config.bottomPadding || 0);
        } else {
          if (config.label) config.labelWidth = this._getLabelWidth(config.labelWidth, config.label);
          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 ? config.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 = obj.aheight - 2 * $active.inputPadding - 2 * $active.borderWidth;
      var rightPadding = obj.clear === true ? "padding-right:51px;" : "";

      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 + ";" + rightPadding + "'>" + text + "</div>";
      return common.$renderInput(obj, html, id);
    },
    _baseInputHTML: function (tag) {
      var html = "<" + tag + (this._settings.placeholder ? " placeholder='" + template.escape(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 top = this._settings.labelPosition == "top";
        var style = "text-align:".concat(config.labelAlign || "left", "; line-height:").concat(this._getLabelHeight(top), "px; ");
        if (top) style += "display:block;";else style += config.labelWidth ? "width:".concat(config.labelWidth, "px;") : "display:none;";
        label = "<label style='" + style + "' 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));
        var rightPadding = (config.icon || config.clear ? 27 : 0) + (config.icon && config.clear === true ? 24 : 0);
        rightPadding = rightPadding && !this.addSection ? "padding-right:" + rightPadding + "px;" : "";
        html += this._baseInputHTML("input") + "id='" + id + "' type='" + (config.type || this.name) + "'" + (config.editable ? " role='combobox'" : "") + " value='" + value + "' style='width:" + inputWidth + "px;text-align:" + inputAlign + ";" + rightPadding + "'";
        var attrs = config.attributes;
        if (attrs) for (var prop in attrs) {
          html += " " + prop + "='" + attrs[prop] + "'";
        }
        html += " />";
      }

      html += this.$renderIcon ? this.$renderIcon(config) : "";
      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
    },
    _getLabelWidth: function (width, label) {
      if (width == "auto") width = getTextSize(label, "webix_inp_label").width;
      return width ? Math.max(width, $active.dataPadding) : 0;
    },
    type_setter: function (value) {
      return value;
    },
    _set_inner_size: false,
    _set_default_css: function () {},
    _pattern: function (value) {
      return value;
    },
    $setValue: function (value) {
      value = this._pattern(value);
      this.getInputNode().value = value;

      this._toggleClearIcon(value);
    },
    $getValue: function () {
      return this._pattern(this.getInputNode().value, false);
    },
    setValueHere: function (v, data, details) {
      if (details && details.symbol) {
        var s = details.symbol;
        var value = this.getValue();
        var last = value.substring(details.pos);
        value = value.substring(0, details.pos);
        value = value.substring(0, value.lastIndexOf(s) + s.length) + v;
        this.setValue(value + last, details.config);
        setSelectionRange(this.getInputNode(), value.length);
      } else this.setValue(v, details && details.config);
    },
    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$x = exports.protoUI(api$x, TextPattern, button$1.view);
  var text = {
    api: api$x,
    view: view$x
  };

  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$y = {
    name: "select",
    defaults: {
      template: function (obj, common) {
        var id = "x" + uid();
        var html = common._baseInputHTML("select") + "id='" + id + "' style='width:" + common._get_input_width(obj) + "px;'>";
        var optview = $$(obj.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 {
          var options = common._check_options(obj.options);

          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;
      }
    },
    $renderIcon: function () {
      return "";
    },
    //get input element
    getInputNode: function () {
      return this._dataobj.getElementsByTagName("select")[0];
    }
  };
  var view$y = exports.protoUI(api$y, text.view);

  var api$z = {
    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();
    },
    $prepareValue: 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 (config) {
      var c = this._settings;
      var value = this.getValue() != c.checkValue ? c.checkValue : c.uncheckValue;
      this.setValue(value, config);
    },
    getValue: function () {
      var c = this._settings;
      return c.value == c.checkValue ? c.checkValue : c.uncheckValue;
    },
    getInputNode: function () {
      return this.$view.getElementsByTagName(this._settings.customCheckbox ? "button" : "input")[0];
    },
    $skin: function () {
      text.api.$skin.call(this);
      this.defaults.customCheckbox = !!$active.customCheckbox;
    }
  };
  var view$z = exports.protoUI(api$z, text.view);
  var checkbox = {
    api: api$z,
    view: view$z
  };

  var api$A = {
    name: "radio",
    defaults: {
      template: function (config, common) {
        common._check_options(config.options);

        var options = common._filterOptions(config.options);

        var active = common._getFirstActive();

        var id, eachid, isChecked, isDisabled, label, tooltip, customRadio, optlabel, rd, input, focusable;
        var html = [];

        for (var i = 0; i < options.length; i++) {
          eachid = "x" + uid();
          id = id || eachid;
          if (i && (options[i].newline || config.vertical)) html.push("<div class='webix_line_break'></div>");
          isChecked = options[i].id == config.value;
          focusable = isChecked || !config.value && options[i].id === active;
          isDisabled = !!options[i].disabled;
          label = options[i].value || "";
          tooltip = config.tooltip ? " webix_t_id='" + options[i].id + "'" : "";
          customRadio = config.customRadio || "";

          if (customRadio) {
            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" + (!isDisabled && focusable ? "0" : "-1"));
            customRadio = customRadio.replace(/(aria-invalid=')\w*(?=')/, "$1" + (config.invalid ? "true" : "false"));
            customRadio = customRadio.replace(/(button_id=')\w*(?=')/, "$1" + options[i].id);
            if (isDisabled) customRadio = customRadio.replace("role='radio'", "role='radio' webix_disabled='true'");
          }

          rd = common._baseInputHTML("input") + " name='" + (config.name || config.id) + "' type='radio' " + (isChecked ? "checked='1'" : "") + "tabindex=" + (!isDisabled && focusable ? "0" : "-1") + " value='" + options[i].id + "' id='" + eachid + "'" + (isDisabled ? " disabled='true'" : "") + " style='" + (customRadio ? "display:none" : "") + "' />";
          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" + (isDisabled ? " webix_disabled" : "") + "' 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);

      var options = this._filterOptions(this._settings.options);

      if (options) {
        var count = this._settings.vertical ? 0 : 1;

        for (var i = 0; i < options.length; i++) {
          if (this._settings.vertical || 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");

      var active = this._getFirstActive();

      var id, option, focusable, parentNode, button;

      for (var i = 0; i < inp.length; i++) {
        id = inp[i].parentNode.getAttribute(
        /*@attr*/
        "radio_id");
        option = this.getOption(id);
        inp[i].checked = id == value;
        focusable = option && !option.disabled && (inp[i].checked || !value && option.id == active);
        inp[i].setAttribute("tabindex", focusable ? "0" : "-1");
        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) {
            button = parentNode.getElementsByTagName("BUTTON");

            if (button[0]) {
              button[0].setAttribute("aria-checked", inp[i].checked ? "true" : "false");
              button[0].setAttribute("tabindex", focusable ? "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 () {
      text.api.$skin.call(this);
      this.defaults.customRadio = !!$active.customRadio;
      if ($active.optionHeight) this.defaults.optionHeight = $active.optionHeight;
    }
  };
  var view$A = exports.protoUI(api$A, text.view, HTMLOptions);

  var api$B = {
    name: "datepicker",
    _editable: true,
    $init: function (config) {
      // value_setter handling
      if (config.multiselect) {
        this._settings.multiselect = config.multiselect;
      }

      if (config.type) {
        this._settings.type = config.type;
      }

      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 () {
      var text$$1 = this.getText();
      if (this._settings.text == text$$1) return;
      var value = this._settings.editable ? this.getValue() : this.getPopup().getValue();
      this.setValue(value || "", "user");
    },
    $skin: function () {
      text.api.$skin.call(this);
      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) {
      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) {
      var _this = this;

      if (this._settings.multiselect) return [].concat(value).map(function (a) {
        return _this._get_visible_text_single(a);
      }).join(this.config.separator);else return this._get_visible_text_single(value);
    },
    _get_visible_text_single: function (value) {
      var formatStr = this._formatStr;

      if (!formatStr) {
        if (this._settings.type == "time") formatStr = i18n.timeFormatStr;else if (this.config.timepicker) formatStr = i18n.fullDateFormatStr;else formatStr = i18n.dateFormatStr;
      }

      return formatStr(value);
    },
    _set_visible_text: function () {
      var node = this.getInputNode();
      if (isUndefined(node.value)) 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();

      this._toggleClearIcon(this._settings.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 () {
      var _this2 = this;

      if (this._settings.multiselect) {
        var value = this._settings.value;
        if (!value) return [];
        var result = [].concat(value).map(function (a) {
          return _this2._get_value_single(a);
        });
        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;
      var timeMode = type == "time"; //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;

          if (!formatDate) {
            if (timeMode) formatDate = i18n.timeFormatDate;else if (this.config.timepicker) formatDate = i18n.fullDateFormatDate;else formatDate = i18n.dateFormatDate;
          }

          var time = formatDate(this.getInputNode().value);

          if (timeMode && value != "") {
            value.setHours(time.getHours());
            value.setMinutes(time.getMinutes());
          } else value = time;
        } //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();
      if (!node) return "";

      if (isUndefined(node.value)) {
        if (node.firstChild && node.firstChild.className === "webix_placeholder") return "";
        return node.innerHTML;
      }

      return node.value;
    }
  };
  var view$B = exports.protoUI(api$B, text.view);
  var datepicker = {
    api: api$B,
    view: view$B
  };

  var api$C = {
    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"
      });

      this._init_once = function () {};
    },
    clear_setter: function (value) {
      return !!value;
    },
    getValue: function () {
      if (this._rendered_input && this._settings.editable) return this.getInputNode().value;else return this._settings.value;
    },
    $prepareValue: function (value) {
      value = value ? value.toString(16) : "";
      if (value && value.charAt(0) != "#" && /^[0-9a-fA-F]+$/.test(value)) value = "#" + value;
      return value;
    },
    _getColorNode: function () {
      return this.$view.getElementsByTagName("DIV")[this._settings.editable ? 1 : 2];
    },
    _get_visible_text: function (value) {
      return value;
    },
    $compareValue: function (oldvalue, value) {
      return oldvalue == value;
    },
    $setValue: function (value) {
      this._getColorNode().style.backgroundColor = value;
      this._settings.text = value;

      this._toggleClearIcon(value);

      var node = this.getInputNode();
      if (isUndefined(node.value)) node.innerHTML = value;else node.value = value;
    },
    $renderIcon: function (c) {
      var right = this._inputSpacing / 2 + 5;
      var html = "<div class='webix_input_icon' style='top:" + (c.inputPadding + 4) + "px;right:" + right + "px;background-color:" + c.value + ";'></div>";

      if (c.clear) {
        var height = c.aheight - 2 * c.inputPadding;
        var padding = (height - 18) / 2 - 1;
        right += 24;
        html += "<span style='right:" + right + "px;height:" + (height - padding) + "px;padding-top:" + padding + "px;' class='webix_input_icon webix_clear_icon wxi-close'></span>";
      }

      return html;
    }
  };
  var view$C = exports.protoUI(api$C, datepicker.view);

  var api$D = {
    name: "richselect",
    defaults: {
      template: function (obj, common) {
        return common._render_div_block(obj, common);
      },
      popupWidth: 200,
      icon: "wxi-menu-down"
    },
    _onBlur: function () {
      var text$$1 = this.getText();
      if (this._settings.text == text$$1 || isUndefined(this._settings.text) && !text$$1) return;
      var suggest = this.getPopup(); //handle clicks on suggest items

      if (suggest.$view.contains(document.activeElement)) return;
      var nodeValue = this.getInputNode().value;
      var value = suggest.getSuggestion(nodeValue);
      var 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, "user");else if (nodeValue === "") this.setValue("", "user");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();
    },
    _reset_value: function () {
      // multiselect requires value as an array
      var value = this._settings.value || [];
      if (value.length && !this.getPopup().isVisible() && this.getInputNode() && !(this._settings.text || this.getText())) this.$setValue(value);
    },
    $skin: function () {
      text.api.$skin.call(this);
      this.defaults.inputPadding = $active.inputPadding;
    },
    $render: function (obj) {
      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 (isUndefined(node.value)) {
        if (node.firstChild && node.firstChild.className === "webix_placeholder") return "";
        return node.innerHTML;
      }

      return node.value;
    },
    $prepareValue: function (value) {
      if (value && value.id) return value; //don't convert new items

      return text.api.$prepareValue.call(this, value);
    },
    $setValue: function (value) {
      var text$$1 = value;
      var popup = this.getPopup();
      if (popup) text$$1 = popup.getItemText(value);

      if (value && value.id) {
        //add new value
        var list = popup.getList();
        var exists = list.exists(value.id); // add new item only when item with such id doesn't exists yet

        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 = this.$prepareValue(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;

      this._toggleClearIcon(text$$1);
    },
    getValue: function () {
      return this._settings.value || "";
    },
    _ignoreLabelClick: function (ev) {
      this.focus();
      preventEvent(ev);
    }
  };
  var view$D = exports.protoUI(api$D, text.view);
  var richselect = {
    api: api$D,
    view: view$D
  };

  var api$E = {
    name: "combo",
    getInputNode: function () {
      return this._dataobj.getElementsByTagName("input")[0];
    },
    _revertValue: function () {
      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, "user");else this.$setValue(value);
    },
    defaults: {
      template: function (config, common) {
        return common.$renderInput(config).replace(/(<input)\s*(?=\w)/, "$1" + " role='combobox'");
      },
      icon: "wxi-menu-down"
    },
    on_click: {
      webix_clear_icon: function () {
        if (this.$allowsClear) this.setValue("", "user");
        return false;
      },
      "webix_inp_label": function (e) {
        this._ignoreLabelClick(e);
      },
      "webix_inp_top_label": function (e) {
        this._ignoreLabelClick(e);
      }
    }
  };
  var view$E = exports.protoUI(api$E, richselect.view);

  var api$F = {
    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) {
      if (this._settings.readonly) return;
      var code = e.which || e.keyCode,
          c = this._settings;
      var 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(c.step, "user");else if (code === 34) this.prev(c.step, "user");else value = value + (code === 37 || code === 40 ? -1 : 1);
        if (code > 34 && value >= c.min && value <= c.max) this.setValue(value, "user");
      }
    },
    $setValue: function (value) {
      this.getInputNode().value = value;
    },
    $prepareValue: function (value) {
      value = parseFloat(value);
      var min = this._settings.min;
      var max = this._settings.max;
      if (isNaN(value)) value = isFinite(min) ? min : 0;
      return Math.min(Math.max(value, min), max);
    },
    getInputNode: function () {
      return this._dataobj.getElementsByTagName("input")[0];
    },
    getValue: function () {
      return button$1.api.getValue.apply(this, arguments) * 1;
    },
    next: function (step, config) {
      step = 1 * (step || this._settings.step);
      this.shift(step, config);
    },
    prev: function (step, config) {
      step = -1 * (step || this._settings.step);
      this.shift(step, config);
    },
    shift: function (step, config) {
      //round values to fix math precision issue in JS
      var new_value = Math.round((this.getValue() + step) * 100000) / 100000;
      this.setValue(new_value, config);
    }
  };
  var view$F = exports.protoUI(api$F, text.view);

  var api$G = {
    name: "icon",
    $skin: function () {
      button$1.api.$skin.call(this);
      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>";
        var lineHeight = obj.aheight != min ? obj.aheight : 0;
        return "<div class='webix_el_box' style='width:" + obj.awidth + "px;height:" + obj.aheight + "px;line-height:" + lineHeight + "px;margin-top:" + top + "px'>" + inner + (obj.badge || obj.badge === 0 ? "<span class='webix_badge'>" + obj.badge + "</span>" : "") + "</div>";
      }
    },
    _set_inner_size: false,
    _set_default_css: function () {},
    $setValue: function () {}
  };
  var view$G = exports.protoUI(api$G, button$1.view);

  var api$H = {
    name: "search",
    on_click: {
      webix_clear_icon: function () {
        if (this.$allowsClear) this.setValue("", "user");
        return false;
      },
      "webix_input_icon": function (e) {
        this.getInputNode().focus();
        if (this.config.clear !== "hover" && e.target && e.target.className.indexOf(this.config.icon) !== -1) this.callEvent("onSearchIconClick", [e]);
      }
    },
    defaults: {
      type: "text",
      icon: "wxi-search"
    }
  };
  var view$H = exports.protoUI(api$H, text.view);

  var api$I = {
    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) {
        common._check_options(obj.options);

        var options = common._filterOptions(obj.options);

        var width = common._get_input_width(obj);

        var borders = $name == "contrast" ? 0 : options.length - 1;
        var optionWidth = obj.optionWidth || Math.floor((width - borders) / options.length);
        var html = "<div style='width:" + width + "px' class='webix_all_segments' role='tablist' aria-label='" + template.escape(obj.label) + "'>";
        var tooltip, isDisabled;
        if (!obj.value) obj.value = common._getFirstActive(true);

        for (var i = 0; i < options.length; i++) {
          isDisabled = !!options[i].disabled;
          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='" + (!isDisabled && obj.value == options[i].id ? "0" : "-1") + "' class='" + "webix_segment_" + (i == options.length - 1 ? "N" : i > 0 ? 1 : 0) + (obj.value == options[i].id ? " webix_selected" : "") + (isDisabled ? " webix_disabled" : "") + "' " + (isDisabled ? "webix_disabled='true' " : "") +
          /*@attr*/
          "button_id='" + options[i].id + "'" + tooltip + ">" + options[i].value + "</button>";
        }

        return common.$renderInput(obj, html + "</div>", uid());
      }
    },
    _getInputNode: function () {
      return this.$view.getElementsByTagName("BUTTON");
    },
    focus: function () {
      return this._focus();
    },
    blur: function () {
      this._blur();
    },
    $setValue: function (value) {
      //refresh tabbar if the option is in the popup list
      var popup = this.config.tabbarPopup;
      if (popup && $$(popup) && $$(popup).getBody().exists(value)) return this.refresh();

      var inputs = this._getInputNode();

      var id, option;

      for (var i = 0; i < inputs.length; i++) {
        id = inputs[i].getAttribute(
        /*@attr*/
        "button_id");
        option = this.getOption(id);
        inputs[i].setAttribute("aria-selected", value == id ? "true" : "false");
        inputs[i].setAttribute("tabindex", option && !option.disabled && value == id ? "0" : "-1");
        if (value == id) addCss(inputs[i], "webix_selected");else removeCss(inputs[i], "webix_selected");
      }
    },
    $getValue: function () {
      return this._settings.value || "";
    },
    getValue: function () {
      return this._settings.value;
    },
    getInputNode: function () {
      return null;
    },
    _set_inner_size: false
  };
  var view$I = exports.protoUI(api$I, text.view, HTMLOptions);
  var segmented = {
    api: api$I,
    view: view$I
  };

  var api$J = {
    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);
      },
      minHeight: 60
    },
    $skin: function () {
      text.api.$skin.call(this);
      this.defaults.height = 0;
    },
    _skipSubmit: true,
    _getLabelHeight: function (top) {
      return top ? this._labelTopHeight - this._settings.inputPadding : "";
    },
    $renderIcon: function () {
      return "";
    },
    //get input element
    getInputNode: function () {
      return this._dataobj.getElementsByTagName("textarea")[0];
    }
  };
  var view$J = exports.protoUI(api$J, text.view);

  var api$K = {
    name: "toggle",
    $allowsClear: true,
    $init: function () {
      this.attachEvent("onItemClick", function () {
        this.toggle("user");
      });
    },
    $renderInput: function (obj) {
      return "<button type='button' " + (obj.popup ? "aria-haspopup='true'" : "") + " class='webix_button'>" + obj.label + "</button>";
    },
    $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 children = input.children; //icon or image button

      if (this._types[obj.type]) {
        var icon = children[0];
        if (icon.nodeName == "SPAN" && obj.onIcon && obj.offIcon && obj.onIcon != obj.offIcon) icon.className = icon.className.replace(isPressed ? obj.offIcon : obj.onIcon, isPressed ? obj.onIcon : obj.offIcon);
        if (obj.type == "imageTop" || obj.type == "iconTop") children[1].innerHTML = text;else {
          input.innerHTML = text;
          input.insertBefore(icon, input.firstChild);
        }
      } else input.innerHTML = text;

      input.setAttribute("aria-pressed", isPressed ? "true" : "false");
      var changeCss = isPressed ? addCss : removeCss;
      changeCss(input.parentNode, "webix_pressed");
    },
    toggle: function (config) {
      this.setValue(!this.getValue(), config);
    },
    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 || obj.badge === 0) html = html.replace(/<\/div>$/, "<span class='webix_badge'>" + obj.badge + "</span></div>");
        return html;
      }
    },
    _set_inner_size: false
  };
  var view$K = exports.protoUI(api$K, button$1.view);

  var api$L = {
    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 = "";
    },
    $renderIcon: function (c) {
      if (c.icon) {
        var height = c.aheight - 2 * c.inputPadding;
        var padding = (height - 18) / 2 - 1;
        var aria = "role='button' tabindex='0' aria-label='" + i18n.aria["multitext" + (c.mode || "") + "Section"] + "'";
        return "<span style='right:0;height:" + (height - padding) + "px;padding-top:" + padding + "px;' class='webix_input_icon webix_multitext_icon " + c.icon + "' " + aria + "></span>";
      }

      return "";
    },
    _subOnChange: function (v, o, config) {
      var parent = this.config.master ? $$(this.config.master) : this;
      var value = parent.getValue();
      var oldvalue = parent._settings.value;

      if (value != oldvalue) {
        parent._settings.value = value;
        parent.callEvent("onChange", [value, oldvalue, config]);
      }
    },
    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") {
          var parent = this.getParentView();
          this.removeSection(this.config.id);
          var childs = parent.getChildViews();
          childs[childs.length - 1].focus();

          this._subOnChange(null, null, "user");
        } else $$(this.addSection()).focus();

        return false;
      }
    },
    $init: function () {
      this._subs = _to_array([]);
      this.attachEvent("onKeyPress", this._onKeyPress);
    },
    $render: function (obj) {
      this.$setValue(obj.value);
    }
  };
  var view$L = exports.protoUI(api$L, 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$M = {
    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;'" : "") + ">&nbsp;</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 = this._safeValue(value);
        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 = this._safeValue(corner2, 2 * this._sliderBorder, size - this._sliderPadding * 2 - 2 * this._sliderBorder);
        corner1 = this._safeValue(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);
    },
    _safeValue: function (value, min, max) {
      min = min ? min : this._settings.min;
      max = max ? max : this._settings.max;
      return Math.min(Math.max(value, min), max);
    },
    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();
      }
    },
    $setValue: function () {
      this.refresh();
    },
    $getValue: function () {
      return this._settings.value;
    },
    $prepareValue: function (value) {
      value = parseFloat(value);
      if (isNaN(value)) value = this._settings.min;
      return this._safeValue(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 () {
      text.api.$skin.call(this);
      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;
        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, "user");
          this._activeIndex = -1;
        }
      }
    },
    _on_mouse_down_start: function (e) {
      if (this._handle_drag_events) return;
      var trg = e.target;

      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 = null;
      }

      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, "user");

      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 this._safeValue(newvalue);
    },
    _init_onchange: function () {} //need not ui.text logic

  };
  var view$M = exports.protoUI(api$M, text.view);

  exports.protoUI({
    name: "rangeslider",
    defaults: {
      template: "GPL version doesn't support rangeslider <br> You need Webix PRO"
    }
  }, template$1.view);

  var api$N = {
    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 () {
      checkbox.api.$skin.call(this);
      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("user");
      },
      "webix_label_right": function () {
        if (!this._settings.readonly) this.toggle("user");
      }
    }
  };
  var view$N = exports.protoUI(api$N, checkbox.view);

  var api$O = {
    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._filterOptions(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 _this = this;

      var obj = this._settings; // if tabbar popup is set as plain object with config

      if (!obj.tabbarPopup || !$$(obj.tabbarPopup)) {
        var popupConfig = exports.extend({
          view: "popup",
          autofocus: false,
          width: obj.popupWidth || 200
        }, obj.tabbarPopup || {});
        var body = exports.extend({
          view: "list",
          borderless: true,
          select: true,
          autoheight: true,
          yCount: obj.yCount || 7,
          template: template(obj.popupTemplate || "#value#")
        }, obj.tabbarPopup ? obj.tabbarPopup.body || {} : {}, true);
        body.css = "webix_tab_list ".concat(body.css || "");
        popupConfig.body = body;

        var _view = ui(popupConfig);

        var list = _view.getBody();

        _view.attachEvent("onShow", function () {
          list.unselect();
          UIManager.setFocus(list);
          var node = list.getItemNode(list.getFirstId());
          if (node) node.focus();
        });

        list.attachEvent("onItemClick", function (id) {
          return _this._popupInnerClick(id);
        });
        list.attachEvent("onEnter", function () {
          return _this._popupInnerClick();
        });
        obj.tabbarPopup = _view._settings.id;

        this._destroy_with_me.push(_view);
      }

      this._init_popup = function () {};
    },
    _popupInnerClick: function (id) {
      var popup = $$(this._settings.tabbarPopup);
      id = id || popup.getBody().getSelectedId();

      if (id && this.callEvent("onBeforeTabClick", [id])) {
        this.setValue(id, "user");
        popup.hide();
        this.callEvent("onAfterTabClick", [id]);
        this.refresh();
        this.focus();
      }

      return false;
    },
    getPopup: function () {
      this._init_popup();

      return $$(this._settings.tabbarPopup);
    },
    moreTemplate_setter: template,
    defaults: {
      moreTemplate: "<span class=\"webix_icon wxi-dots\"></span>",
      template: function (obj, common) {
        common._check_options(obj.options);

        var tabs = common._filterOptions(obj.options);

        var contentWidth, html, leafWidth, resultHTML, style, sum, verticalOffset, width;

        if (!tabs.length) {
          html = "<div class='webix_tab_filler' style='width:" + common._input_width + "px; border-right:0px;'></div>";
        } else {
          if (!obj.value) obj.value = common._getFirstActive(true);
          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 body = popup.getBody() || null;

            if (body) {
              if (sizes.max) {
                for (var i = 0, found = false; 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);
                    }
                  }
                }

                body.clearAll();
                body.parse(tabs.slice(sizes.max));
              } else {
                body.clearAll();
              }
            }
          } else if (common._settings.tabbarPopup) {
            var _popup = $$(common._settings.tabbarPopup);

            if (_popup) _popup.hide();
          }

          sum = obj.tabOffset;

          for (var _i = 0, lastTab = false; _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 = "",
          isDisabled = !!tab.disabled,
          config = this.config;
      if (tab.id == config.value) className += " webix_selected";
      if (isDisabled) className += " webix_disabled";
      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=\"" + (!isDisabled && tab.id == config.value ? "0" : "-1") + "\" style=\"width:" + width + "px;\"" + (isDisabled ? " webix_disabled=\"true\"" : "") + 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 (!isDisabled && (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$O = exports.protoUI(api$O, segmented.view);

  var api$P = {
    name: "richtext",
    defaults: {
      label: "",
      labelWidth: 80,
      labelPosition: "left"
    },
    $init: function () {
      this._viewobj.className += " webix_richtext";
      this.$ready.unshift(this._setLayout);
    },
    $skin: function () {
      layout.api.$skin.call(this);
      this.defaults.paddingX = $active.inputSpacing / 2;
      this.defaults.paddingY = $active.inputPadding;
    },
    getInputNode: function () {
      return this.$view.querySelector(".webix_richtext_editor");
    },
    _button: function (name) {
      return {
        view: "toggle",
        type: "icon",
        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, "user");
            });

            _event(top.getInputNode(), "keyup", function () {
              top._getselection("auto");
            });
          }
        },
        onClick: {
          webix_richtext_editor: function () {
            top._getselection("auto");
          }
        }
      };
      var controls = [this._button("underline"), this._button("bold"), this._button("italic"), {}];
      var editorToolbar = {
        view: "toolbar",
        id: "toolbar",
        elements: controls
      };
      var rows = [editorToolbar, editField];
      var config = this.config;

      if (config.labelPosition == "top") {
        editorToolbar.elements = controls.concat([{
          view: "label",
          label: config.label,
          align: "right"
        }, {
          width: 4
        }]);
        this.rows_setter(rows);
      } else {
        config.labelWidth = config.label ? this._getLabelWidth(config.labelWidth, config.label) : 0;

        if (config.labelWidth) {
          config.margin = 0;
          this.cols_setter([{
            template: config.label,
            css: "webix_richtext_inp_label" + (config.required ? " webix_required" : ""),
            borderless: true,
            width: config.labelWidth
          }, {
            rows: rows
          }]);
        } else this.rows_setter(rows);
      }
    },
    _getLabelWidth: function (width, label) {
      if (width == "auto") width = getTextSize(label, "webix_inp_label").width;
      return width ? Math.max(width, $active.dataPadding) : 0;
    },
    _getselection: function (config) {
      var top = this;
      var bar = top.$$("toolbar");
      var sel;
      bar.setValues({
        italic: false,
        underline: false,
        bold: false
      }, false, config);

      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, config);
          }

          if (document.queryCommandState("underline")) {
            top.$$("underline").setValue(true, config);
          }

          if (document.queryCommandState("italic")) {
            top.$$("italic").setValue(true, config);
          }
        }
      }
    },
    refresh: function () {
      if (this._rendered_input) this.getInputNode().innerHTML = this._settings.value;
    },
    _execCommandOnElement: function (commandName) {
      var sel, selText;

      if (window.getSelection) {
        sel = window.getSelection();
        selText = sel.toString().length;
        var input = this.getInputNode();
        if (!input.contains(sel.anchorNode) || !input.contains(sel.focusNode)) return;
      } else return; //ie8


      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();
            sel.addRange(range);
            document.execCommand(commandName, false, "");
          }
        }
      }
    },
    _add_data: function () {
      var top = this.getTopParentView();

      top._execCommandOnElement(this.config.action);
    },
    focus: function () {
      if (!UIManager.canFocus(this)) return false;
      var editableElement = this.getInputNode();
      editableElement.focus();
    },
    _updateValue: function (value, config) {
      value = this.$prepareValue(value);
      var oldvalue = this._settings.value;

      if (oldvalue != value) {
        this._settings.value = value;
        this.callEvent("onChange", [value, oldvalue, config]);
      }
    },
    setValue: function (value, config) {
      this._updateValue(value, config);

      this.refresh();
    },
    $prepareValue: function (value) {
      return value === 0 ? "0" : (value || "").toString();
    },
    value_setter: function (value) {
      return this.$prepareValue(value);
    },
    getValue: function () {
      var input = this.getInputNode();
      if (input) return input.innerHTML;
      return this._settings.value;
    }
  };
  var view$P = exports.protoUI(api$P, IdSpace, layout.view);

  var api$Q = {
    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$1.api.$setSize.apply(this, arguments)) this.render();
    },
    _id:
    /*@attr*/
    "webix_item",
    on_mouse_move: {},
    type: {}
  };
  var view$Q = exports.protoUI(api$Q, PagingAbility, DataMarks, AutoTooltip, ValidateCollection, RenderStack, DataLoader, base$1.view, EventSystem, Settings);
  var proto = {
    api: api$Q,
    view: view$Q
  };

  var api$R = {
    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._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 () {
      var c = this._settings;
      if (c.autoheight || c.autowidth) return this.resize();

      if (c.layout == "y") {
        if (c.yCount) this._auto_height_calc(c.yCount);
      } else {
        if (c.xCount) this._auto_width_calc(c.xCount);
      }
    },
    _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;
    },
    $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$1.api.$getSize.call(this, dx, dy);
    },
    $setSize: function () {
      base$1.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.disabled) css += " webix_disabled";

        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\"" : "") + (obj.disabled ? " aria-disabled=\"true\" webix_disabled=\"true\"" : "");
      },
      template: function (obj) {
        return (obj.icon ? "<span class='webix_list_icon webix_icon " + obj.icon + "'></span>" : "") + obj.value + (obj.badge || obj.badge === 0 ? "<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;
    },
    disableItem: function (id) {
      this._set_item_disabled(id, true);
    },
    enableItem: function (id) {
      this._set_item_disabled(id, false);
    },
    _set_item_disabled: function (id, state) {
      var item = this.getItem(id);

      if (item) {
        item.disabled = state;
        this.refresh(id);
      }
    },
    isItemEnabled: function (id) {
      var item = this.getItem(id);
      return item && !item.disabled;
    },
    _skip_item: function (id, prev, dir) {
      if (!this.isItemEnabled(id)) {
        id = this.getNextId(id, dir) || null;
        return id && id != prev ? this._skip_item(id, prev, dir) : prev;
      }

      return id;
    }
  };
  var view$R = exports.protoUI(api$R, CustomPrint, KeysNavigation, DataMove, DragItem, MouseEvents, SelectionModel, Scrollable, proto.view, CopyPaste);
  var list = {
    api: api$R,
    view: view$R
  };

  /*
  	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$S = {
    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 () {},
    $skin: function () {
      button$1.api.$skin.call(this);
      if ($name == "material" || $name == "mini") this.defaults.css = "webix_primary";
    },
    $init: function () {
      this.files = new DataCollection();
      this._destroy_with_me = [this.files];
      exports.extend(this, UploadDriver, true);
    },
    $setSize: function (x, y) {
      if (base$1.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, false, "auto");
        });
        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$S = exports.protoUI(api$S, button$1.view);

  var api$T = {
    name: "texthighlight",
    defaults: {
      template: function (obj, common) {
        var name = obj.name || obj.id;
        var id = "x" + uid();

        var width = common._get_input_width(obj);

        var html = "<div class=\"webix_text_highlight\" style=\"width:".concat(width, "px;\"><div class=\"webix_text_highlight_value\""); // iOS adds 3px of (unremovable) padding to the left and right of a textarea

        if (obj.type == "textarea" && env.mobile && env.isSafari) html += " style=\"margin-left:".concat($active.dataPadding + 3, "px; margin-right:").concat($active.dataPadding + 3, "px;\"");
        html += "></div></div>";

        if (obj.type == "textarea") {
          html += "".concat(common._baseInputHTML("textarea"), " style=\"width:").concat(width, "px;\" id=\"").concat(id, "\" name=\"").concat(name, "\" class=\"webix_inp_textarea\">").concat(common._pattern(obj.value), "</textarea>");
          return common.$renderInput(obj, html, id);
        } else return html + common.$renderInput(obj);
      },
      highlight: function (text$$1) {
        return template.escape(text$$1);
      },
      type: "text"
    },
    $init: function (config) {
      var _this = this;

      var type = config.type || this.defaults.type;
      this._viewobj.className += " webix_el_" + type;

      if (type == "textarea") {
        delete config.clear;
        config.height = config.height || 0;
        config.minHeight = config.minHeight || 60;
        this._skipSubmit = true;
      } else {
        //input hasn't scroll event
        this.scrollEv = event$1(document, "selectionchange", function () {
          if (_this.$view.contains(document.getSelection().focusNode)) _this._syncText();
        });
        this.attachEvent("onDestruct", function () {
          eventRemove(_this.scrollEv);
        });
      } //setValue


      this.attachEvent("onChange", function () {
        return _this._syncText();
      });
      this.attachEvent("onAfterRender", function () {
        _this._updatePos();

        var evs = ["scroll", "focus", "blur", "paste", "cut", "keyup", "input"];
        evs.forEach(function (ev) {
          return _event(_this.getInputNode(), ev, function () {
            return _this._syncText();
          });
        });

        _this._handleInput(true);
      });
    },
    $setSize: function () {
      text.api.$setSize.apply(this, arguments);

      this._updatePos();
    },
    $renderIcon: function (c) {
      if (c.type == "text") return text.api.$renderIcon.apply(this, arguments);
      return "";
    },
    _getLabelHeight: function (top) {
      if (this._settings.type == "textarea") return top ? this._labelTopHeight - this._settings.inputPadding : "";
      return text.api._getLabelHeight.apply(this, arguments);
    },
    getInputNode: function () {
      return this._dataobj.querySelector(this._settings.type == "text" ? "input" : "textarea");
    },
    _getHighlightNode: function () {
      return this._dataobj.querySelector(".webix_text_highlight");
    },
    _handleInput: function (init) {
      var highlight = this._getHighlightNode().firstElementChild;

      var text$$1 = this.getValue();

      if (init || text$$1 != (this._lastText || "")) {
        this._lastText = text$$1; //add &nbsp; at the end because div ignores last empty line + it gives better positioning

        highlight.innerHTML = this._settings.highlight.apply(this, [text$$1]) + "&nbsp;";
      }
    },
    _handleScroll: function () {
      var highlight = this._getHighlightNode();

      if (this._settings.type == "text") highlight = highlight.firstElementChild;
      var text$$1 = this.getInputNode();
      highlight.scrollTop = text$$1.scrollTop;
      highlight.scrollLeft = text$$1.scrollLeft;
    },
    _updatePos: function () {
      if (this._rendered_input && this.isVisible()) {
        var input = this.getInputNode();

        var highlightStyles = this._getHighlightNode().style;

        highlightStyles.left = input.offsetLeft + "px";
        highlightStyles.top = input.offsetTop + "px";
        highlightStyles.height = input.getBoundingClientRect().height + "px";
      }
    },
    _syncText: function () {
      delay(function () {
        if (this.$destructed) return;

        this._handleInput();

        this._handleScroll();
      }, this);
    }
  };
  var view$T = exports.protoUI(api$T, text.view);

  var api$U = {
    name: "timeboard",
    defaults: {
      width: 260,
      hours: true,
      seconds: false,
      twelve: /%([a,A])/.test(i18n.timeFormat)
    },
    $skin: function () {
      layout.api.$skin.call(this);
      this.defaults.margin = $active.layoutMargin.form;
      this.defaults.padding = $active.layoutPadding.space;
    },
    $init: function (config) {
      var _this = this;

      this.$view.className += " webix_timeboard";
      var rows = [];
      var twelve = !isUndefined(config.twelve) ? config.twelve : this.defaults.twelve;
      var hours = !isUndefined(config.hours) ? config.hours : this.defaults.hours;
      var seconds = config.seconds || this.defaults.seconds;

      if (isUndefined(config.height)) {
        var nrows = 1 + hours * 1 + 1 + seconds * 1 + !!config.button * 1;
        config.height = $active.inputHeight * nrows + (config.margin || this.defaults.margin) * (nrows - 1) + (config.padding || this.defaults.padding) * 2;
      }

      rows.push(this._getClock(hours, seconds, twelve));
      rows.push.apply(rows, _toConsumableArray(this._getSliders(hours, seconds, twelve)));
      if (config.button) rows.push(this._getDoneButton());
      config.rows = [{
        view: "form",
        elements: rows,
        padding: 0,
        borderless: true,
        on: {
          onChange: function (v, o, c) {
            return _this._recollectValues(c);
          }
        }
      }];
      this.$ready.push(function () {
        this._form = this.queryView("form");
        var value = this._settings.value;
        if (value) this.setValue(value, "auto");
      });
    },
    // accepts strings and Dates
    setValue: function (value, config) {
      value = this.$prepareValue(value);
      var oldvalue = this._settings.value;

      if (!wDate.equal(value, oldvalue)) {
        this._settings.value = value;
        this.$setValue(value);
        this.callEvent("onChange", [value, oldvalue, config]);
      }
    },
    $prepareValue: function (value) {
      if (typeof value === "string") value = i18n.parseTimeFormatDate(value);
      return value || wDate.datePart(new Date());
    },
    $setValue: function (value) {
      var obj = {};
      obj.minutes = obj.sminutes = value.getMinutes();
      if (this._settings.hours) obj.shours = obj.hours = value.getHours();
      if (this._settings.seconds) obj.sseconds = obj.seconds = value.getSeconds();

      if (this._settings.twelve) {
        if (!isUndefined(obj.hours)) {
          obj.day_part = obj.hours > 11 ? i18n.pm[1] : i18n.am[1];
          obj.hours = !obj.hours || obj.hours == 12 ? 12 : obj.hours % 12;
          obj.shours = obj.hours == 12 ? 0 : obj.hours;
        }
      }

      this._form.setValues(obj, false, "auto");
    },
    _recollectValues: function (config) {
      var values = this.$getValue();
      var date = this._settings.value ? wDate.copy(this._settings.value) : new Date();
      date.setHours(values.hours || 0);
      date.setMinutes(values.minutes);
      if (this._settings.seconds) date.setSeconds(values.seconds);
      this.setValue(date, config);
    },
    $getValue: function () {
      var values = this._form.getValues();

      if (this._settings.twelve && this._settings.hours) {
        if (values.day_part == i18n.pm[1] && values.hours < 12) values.hours = (values.hours * 1 + 12).toString();else if (values.day_part == i18n.am[1] && values.hours == 12) values.hours = 0;
      }

      return values;
    },
    getValue: function () {
      if (this._settings.stringResult) {
        var values = this.$getValue();
        var res = [];
        if (values.hours) res.push(values.hours);
        if (values.minutes) res.push(values.minutes);
        if (values.seconds) res.push(values.seconds);
        return res.join(":");
      } else return this._settings.value;
    },
    _getClock: function (hours, seconds, twelve) {
      var inputs = [{}, this._getText("minutes"), {}];
      var separator = {
        css: "webix_colon_template",
        template: ":",
        borderless: true,
        width: 16
      };
      if (hours) inputs.splice(1, 0, this._getText("hours", twelve), copy(separator));
      if (seconds) inputs.splice(-1, 0, copy(separator), this._getText("seconds"));

      if (twelve && hours) {
        var am = i18n.am[1];
        var pm = i18n.pm[1];
        var control = {
          view: "label",
          name: "day_part",
          css: "webix_day_part",
          template: "<div tabindex='0' role='button' class='webix_el_box' style='width:#awidth#px;height:#aheight#px;line-height:#cheight#px'>#label#</div>",
          inputWidth: 30,
          on: {
            onItemClick: function () {
              this.setValue(this.getValue() == am ? pm : am, "user");
            },
            onKeyPress: function (code, e) {
              this._onKeyPress(code, e);
            }
          }
        };
        inputs.splice(-1, 1, control);
      }

      return {
        type: "clean",
        cols: inputs
      };
    },
    _getText: function (name, twelve) {
      var _this2 = this;

      var max = name === "hours" ? twelve ? 11 : 23 : 59;
      return {
        view: "text",
        width: getTextSize("00").width + 2 * $active.dataPadding + 2 * $active.inputPadding,
        name: name,
        format: {
          parse: function (a) {
            if (a == 12 && name === "hours") a = "00";
            return a.length > 1 ? a.replace(/^0/, "") : a || 0;
          },
          edit: function (a) {
            if (a <= 0 && name === "hours" && twelve) a = 12;else if (a < 0) a = 0;else if (a > max) a = max;
            return (a + "").length === 1 ? "0" + a : a + "" || "00";
          }
        },
        on: {
          onChange: function (nv) {
            var v = (twelve && name === "hours" && (!nv || nv == 12) ? 0 : nv) * 1;

            _this2._form.elements["s" + name].setValue(v, "auto");
          }
        }
      };
    },
    _getSliders: function (hours, seconds, twelve) {
      var sliders = [this._getSlider("minutes", i18n.calendar.minutes, 59)];

      if (hours) {
        sliders.unshift(this._getSlider("hours", i18n.calendar.hours, twelve ? 11 : 23));
      }

      if (seconds) sliders.push(this._getSlider("seconds", i18n.timeboard.seconds, 59, twelve));
      return sliders;
    },
    _getSlider: function (name, title, max, twelve) {
      var _this3 = this;

      var enLocale = name === "hours" && twelve;
      var config = {
        view: "slider",
        name: "s" + name,
        title: title,
        moveTitle: false,
        min: 0,
        max: max,
        on: {
          onChange: function (nv) {
            _this3._form.elements[name].setValue((enLocale ? !nv || nv == 12 ? 12 : nv % 12 : nv) + "", "auto");
          },
          onSliderDrag: function () {
            var nv = this.getValue();
            var form = this.getFormView();
            form.blockEvent();
            form.elements[name].setValue((enLocale ? !nv || nv == 12 ? 12 : nv % 12 : nv) + "", "auto");
            form.unblockEvent();
          }
        }
      };
      return config;
    },
    _getDoneButton: function () {
      var _this4 = this;

      return {
        view: "button",
        value: i18n.calendar.done,
        css: "webix_primary",
        click: function () {
          _this4.callEvent("onTimeSelect", [_this4._settings.value]);
        }
      };
    }
  };
  var view$U = exports.protoUI(api$U, layout.view);

  var api$V = {
    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");
          } // execute only if there is a master view


          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;
    },
    _get_details: function (config) {
      return isUndefined(config) ? null : {
        config: config
      };
    },
    _set_input_value: function (text) {
      this._last_input_target.value = text;
    },
    _preselectMasterOption: function (data) {
      var text = data.id ? this.getItemText(data.id) : data.text || data.value;
      var node;

      if (this._settings.master) {
        var master = $$(this._settings.master);
        node = master.getInputNode();

        if (node) {
          // restore last text to allow 'master' view to save new value on blur
          var prev_text = master._settings.text;
          if (master.options_setter) master.$setValue(data.$empty ? "" : data.id);else if (master.$setValueHere) master.$setValueHere(text, data, this._get_details());else master.$setValue(text);
          master._settings.text = prev_text;
        }
      } else if (this._last_input_target) this._set_input_value(text);

      node = node || this._last_input_target;
      if (node) node.focus();
    },
    setMasterValue: function (data, refresh, config) {
      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, config);else if (master.setValueHere) master.setValueHere(text, data, this._get_details(config));else master.setValue(text, config);
      } else if (this._last_input_target) this._set_input_value(text);

      if (!refresh) {
        this.hide();
        if (this._last_input_target) this._last_input_target.focus();
      }

      this.callEvent("onValueSuggest", [data, text]);
    },
    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) {
        if (_typeof(id) == "object") id = id + "";
        if (list.getItem(id).$empty) return null;
      }

      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), false, "user");
        }, this));
        list.data.attachEvent("onStoreUpdated", bind(function (id, obj, mode) {
          if (mode == "delete" && id == this.getMasterValue()) this.setMasterValue({
            id: "",
            text: ""
          }, true, "auto");else if (mode == "update" && id == this.getMasterValue()) {
            this.setMasterValue(obj, true, "auto");
          }
        }, 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("onAfterDateSelect", function () {
          this.getParentView().setMasterValue({
            value: list.getSelectedDate()
          }, list.config.multiselect, "user");
        });
        list.attachEvent("onTodaySet", function (date) {
          this.getParentView().setMasterValue({
            value: date
          }, false, "user");
        });
        list.attachEvent("onDateClear", function (date) {
          this.getParentView().setMasterValue({
            value: date
          }, false, "user");
        });
      } else if (type == "colorboard") {
        list.attachEvent("onItemClick", function (value) {
          this.getParentView().setMasterValue({
            value: value
          }, false, "user");
        });
      } else if (type == "timeboard") {
        list.attachEvent("onTimeSelect", function (value) {
          this.getParentView().setMasterValue({
            value: value
          }, false, "user");
        });
      } else if (type == "colorselect") {
        list.attachEvent("onColorSelect", function (value) {
          this.getParentView().setMasterValue({
            value: value
          }, false, "user");
        });
      }
    },
    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) {
      //should be before tab and arrows handlers: IME can call keydown twice
      if (this._last_delay) this._last_delay = clearTimeout(this._last_delay);
      e = e || event;
      var list = this.getList();
      var trg = e.target;
      if (trg == document.body && !this.isVisible() || trg.className == "webix_clipbuffer") return;
      this._last_input_target = trg;
      this._settings.master = trg.webix_master_id;
      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(e, list); // escape - hide popup

      if (code == 27) return this._escape_key(e, list); // enter

      if (code == 13) return this.$enterKey(e, list); // up/down/right/left are used for navigation

      if (this._navigate(e) && this.isVisible()) {
        preventEvent(e);
        return false;
      }

      var contentEditable = trg.getAttribute("contentEditable") == "true" || trg.getAttribute("contentEditable") == "";
      if (isUndefined(trg.value) && !contentEditable) return;
      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; //spreadsheet use contentEditable div for cell highlighting

        var val = contentEditable ? trg.innerText : trg.value;
        if (this._before_filtering) this._before_filtering(); // 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();
        this._last_input_target = null;
      }
    },
    show: function (node) {
      var input = node && (node.tagName == "INPUT" || node.tagName == "TEXTAREA") ? node : null;

      if (!this.isVisible() || input && input != this._last_input_target) {
        var list = this.getList();

        if (list.filter && !this._dont_unfilter) {
          list.filter("");
        }

        if (this.$customWidth) {
          this.$customWidth(node);
        } else if (node && 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 (input) this._last_input_target = input;
      }

      popup.api.show.apply(this, arguments);
    },
    _show_selection: function () {
      var 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, "auto");
      }
    },
    $enterKey: function (e, list) {
      var visible = this.isVisible();
      var value;
      var master;
      if (this._settings.master) master = $$(this._settings.master);
      if (master && master._editable && master._settings.editable) master._applyChanges();else if (visible) {
        if (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) value = list.getSelectedDate();else if (list.getValue) value = list.getValue();
          if (value) value = {
            value: value
          };
        }

        if (value) this.setMasterValue(value, false, "user");
      }
      if (visible) this.hide();else if (this._show_on_key_press) this.show(this._last_input_target);
    },
    _escape_key: function () {
      return this.hide();
    },
    _tab_key: function () {
      return this.hide();
    },

    /*! 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(false);else {
          if (list.getSelectedDate) data = list.getSelectedDate();else if (list.getValue) data = list.getValue();
          if (data) data = {
            value: data
          };
        }
        if (data && 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$V = exports.protoUI(api$V, popup.view);
  var suggest = {
    api: api$V,
    view: view$V
  };

  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);

  var api$W = {
    name: "mentionsuggest",
    defaults: {
      symbol: "@",
      filter: function (data, value) {
        var s = this._settings.symbol;
        value = value.substring(0, this._last_cursor_pos);
        if (value.indexOf(s) === -1) return false;else {
          value = value.substring(value.lastIndexOf(s) + s.length);

          if (value.length) {
            var text = data.id ? this.getItemText(data.id) : data.text || data.value;
            return text.toString().toLowerCase().indexOf(value.toLowerCase()) !== -1;
          }

          return false;
        }
      }
    },
    $init: function () {
      this.attachEvent("onValueSuggest", this._before_filtering);
    },
    $enterKey: function (e) {
      if (this.isVisible()) {
        preventEvent(e);
        var master;
        if (this._settings.master) master = $$(this._settings.master);
        if (master && master.callEvent) master.callEvent("onEnter");
      }

      return suggest.api.$enterKey.apply(this, arguments);
    },
    _before_filtering: function () {
      if (this._last_input_target) this._last_cursor_pos = getSelectionRange(this._last_input_target).start;
    },
    _get_details: function (config) {
      return {
        pos: this._last_cursor_pos,
        symbol: this._settings.symbol,
        config: config
      };
    },
    _preselectMasterOption: function () {},
    _set_input_value: function (text) {
      var s = this._settings.symbol;
      var value = this._last_input_target.value;
      var last = value.substring(this._last_cursor_pos);
      value = value.substring(0, this._last_cursor_pos);
      value = value.substring(0, value.lastIndexOf(s) + s.length) + text;
      this._last_input_target.value = value + last;
      setSelectionRange(this._last_input_target, value.length);
    },
    _show_on_key_press: false
  };
  var view$W = exports.protoUI(api$W, suggest.view);

  exports.protoUI({
    name: "daterange",
    defaults: {
      template: "GPL version doesn't support daterange <br> You need Webix PRO"
    }
  }, template$1.view);

  var api$X = {
    name: "daterangesuggest",
    defaults: {
      type: "daterange",
      body: {
        view: "daterange",
        icons: true,
        button: true,
        borderless: true
      }
    },
    getValue: function () {
      return this.getRange().getValue();
    },
    setValue: function (value, config) {
      this.getRange().setValue(copy(value), config);
    },
    getRange: function () {
      return this.getBody();
    },
    getButton: function () {
      return this.getBody().getChildViews()[1].getChildViews()[1];
    },
    _setValue: function (value) {
      var master = $$(this._settings.master);
      if (master) master.setValue(value, "user");
    },
    _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$X = exports.protoUI(api$X, suggest.view);

  exports.protoUI({
    name: "daterangepicker",
    defaults: {
      template: "GPL version doesn't support daterangepicker <br> You need Webix PRO"
    }
  }, template$1.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$Y = {
    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$Y = exports.protoUI(api$Y, base$1.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$Z = {
    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$1.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$Z = exports.protoUI(api$Z, DataLoader, EventSystem, base$1.view);

  var google$1, script$1;
  var api$_ = {
    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$1.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$_ = exports.protoUI(api$_, DataLoader, EventSystem, base$1.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;
      if (pieRadius <= 0) return;
      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 = $active.backColor;
      ctx.beginPath();
      ctx.arc(x0, y0, innerRadius, 0, Math.PI * 2, true);
      ctx.fill();

      if (this._settings.donutInnerText) {
        var values = this._getValues(data);

        var totalValue = this._getTotalValue(values);

        var padding = $active.dataPadding;
        var width = innerRadius * 2 - padding * 2;
        var centerText = this.canvases[0].renderText(x0 - innerRadius + padding, y0 - innerRadius + padding, "<div class=\"webix_donut_center_text\">".concat(this._settings.donutInnerText(data, totalValue), "</div>"), "webix_donut_center_text_box", width);
        centerText.style.height = centerText.style.lineHeight = width + "px";
      }
    }
  };

  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 || isNaN(value)) continue;
        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]);
        /*takes start value into consideration */


        if (!yax && !(this._settings.origin != "auto" && xax)) value += startValue / unit;
        /* don't draw borders and labels for not painted values (on y-Axis or lower) */
        else 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 = "";
          }
        /*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 axisStart = y;

      if (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 = 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;
        var border = config.border;
        var label = config.label(data[i]);
        /*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);

        if (Math.floor(y1) > point1.y) {
          y1 = point1.y;
          if (label != "" && y1 - y0 < getTextSize("<div class=\"webix_canvas_text\">".concat(label, "</div>"), this.$view.className).height) label = "";
        }

        var points = this._setStakedBarPoints(ctx, x0 - (border ? 0.5 : 0), y0, barWidth + (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, border ? 1 : 0, point0.y);
          ctx.fill();
          ctx.restore();
        }
        /*drawing the gradient border of a bar*/


        if (border) {
          ctx.save();
          if (typeof border == "string") ctx.strokeStyle = 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, label);
        /*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$$1, 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");
        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) {
          if (configX.lines.call(this, data[i], i)) this._drawLine(ctx, x, y, p.x, p.y, configX.lineColor.call(this, data[i]), 1);

          this._drawRadarScaleLabel(ctx, x, y, radius, angles[i], configX.template.call(this, data[i]));
        }
      }
    },
    _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$10 = {
    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$1.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 = 80;

      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,
    donutInnerText_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: $active.backColor
      });

      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;
      var origin = this._settings.origin;

      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 && 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 (origin != "auto" && i <= origin && i + step > origin) lineX = yi - stepHeight * (origin - value) / step;
        /*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);
        if (powerStart != 0 || step >= 0.1) start = Math.ceil(nminVal * 10) / 10 * Math.pow(10, powerStart) - step;else start = absNmin;

        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;
      if (nmax == 0 && nmax == nmin) nmax = step;

      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 = 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));
      }
      var rendered = document.body.contains(this._contentobj);
      var parentNode, d; // inside window

      if (!rendered) {
        d = create("DIV", {
          "style": "visibility:hidden; position:absolute; top:0px; left:0px;"
        }, "");
        parentNode = this._contentobj.parentNode;
        document.body.appendChild(d);
        d.appendChild(this._contentobj);
      }

      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();

      if (!rendered) {
        parentNode.appendChild(this._contentobj);
        remove(d);
      }

      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();
        var r = marker.height / 2;
        ctx.arc(x + r + 5, y + height / 2, r, 0, 2 * Math.PI);
        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 x1 = x0 + marker.width;
          ctx.lineTo(x1, 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$10 = exports.protoUI(api$10, Pie$1, BarChart, LineChart, BarHChart, StackedBarChart, StackedBarHChart, Spline$1, AreaChart, Radar, Scatter, Presets, SplineArea$1, DynamicChart, Group, AutoTooltip, DataLoader, MouseEvents, EventSystem, base$1.view);

  exports.protoUI({
    name: "rangechart",
    defaults: {
      template: "GPL version doesn't support rangechart <br> You need Webix PRO"
    }
  }, template$1.view);

  var api$11 = {
    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$1.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._animation_promise) {
          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 (id, data, type$$1) {
      var _this = this,
          _arguments = arguments;

      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._animation_promise) return this._animation_promise.then(function () {
        return _this.render.apply(_this, _arguments);
      });

      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 = _to_array([].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
          // don't repaint invisible data
          if (id && type$$1 !== "delete" && this.data.getIndexById(id) === -1) return;
          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)];
            var getScrollState;
            if (this._is_level_down) this._back_scroll_states.push(this.getScrollState());else getScrollState = this._back_scroll_states.pop();
            var line = animate.formLine(next_div, this._dataobj, aniset);
            aniset.master = this;

            aniset.callback = function () {
              this._dataobj = next_div;
              /*scroll position restore*/

              if (!this._is_level_down) {
                if (getScrollState) this.scrollTo(0, getScrollState.y);
              } else this.scrollTo(0, 0);

              animate.breakLine(line);
              aniset.master = aniset.callback = null;
              this._htmlmap = this._animation_promise = null; //clear map, it will be filled at first getItemNode

              aniset.wait_animation.resolve();
              this.callEvent("onAfterRender", []);
            };

            animate(line, animArr);
            this._animation_promise = aniset.wait_animation = Deferred.defer();
          }
        }

        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$11 = exports.protoUI(api$11, Group, list.view);

  type(view$11, {});

  var api$12 = {
    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.callEvent("onUnits", []);
          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 = [];
      var min = this.data.$min || 0;
      var max = this.data.$max || Infinity;
      var count = 0;

      for (var u in this.units) {
        data.push({
          $unit: u
        });
        var unit = this.units[u];

        for (var i = 0; i < unit.length; i++) {
          if (count == min) data = [{
            $unit: u
          }];
          data.push(this.getItem(unit[i]));
          if (count == max) return _to_array(data);
          count++;
        }
      }

      return _to_array(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,
      classname: function (obj, type, marks) {
        var css = "webix_list_item";
        if (type.css) css += " webix_list_" + type.css + "_item";
        if (marks && marks.$css) css += " " + marks.$css;

        if (obj.$css) {
          if (_typeof(obj.$css) == "object") obj.$css = createCss(obj.$css);
          css += " " + obj.$css;
        }

        return css;
      },
      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 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=\"" + type.classname(obj, type, marks) + "\" style=\"" + style + "\" " + type.aria(obj, type, marks) + ">";
      },
      templateStartHeader: function (obj, type, marks) {
        var className = "webix_unit_header" + (type.css ? " webix_unit_header_" + type.css + "_item" : "");
        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 () {
      list.api.$skin.call(this);
      this.type.headerHeight = $active.unitHeaderHeight;
    }
  };
  var view$12 = exports.protoUI(api$12, 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$13 = {
    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$13 = exports.protoUI(api$13, AtomDataLoader, IdSpace, layout.view);

  var api$14 = {
    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.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$14 = exports.protoUI(api$14, TreeStateCheckbox, Group, TreeAPI, DragItem, TreeDataMove, SelectionModel, KeysNavigation, MouseEvents, Scrollable, TreeDataLoader, proto.view, TreeRenderStack, CopyPaste, EventSystem);
  var tree = {
    api: api$14,
    view: view$14
  };
  type(view$14, {
    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$15 = {
    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$15 = exports.protoUI(api$15, 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$16 = {
    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._tilesPadding = type.padding || this.type.padding;
        this._viewobj.firstChild.style.float = "left";
        this._viewobj.firstChild.style.padding = this._tilesPadding / 2 + "px";
      }

      this._contentobj.className += " webix_dataview";

      this._viewobj.setAttribute("role", "listbox");
    },
    _after_init_call: function () {
      var test = create("DIV", 0, this.type.template({}));
      test.className = "webix_dataview_item";
      test.style.position = "absolute";
      document.body.appendChild(test);
      this.type.width = test.offsetWidth + this._tilesPadding;
      this.type.height = test.offsetHeight + this._tilesPadding;
      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,
      padding: 8,
      classname: function (obj, common, marks) {
        var css = "webix_dataview_item";
        if (common.css) css += " " + common.css;
        if (common.type) css += " " + common.type;

        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\"");
      },
      templateStart: function (obj, common, marks) {
        var width = common.width,
            height = common.height;
        var padding = 0;

        if (common.type == "tiles") {
          width -= common.padding;
          height -= common.padding;
          padding = common.padding / 2;
        }

        return "<div " +
        /*@attr*/
        "webix_l_id=\"" + obj.id + "\" class=\"" + common.classname(obj, common, marks) + "\" " + common.aria(obj, common, marks) + " style=\"margin:" + padding + "px; width:" + width + "px; height:" + height + "px; float:left; overflow:hidden;\">";
      },
      templateEnd: template("</div>")
    },
    $dropHTML: function () {
      var p = this._tilesPadding;
      return "<div class=\"webix_drop_area_inner\" style=\"width:".concat(this.type.width - p, "px; height:").concat(this.type.height - p, "px; margin:").concat(p / 2, "px\"></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 && this.type.width != "auto" && !this._autowidth) this._settings.width = this.type.width * this._settings.xCount + this._tilesPadding + (this._scroll_y ? env.scrollSize : 0);
      if (this._settings.yCount && this.type.height != "auto" && !this._autoheight) this._settings.height = this.type.height * this._settings.yCount + this._tilesPadding;
      var width = this._settings.width || this._content_width;

      if (this._settings.autoheight && width) {
        this._recalk_counts();

        this._calck_autoheight(width);

        this.scroll_setter(false);
      }

      return base$1.api.$getSize.call(this, dx, dy);
    },
    _recalk_counts: function () {
      if (this._settings.yCount && (this._autoheight || this.type.height == "auto")) {
        this.type.height = Math.floor((this._content_height - this._tilesPadding) / this._settings.yCount);
        this._autoheight = this._settings.yCount;
      }

      if (this._settings.xCount && (this._autowidth || this.type.width == "auto")) {
        this.type.width = Math.floor((this._content_width - this._tilesPadding) / this._settings.xCount);
        this._autowidth = this._settings.xCount;
      }

      return this._autoheight || this._autowidth;
    },
    $setSize: function (x, y) {
      var c = this._settings;

      if (base$1.api.$setSize.call(this, x, y)) {
        if (c.autoheight && this._calck_autoheight() != this._content_height) return delay(this.resize, this);
        if (this._recalk_counts() || this._render_visible_rows) this.render();
      } else if (c.yCount && c.yCount != this._autoheight || c.xCount && c.xCount != this._autowidth) {
        if (this._recalk_counts()) this.render();
      }
    }
  };
  var view$16 = exports.protoUI(api$16, 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$17 = {
    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; //max page number

      s.limit = Math.ceil(s.count / s.size) || 1;
      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 = 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); //run animation

      line = animate.formLine(snode, onode, aniset);
      animate([snode, onode], aniset);
      this._pgInAnimation = true;
    }
  };
  var view$17 = exports.protoUI(api$17, MouseEvents, SingleRender, base$1.view, EventSystem);

  var api$18 = {
    name: "comments",
    defaults: {
      sendAction: "click",
      mode: "comments",
      highlight: true
    },
    $init: function (config) {
      this.$view.className += " webix_comments";
      this._destroy_with_me = [];
      config.rows = [this._configList(config)];
      if (!config.moreButton) config.moreButton = template(i18n.comments.moreComments);

      if (!config.readonly) {
        config.rows.push(this._configForm(config));

        this._initMenu();
      }

      this._initUsers(config.users);

      this.$ready.push(this._afterInit);
    },
    $exportView: function () {
      return this._list;
    },
    _afterInit: function () {
      var _this = this;

      // store UI blocks
      this._list = this.queryView("list");
      this._form = this.queryView("form");
      this._sendButton = this.queryView("button");
      this._input = this.queryView({
        localId: "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


      if (!this._settings.readonly) {
        this._clickHandler = attachEvent("onClick", function (e) {
          var view = $$(e);

          if (view == _this._input) {
            _this.focus();
          } else if (view !== _this._sendButton && view !== _this._listMenu && (!_this._userList || view !== _this._userList.getList()) && (!e || (e.target.className || "").toString().indexOf("webix_comments_menu") === -1)) {
            _this._changeTextarea();
          }
        });
        this.attachEvent("onDestruct", function () {
          detachEvent(this._clickHandler);
        });

        this._list.attachEvent("onAfterScroll", function () {
          _this._listMenu.hide();
        });
      }
    },
    $onLoad: function (data, driver) {
      return 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 = false; //check if datastore

      if (typeof data.serialize == "function") data = data.serialize();else {
        more = data.more;
        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 = _to_array([order[0]].concat(new Array(data.length), order.slice(1)));else {
            var start = list$$1.getIndexById("$more");
            list$$1.data.order = _to_array(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
            });
          });
          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,
              $css: "webix_comments_more_item"
            };
            if (this._settings.mode == "chat") data.unshift(more);else data.push(more);
          }

          list$$1.parse(data);

          if (this._settings.mode == "chat") {
            //wait until rendered
            list$$1.waitData.then(function () {
              return list$$1.showItem(list$$1.getLastId());
            });
          }
        }

      return true;
    },
    $skin: function () {
      layout.api.$skin.call(this);
      this._inputHeight = $active.inputHeight + 6;
    },
    getUsers: function () {
      return this._users;
    },
    getMenu: function () {
      return this._listMenu;
    },
    setCurrentUser: function (id, config) {
      this.config.currentUser = id;

      this._form.clear(config);

      this._list.refresh();
    },
    edit: function (id, config) {
      if (!this.config.readonly && this.callEvent("onBeforeEditStart", [id])) {
        this._changeTextarea(true);

        var values = this._list.getItem(id);

        this._form.setValues(values, false, config);

        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;
          values.date = new Date();
          this.add(values);

          this._list.showItem(values.id);
        }

        this._form.clear("user");

        if (clear) this._input.getInputNode().value = "";
      }
    },
    _removeComment: function (id) {
      if (this._form.getValues().id == id) {
        this._form.clear("user");
      }

      this.remove(id);
    },
    _changeTextarea: function (increase) {
      // this behaviour is only for desktop, otherwise we will never see the button on mobile
      // prevent unnecessary operations
      if (env.touch || !increase == !this._text_expanded) return;
      var text$$1 = this._input;

      if (increase) {
        this._sendButton.getParentView().show();

        text$$1.define({
          height: 84
        });
        this._text_expanded = true;
      } else {
        if (UIManager.hasFocus(this._sendButton)) {
          UIManager.setFocus(this._list);
        }

        this._sendButton.getParentView().hide();

        text$$1.define({
          height: this._inputHeight
        });
        this._text_expanded = false;
      }

      text$$1.resize();
    },
    focus: function () {
      var _this3 = this;

      this._changeTextarea(true);

      delay(function () {
        _this3._input.focus();
      });
    },
    _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 _this4 = this;

      this._listMenu = ui({
        view: "contextmenu",
        autowidth: true,
        point: false,
        data: [{
          id: "edit",
          icon: "wxi-pencil",
          value: i18n.comments["edit"]
        }, {
          id: "remove",
          icon: "wxi-trash",
          value: i18n.comments["remove"]
        }],
        on: {
          onShow: function () {
            var ctx = _this4._listMenu.getContext();

            _this4._list.addCss(ctx.id, "active_menu");
          },
          onHide: function () {
            var ctx = _this4._listMenu.getContext();

            _this4._list.removeCss(ctx.id, "active_menu");
          },
          onItemClick: function (id) {
            var ctx = _this4._listMenu.getContext();

            if (_this4.callEvent("onBeforeMenuAction", [id, ctx.id])) {
              if (id == "edit") _this4.edit(ctx.id, "user");else if (id == "remove") {
                if (i18n.comments.confirmMessage) confirm({
                  text: i18n.comments.confirmMessage,
                  callback: function (res) {
                    if (res) _this4._removeComment(ctx.id);
                  }
                });else _this4._removeComment(ctx.id);
              }
            }
          }
        }
      });

      this._destroy_with_me.push(this._listMenu);
    },
    _configForm: function (config) {
      var _this5 = this;

      var locale = i18n.comments;
      var textarea = {
        view: "textarea",
        localId: "textarea",
        css: "webix_comments_textarea",
        height: this._inputHeight,
        name: "text",
        placeholder: locale["placeholder"],
        keyPressTimeout: 100,
        on: {
          onTimedKeyPress: function () {
            _this5._toggleButton();
          },
          onChange: function (newv) {
            _this5._toggleButton(newv);
          },
          onKeyPress: function (code, ev) {
            if (code == 13) {
              var action = _this5._settings.sendAction,
                  shift = ev.shiftKey;

              if (action == "enter" && !shift || action !== "enter" && shift) {
                preventEvent(ev);

                _this5._saveComment(true);
              }
            }
          }
        }
      };

      if (config.highlight !== false) {
        exports.extend(textarea, {
          view: "texthighlight",
          type: "textarea",
          highlight: function (text$$1) {
            return _this5._highlightMention(template.escape(text$$1), true);
          }
        }, true);
      }

      return {
        view: "form",
        minHeight: 50,
        paddingX: 10,
        elements: [textarea, {
          hidden: !env.touch,
          cols: [{}, {
            view: "button",
            disabled: true,
            css: "webix_comments_send webix_primary",
            value: locale["send"],
            autowidth: true,
            click: function () {
              _this5._saveComment();
            }
          }]
        }]
      };
    },
    _highlightMention: function (text$$1, textarea) {
      var _this6 = this;

      if (text$$1.indexOf("@") === -1) return text$$1;
      var field;
      if (this._settings.highlight === "users") field = this._userList ? this._userList._settings.textValue || "value" : "value";
      var getMention = textarea ? this._markedText : this._markedHTML;
      return text$$1.replace(getMention, function (text$$1, name1, _c, name2) {
        return _this6._wrapName(text$$1, name2 || name1, field, textarea);
      });
    },
    _wrapName: function (text$$1, name, field, textarea) {
      if (field && !this._users.find(function (user) {
        return user[field] == name;
      }, true)) return text$$1;
      return "<span class=\"webix_comments_mention\">".concat(textarea ? text$$1 : "@" + name, "</span>");
    },
    _configList: function (config) {
      var _this7 = 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 = _this7.getUsers();

          var user = users && users.exists(obj.user_id) ? users.getItem(obj.user_id) : {};
          var name = "<span class = '" + css + "name'>" + (user.value || "") + "</span>";
          return name;
        },
        templateMenu: function () {
          return _this7.config.readonly ? "" : "<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$$1 = 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$$1;
        },
        templateMentioned: function (obj) {
          var text$$1 = obj.text;
          if (_this7._settings.mentions && _this7._settings.highlight) text$$1 = _this7._highlightMention(obj.text);
          return text$$1;
        },
        templateText: function (obj) {
          return "<div class = '" + css + "message'>" + obj.text + "</div>";
        },
        templateAvatar: function (obj, common) {
          var avatar = "<div class='" + css + "avatar'>";

          var users = _this7.getUsers();

          var user = users && users.exists(obj.user_id) ? users.getItem(obj.user_id) : {};
          if (user.status) avatar += common.templateStatus(user);
          avatar += "<div class='" + css + "avatar_image ";
          if (user.image) avatar += "'><img src = '" + user.image + "' class='" + css + "photo'>";else {
            var icon = user.value ? user.value[0].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'>" + _this7._settings.moreButton(obj) + "</div>";
          } else {
            obj = copy(obj);
            var avatar = common.templateAvatar(obj, common);
            var user = common.templateUser(obj, common);
            var date = common.templateDate(obj, common);
            var menu = common.templateMenu(obj, common);
            obj.text = common.templateMentioned(obj);
            obj.text = common.templateLinks(obj);

            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 == _this7._settings.currentUser || !_this7._users.count()) 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);
        }
      }; // using webix.extend() for extending schemes results in wrong calls of $init()

      if (config.scheme) Object.keys(config.scheme).forEach(function (k) {
        scheme[k] = config.scheme[k];
      });
      var listConfig = {
        view: "list",
        navigation: false,
        type: type,
        scheme: scheme,
        onClick: {
          "webix_comments_menu": function (ev, id) {
            if (_this7._listMenu.isVisible()) _this7._listMenu.hide();else {
              _this7._listMenu.setContext({
                obj: _this7,
                id: id
              });

              _this7._listMenu.show(ev.target, type.menuPosition || {
                pos: "left",
                y: 30,
                x: 10
              });
            }
          },
          "webix_comments_more": function () {
            if (_this7.config.url && _this7.callEvent("onDataRequest", [])) {
              _this7._moreCall = true;

              var more = _this7._list.getItem("$more").value;

              var pos$$1 = _this7._settings.mode == "chat" ? more : _this7._list.getIndexById("$more");
              var url = proxy$5.$parse(_this7.config.url);
              var callback = {
                error: function () {
                  _this7._moreCall = false;
                }
              };
              if (typeof url == "string") url = url + (url.indexOf("?") < 0 ? "?" : "&") + "pos=" + pos$$1 + "&more=" + more;

              _this7.load(url, callback, {
                pos: pos$$1,
                more: more
              });
            }
          }
        }
      };
      if (config.save) listConfig.save = config.save;
      return listConfig;
    },
    _initUsers: function (value) {
      var _this8 = 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 () {
        return _this8._list.refresh();
      });
    },
    _initMentions: function (value) {
      var _this9 = this;

      var readonly = this.config.readonly;
      if (!readonly) this._initUserList(value);

      if (this.config.highlight) {
        if (!readonly) this._markedText = new RegExp("@((&quot;(.*?)&quot;)|([^\\s]{1,}))", "g");
        this._markedHTML = new RegExp("@((\"(.*?)\")|([^\\s]{1,}))", "g");
      }

      Deferred.all([this._list.waitData, this._users.waitData]).then(function () {
        _this9._list.refresh();
      });

      this._list.data.attachEvent("onStoreUpdated", function (id, obj, mode) {
        if (id && (mode === "add" || mode === "update")) {
          _this9._findMentioned(obj);
        }
      });
    },
    _initUserList: function (value) {
      var config = _typeof(value) != "object" ? {} : value;
      if (_typeof(config.body) !== "object") config.body = {
        data: this._users
      };else config.body.data = this._users;
      exports.extend(config, {
        view: "mentionsuggest"
      }, true);

      var suggest = this._input.define("suggest", config);

      this._input.setValueHere = function (value, data, details) {
        if (value.indexOf(" ") != -1) value = "\"".concat(value, "\"");
        return text.api.setValueHere.apply(this, [value, data, details]);
      };

      this._userList = $$(suggest);
    },
    _findMentioned: function (obj) {
      var _this10 = this;

      if (obj.text.indexOf("@") != -1) {
        var field = this._userList ? this._userList._settings.textValue || "value" : "value";
        var mentioned = {}; // text.replace used instead of matchAll, which not supported by older browsers

        obj.text.replace(this._markedHTML, function (text$$1, name1, _b, name2) {
          var name = name2 || name1;

          var user = _this10._users.find(function (user) {
            return user[field] == name;
          }, true);

          if (user && !mentioned[name]) {
            _this10.callEvent("onUserMentioned", [user.id, obj.id]);

            mentioned[name] = true;
          } // return original text to minimize extra work


          return text$$1;
        });
      }
    }
  };
  var view$18 = exports.protoUI(api$18, AtomDataLoader, layout.view);

  var api$19 = {
    name: "timeline",
    defaults: {
      scroll: "auto"
    },
    $init: function (config) {
      var _this = this;

      var isHorizontal = config.layout === "x";

      if (isHorizontal) {
        var common = config.type;
        this.type.type = common && common.type || "top";
        if (isUndefined(config.scroll)) config.scroll = "x";
      }

      this._viewobj.className += isHorizontal ? " webix_timeline_horizontal" : " webix_timeline";
      this.$blockRender = true;
      this.data.provideApi(this, true);
      this.data.attachEvent("onStoreUpdated", function (id, data, type) {
        return _this.render(id, data, type);
      });
    },
    _id:
    /*@attr*/
    "webix_tl_id",
    on_click: {},
    $setSize: function (x, y) {
      this.$blockRender = false;

      if (base.api.$setSize.call(this, x, y)) {
        this.refresh();
      }
    },
    render: function (id, data, type) {
      if (!this.isVisible(this._settings.id) || this.$blockRender) return;

      if (type == "update") {
        var node = this.getItemNode(id); //get html element of updated item

        var t = this._htmlmap[id] = this._toHTMLObject(data);

        insertBefore(t, node);
        remove(node);
        return true;
      } else {
        //full reset
        if (this.callEvent("onBeforeRender", [this.data])) {
          this._htmlmap = null;
          this._dataobj.innerHTML = this.data.getRange().map(this._toHTML, this).join("");
          this.callEvent("onAfterRender", []);
        }
      }

      return true;
    },
    _toHTML: function (obj) {
      this.callEvent("onItemRender", [obj]);
      var index$$1 = this.getIndexById(obj.id);
      return this.type.templateStart.call(this, obj, this.type, index$$1) + this.type.template.call(this, obj, this.type, index$$1) + this.type.templateEnd.call(this);
    },
    type: {
      type: "left",
      classname: function (obj, common, index$$1) {
        var css = "webix_timeline_item";
        if (common.type !== "alternate") css += "  webix_timeline_" + common.type;else {
          if (this._settings.layout == "x") css += " webix_timeline_" + (index$$1 % 2 ? "bottom" : "top");else css += " webix_timeline_" + (index$$1 % 2 ? "right" : "left");
        }
        if (common.css) css += " " + common.css;

        if (obj.$css) {
          if (_typeof(obj.$css) == "object") obj.$css = createCss(obj.$css);
          css += " " + obj.$css;
        }

        return css;
      },
      lineColor: function (obj) {
        return obj.color;
      },
      templateValue: template("#value#"),
      templateDetails: function (obj) {
        return obj.details || "";
      },
      templateDate: function (obj) {
        var format = wDate.dateToStr("%d %M, %Y");
        return format(obj.date);
      },
      template: function (obj, common, index$$1) {
        var padding = $active.dataPadding;
        var radius = 6,
            stroke = 2;
        var lineColor = typeof common.lineColor == "string" ? common.lineColor : common.lineColor(obj, common);
        var commonStyle = "stroke-width:".concat(stroke, "px; stroke:").concat(lineColor || $active.timelineColor, ";");
        var type = common.type;
        var last = index$$1 + 1 == this.count();
        var circleCenter = radius + stroke / 2;
        var circleSize = radius * 2 + stroke;
        var innerPadding = circleSize / 2 + 7; // common properties for HTML markup, values are different for layouts

        var svgStyle = "position:relative;",
            dateStyle = "",
            eventStyle = "",
            svgWidth,
            svgHeight,
            x1,
            y1,
            x2,
            y2,
            cx,
            cy;
        var date = common.templateDate(obj, common);
        var details = common.templateDetails(obj, common);
        var eventText = "<div class=\"webix_timeline_value\">".concat(common.templateValue(obj, common), "</div>\n\t\t\t\t").concat(details ? "<div class=\"webix_timeline_details\">".concat(details, "</div>") : "");

        if (this._settings.layout == "x") {
          var height = this.$height - padding * 2;
          var center = $active.listItemHeight;
          var top = padding,
              theight = height - center - innerPadding,
              eventOffset = center + innerPadding + padding,
              eventPos = "top";

          if (type == "bottom") {
            center = height - center;
            top = center + innerPadding + padding;
            eventOffset = height - center + padding + innerPadding;
            eventPos = "bottom";
          } else if (type == "alternate") {
            center = Math.round(height * 0.5);
            theight = center - innerPadding;
            top = center - innerPadding + padding - 20, eventOffset = center + innerPadding + padding;

            if (index$$1 % 2) {
              top = eventOffset;
              eventPos = "bottom";
            }
          }

          var itemWidth = obj.$width || common.width;
          if (itemWidth == "auto") itemWidth = this._getTextSize([{
            text: eventText,
            css: "webix_timeline_event",
            height: theight
          }, {
            text: date,
            css: "webix_timeline_date"
          }]).width + padding;
          dateStyle = "top:".concat(top, "px;");
          eventStyle = "".concat(eventPos, ":").concat(eventOffset, "px; height:auto; max-height:").concat(theight, "px; width:inherit;");
          svgWidth = itemWidth;
          svgHeight = center + circleSize;
          svgStyle += "left:1px;";
          y1 = center;
          x1 = circleCenter + radius;
          y2 = center;
          x2 = "100%";
          cy = center;
          cx = circleCenter;
        } else {
          var scrollSize = this._settings.scroll ? env.scrollSize : 0;
          var width = this.$width - padding * 2 - scrollSize;

          var _center = Math.floor(width * 0.35);

          var left = padding,
              rwidth = Math.floor(width * 0.65) - innerPadding;
          var right = _center + innerPadding + padding,
              lwidth = _center - innerPadding;

          if (type == "right") {
            _center = width - right + innerPadding + padding;
            left = _center + innerPadding + padding;
            right = padding;
          } else if (type == "alternate") {
            _center = Math.floor(width * 0.5);
            right = _center + innerPadding + padding;
            lwidth = rwidth = _center - innerPadding;

            if (index$$1 % 2) {
              left = right;
              right = padding;
            }
          }

          var itemHeight = obj.$height || common.height;
          if (itemHeight == "auto") itemHeight = this._getTextSize([{
            text: eventText,
            css: "webix_timeline_event",
            width: rwidth
          }, {
            text: date,
            css: "webix_timeline_date",
            width: lwidth
          }]).height + padding;
          dateStyle = "left:".concat(left, "px; width:").concat(lwidth, "px;");
          eventStyle = "left:".concat(right, "px; width:").concat(rwidth, "px; height:").concat(itemHeight - padding, "px;");
          svgWidth = _center + circleSize;
          svgHeight = itemHeight;
          svgStyle += "top:3px;";
          x1 = _center;
          y1 = circleCenter + radius;
          x2 = _center;
          y2 = "100%";
          cx = _center;
          cy = circleCenter;
        }

        return "<div style=\"".concat(dateStyle, "\" class=\"webix_timeline_date\">").concat(date, "</div>\n\t\t\t\t\t<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"").concat(svgWidth, "px\" height=\"").concat(svgHeight, "px\" style=\"").concat(svgStyle, "\">\n\t\t\t\t\t\t").concat(!last ? "<line x1=\"".concat(x1, "\" y1=\"").concat(y1, "\" x2=\"").concat(x2, "\" y2=\"").concat(y2, "\" class=\"webix_timeline_node\" style=\"").concat(commonStyle, "\"/>") : "", "\n\t\t\t\t\t\t<circle cx=\"").concat(cx, "\" cy=\"").concat(cy, "\" r=\"").concat(radius, "\" class=\"webix_timeline_node webix_timeline_point\" style=\"").concat(commonStyle, " fill:transparent;\" />\n\t\t\t\t\t</svg>\n\t\t\t\t\t<div class=\"webix_timeline_event\" style=\"").concat(eventStyle, "\">\n\t\t\t\t\t\t").concat(eventText, "\n\t\t\t\t\t</div>");
      },
      widthSize: function (obj, common) {
        var width = obj.$width || common.width;
        return width + (width > -1 ? "px" : "");
      },
      heightSize: function (obj, common) {
        var height = obj.$height || common.height;
        return height + (height > -1 ? "px" : "");
      },
      templateStart: function (obj, common, index$$1) {
        // horizontal layout
        if (this._settings.layout == "x") {
          return "<div ".concat(
          /*@attr*/
          "webix_tl_id", "=\"", obj.id, "\" class=\"").concat(common.classname.call(this, obj, common, index$$1), "\" style=\"width:").concat(common.widthSize(obj, common), ";\">");
        }

        return "<div ".concat(
        /*@attr*/
        "webix_tl_id", "=\"", obj.id, "\" class=\"").concat(common.classname.call(this, obj, common, index$$1), "\" style=\"height:").concat(common.heightSize(obj, common), ";\">");
      },
      templateEnd: template("</div>")
    },
    _getTextSize: function (c) {
      var d = create("DIV");
      d.style.cssText = "visibility:hidden;position:absolute;top:0px;left:0px;overflow:hidden;";
      document.body.appendChild(d);
      var width = 0;
      var height = 0;

      for (var i = 0; i < c.length; i++) {
        d.className = "webix_measure_size " + c[i].css;
        d.style.width = c[i].width ? c[i].width + "px" : "auto";
        d.style.height = c[i].height ? c[i].height + "px" : "auto";
        d.innerHTML = c[i].text;
        width = Math.max(width, d.offsetWidth + 1);
        height = Math.max(height, d.offsetHeight + 1);
      }

      remove(d);
      return {
        width: width,
        height: height
      };
    },
    templateValue_setter: function (config) {
      this.type.templateValue = template(config);
    },
    templateDetails_setter: function (config) {
      this.type.templateDetails = template(config);
    },
    templateDate_setter: function (config) {
      this.type.templateDate = template(config);
    },
    $skin: function () {
      this.type.height = $active.timelineItemHeight;
      this.type.width = $active.timelineItemHeight * 3;
    }
  };
  var view$19 = exports.protoUI(api$19, Scrollable, RenderStack, DataLoader, MouseEvents, EventSystem, AutoTooltip, base.view);

  // #include ui/list.js

  var api$1a = {
    name: "menu",
    _listClassName: "webix_menu",
    $init: function (config) {
      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();

          if (!parent.callEvent("onMenuItemClick", [id, e, trg])) {
            e.showpopup = parent._settings.id;
            return;
          }

          if (this != parent) parent._call_onclick(id, e, trg); //click on group - do not close submenus

          if (!this.type._submenu(item) && !parent._show_child_on_click) {
            parent._hide_sub_menu(true);

            if (parent._hide_on_item_click) parent.hide();
          } else {
            if (env.touch || this === parent && parent._settings.openAction == "click") {
              this._mouse_move_activation(id, trg);
            } //do not close popups when clicking on menu folder


            e.showpopup = parent._settings.id;
          }
        }
      });
      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\"" : "") + (obj.disabled ? " aria-disabled=\"true\"  webix_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 () {
      if (env.touch) return false;
      var top = this.getTopMenu();

      if (top._settings.openAction == "click") {
        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.isItemEnabled(id)) return;
        sub.show(target, {
          pos: this._settings.subMenuPos
        });
        sub._parent_menu = this._settings.id;
        this._open_sub_menu = data.submenu;
      }
    },
    disableItem: function (id) {
      var menu = this.getMenu(id);
      if (menu) menu._set_item_disabled(id, true);
    },
    enableItem: function (id) {
      var menu = this.getMenu(id);
      if (menu) menu._set_item_disabled(id, false);
    },
    isItemEnabled: function (id) {
      var menu = this.getMenu(id);
      if (menu) return list.api.isItemEnabled.apply(menu, arguments);
    },
    _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, dir) {
      var item = this.getItem(id);

      if (item.$template == "Separator" || item.$template == "Spacer" || !this.isItemEnabled(id)) {
        id = this.getNextId(id, dir) || null;
        return id && id != prev ? this._skip_item(id, prev, dir) : prev;
      }

      return id;
    },
    $skin: function () {
      list.api.$skin.call(this);
      this.type.height = $active.menuHeight;
    },
    defaults: {
      scroll: "",
      layout: "x",
      mouseEventDelay: 100,
      subMenuPos: "bottom"
    }
  };
  var view$1a = exports.protoUI(api$1a, list.view);
  var menu = {
    api: api$1a,
    view: view$1a
  };

  var api$1b = {
    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$1b = exports.protoUI(api$1b, menu.view, popup.view);
  var submenu = {
    api: api$1b,
    view: view$1b
  };

  var api$1c = {
    name: "sidemenu",
    defaults: {
      animate: true,
      position: "left",
      width: 200,
      borderless: true
    },
    $init: function () {
      this.$view.className += " webix_sidemenu";
    },
    $skin: function () {
      popup.api.$skin.call(this);
      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$1.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 = zIndex(this._settings.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);
      }
    },
    _state: {},
    _isAnimationSupported: function () {
      return animate.isSupported() && this._settings.animate && !(env.isIE && navigator.appVersion.indexOf("MSIE 9") != -1);
    },
    hidden_setter: function (value) {
      if (value) this.hide();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";
        }
      }
    },
    _hiding_process: function () {
      var maxWidth = document.documentElement.offsetWidth;
      var maxHeight = document.documentElement.offsetHeight;

      if (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();
    }
  };
  var view$1c = exports.protoUI(api$1c, popup.view);

  var api$1d = {
    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 (config) {
      this._fullWidth = isUndefined(config.width) ? this.defaults.width : config.width;
      this._settings.width = config.width = config.collapsed ? config.collapsedWidth || this.defaults.collapsedWidth : this._fullWidth;
      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]);
      };

      config.multiselect = false;
    },
    on_context: {},
    on_mouse_move: {},
    _initSidebar: function () {
      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) {
        var popup = this.getPopup();
        if (popup && !popup.config.hidden) ev.showpopup = popup.config.id;
        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("onAfterUnSelect", function () {
        this.clearCss("webix_sidebar_selected");
      });
      this.attachEvent("onMouseMove", function (id, ev, node) {
        this._showPopup(id, node);
      });
      this.attachEvent("onMouseOut", function () {
        if (this.config.collapsed) this.getPopup().masterId = null;
      });
    },
    _showPopup: function (id, node) {
      if (this.config.collapsed) {
        var popup = this.getPopup();

        if (popup) {
          this._updateTitle(id, popup);

          this._updateList(id, popup);

          popup.masterId = id;
          popup.show(node, {
            x: this.config.position == "left" ? this.config.collapsedWidth : -popup.config.width,
            y: -1
          });
        }
      }
    },
    _updateTitle: function (id, popup) {
      var title = popup.getBody().getChildViews()[0];
      if (!title || popup.masterId == id) return;
      title.parse(this.getItem(id));
      var selectedId = this.getSelectedId();

      if (selectedId == id) {
        addCss(title.$view, "webix_selected", true);
      } else {
        removeCss(title.$view, "webix_selected");
      }
    },
    _updateList: function (id, popup) {
      var list = popup.getBody().getChildViews()[1];
      if (!list || popup.masterId == id) return;
      if (this.exists(popup.masterId) && this.getItem(popup.masterId).menu) this.updateItem(popup.masterId, {
        menu: list.data.serialize()
      });
      list.clearCss("webix_sidebar_selected");
      list.unselectAll();
      var data = copy(this.getItem(id).menu || []);

      if (data.length) {
        list.show();
        list.data.importData(data);
        var selectedId = this.getSelectedId();
        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 css = config.css ? " " + config.css : "";
        var popupConfig = {
          view: "popup",
          css: "webix_sidebar_popup " + dirClassName + 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.getValues().id;
                  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 + css,
              template: menuTemplate,
              type: {
                subsign: false
              },
              submenuConfig: {
                padding: 0,
                subMenuPos: subMenuPos,
                template: menuTemplate,
                select: true,
                type: {
                  subsign: false
                },
                css: "webix_sidebar_popup_list " + dirClassName + 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.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;

        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;
    },
    select: function (id) {
      //ignore multiple selection
      if (id) {
        if (isArray(id)) id = id.pop();
        tree.api.select.call(this, id);
      }
    },
    selectAll: function () {},
    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");
      }

      if (!isUndefined(width) && width !== this.config.width) {
        //skip first rendering
        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$1d = exports.protoUI(api$1d, tree.view);

  var api$1e = {
    name: "context"
  };
  var view$1e = exports.protoUI(api$1e, ContextHelper, popup.view);

  var api$1f = {
    name: "contextmenu",
    _hide_on_item_click: true,
    $init: function (config) {
      if (config.submenuConfig) exports.extend(config, config.submenuConfig);
    }
  };
  var view$1f = exports.protoUI(api$1f, ContextHelper, submenu.view);

  var api$1g = {
    name: "vscroll",
    $apiOnly: true,
    defaults: {
      scroll: "x",
      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;
    },
    $skin: function () {
      this.defaults.scrollStep = $active.rowHeight;
    },
    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);
    },
    _fixSize: function () {
      var pos$$1 = this.getScroll();
      var max = Math.max(this.getSize() - this._last_set_size, 0);
      if (pos$$1 > max) this.scrollTo(max);
    },
    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, "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 (e.ctrlKey) return false;

      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;
      if (dir) if (this.scrollTo(this.getScroll() + dir * this._settings.scrollStep)) return preventEvent(e);
    }
  };
  var view$1g = exports.protoUI(api$1g, 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 false;
        var ids = itemValue.toString().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]._get_filter) return filter[2]._get_filter(filter[0]);
      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 lineId = obj ? obj[id] : ""; //special handling for 0 values

        if (lineId === 0) lineId = "0"; //convert to string to create a valid ID

        if (_typeof(lineId) == "object") lineId = String(lineId);

        if (lineId !== undefined && !checks[lineId]) {
          checks[lineId] = true;
          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) {
      var _this = this;

      if (value && !this._hover_initialized) {
        this._enable_mouse_move();

        this.config.experimental = true;
        this.attachEvent("onMouseMoving", function (e) {
          var pos = _this.locate(e); // when we click inner html element, edge calls mousemove with incorrect e.target


          if (!pos && env.isEdge && e.relatedTarget) pos = _this.locate(e.relatedTarget);
          var row = pos ? pos.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(this.$view, "mouseout", function (e) {
          var trg = e.target; // inner html elements blinking in case of hover:true
          // ie and edge call mouseout when clicking inner html element

          if (_this._last_hover && document.body.contains(trg)) {
            var pos = e.relatedTarget ? _this.locate(e.relatedTarget) : null;

            if (!pos) {
              _this.removeRowCss(_this._last_hover, _this._settings.hover);

              _this._last_hover = null;
            }
          }
        });

        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", 0, true);
          }

          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, true);
          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", 0, true);
          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) {
        if (env.touch) this.attachEvent("onLongTouch", this._bs_down);else _event(this._viewobj, env.mouse.down, this._bs_down, {
          bind: this
        });

        _event(this._viewobj, env.mouse.move, this._bs_move, {
          bind: this
        });

        this._block_sel_flag = this._bs_ready = this._bs_progress = false;
        this.attachEvent("onAfterScroll", 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;

      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)) 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 = env.touch ? e : pos(e);
        this._bs_ready = [pos$$1.x - this._bs_position.x, pos$$1.y - this._bs_position.y];
        preventEvent(e);

        this._bs_up_init();
      }
    },
    _bs_up_init: function () {
      var _this = this;

      var handler = event$1(document.body, env.mouse.up, function (e) {
        eventRemove(handler);
        return _this._bs_up(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) delay(this._bs_select, this, [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 && (!env.touch || this._settings.prerender)) 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._rs_progress) return;

      if (this._bs_ready !== false) {
        if (!this._bs_progress) addCss(document.body, "webix_noselect");
        var pos$$1 = env.touch ? env.mouse.context(e) : 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);

        if (env.touch) preventEvent(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) 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) {
      var _this = this;

      if (value && this._rs_init_flag) {
        _event(this._viewobj, "mousemove", function (e) {
          return _this._rs_move(e);
        });

        _event(this._viewobj, "mousedown", function (e) {
          return _this._rs_down(e);
        });

        _event(this._viewobj, "mouseup", function () {
          return _this._rs_up();
        });

        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._rs_ready || this._settings.subview && this != $$(e.target)) return;
      this._rs_process = [pos(e), this._rs_ready];
      addCss(document.body, "webix_noselect");
      denySelect();
    },
    _rs_up: function () {
      this._rs_process = false;
      removeCss(document.body, "webix_noselect");
      allowSelect();
    },
    _rs_start: function () {
      var _this2 = this;

      if (this._rs_progress) return;

      var _this$_rs_process$ = _slicedToArray(this._rs_process[1], 4),
          dir = _this$_rs_process$[0],
          size = _this$_rs_process$[1],
          node = _this$_rs_process$[2],
          cell = _this$_rs_process$[3];

      var eventPos = this._rs_process[0];
      var start;

      if (dir == "x") {
        start = offset(node).x + size - offset(this._body).x;
        eventPos = eventPos.x;
      } else {
        start = offset(node).y + size - offset(this._body).y + this._header_height;
        eventPos = eventPos.y;
      }

      this._rs_progress = [dir, cell, start];
      var resize = new ui.resizearea({
        container: this._viewobj,
        eventPos: eventPos,
        start: start,
        dir: dir,
        cursor: (dir == "x" ? "col" : "row") + "-resize"
      });
      resize.attachEvent("onResizeEnd", function (pos$$1) {
        return _this2._rs_end(pos$$1);
      });
      this._rs_ready = false;
    },
    _rs_end: function (result) {
      if (this._rs_progress) {
        var cell = this._rs_progress[1];
        var newsize = result - this._rs_progress[2];

        if (this._rs_progress[0] == "x") {
          //in case of right split - different sizing logic applied
          if (this._settings.rightSplit && cell.cind >= this._rightSplit) {
            newsize *= -1;
          }

          var col = this._columns[cell.cind];
          var oldwidth = col.width;
          delete col.fillspace;
          delete col.adjust;

          this._setColumnWidth(cell.cind, oldwidth + newsize, true, true);

          this._updateColsSizeSettings();
        } else {
          var id = this.getIdByIndex(cell.rind);

          var oldheight = this._getRowHeight(this.getItem(id));

          this.setRowHeight(id, oldheight + newsize);
        }

        this._rs_up();
      }

      this._rs_progress = null;
    },
    _rs_move: function (e) {
      if (this._rs_ready && this._rs_process) return this._rs_start();
      this._rs_ready = false;
      var mode = false;
      var node = e.target;
      var config = this._settings; //we can't use node.className because there can be SVG (in SVG it is an SVGAnimatedString object)

      var element_class = node.getAttribute("class") || ""; //ignore resize in case of drag-n-drop enabled

      var in_body = element_class.indexOf("webix_cell") != -1;
      if (in_body && config.drag) return this._mark_resize(mode);
      var in_header = element_class.indexOf("webix_hcell") != -1;

      if (in_body || in_header) {
        var _pos = posRelative(e);

        var cell = this._locate(node);

        mode = this._is_column_rs(cell, _pos, node, config.resizeColumn, in_body) || in_body && this._is_row_rs(cell, _pos, node, config.resizeRow);
      } //mark or unmark resizing ready state


      this._mark_resize(mode);
    },
    _is_column_rs: function (cell, pos$$1, node, rColumn, in_body) {
      // if resize is only within the header
      if (!rColumn || in_body && rColumn.headerOnly) return false;
      var dx = node.offsetWidth;
      rColumn = rColumn.size ? rColumn.size : 3;
      var col, config;

      if (pos$$1.x < rColumn) {
        if (cell.cind < this._rightSplit) cell.cind -= cell.span || 1;
        col = this._columns[cell.cind];
        config = ["x", 0, node, cell];
      } else if (dx - pos$$1.x < rColumn + 1) {
        if (this._settings.rightSplit && cell.cind + 1 >= this._rightSplit) cell.cind++;

        if (!this._settings.rightSplit || cell.cind < this._columns.length) {
          col = this._columns[cell.cind];
          config = ["x", dx, node, cell];
        }
      }

      if (col && col.resize !== false) {
        this._rs_ready = config;
        return "col-resize";
      }

      return false;
    },
    _is_row_rs: function (cell, pos$$1, node, rRow) {
      // if resize is only within the first column
      if (!rRow || rRow.headerOnly && cell.cind > 0) return false; // block selection in progress

      if (this._bs_progress) return false;
      var dy = node.offsetHeight;
      rRow = rRow.size ? rRow.size : 3;

      if (pos$$1.y < rRow) {
        if (cell.rind > 0) {
          // avoid resize header border
          cell.rind--;
          this._rs_ready = ["y", 0, node, cell];
        }
      } else if (dy - pos$$1.y < rRow + 1) this._rs_ready = ["y", dy, node, cell];

      return this._rs_ready ? "row-resize" : false;
    },
    _mark_resize: function (mode) {
      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 || "";
      }
    }
  };

  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");
    },
    $hasYScroll: function () {
      return this._dtable_height - this._dtable_offset_height > 2;
    },
    _touchNative: {
      _scrollTo_touch: function (x, y) {
        Touch._set_matrix(this._body.childNodes[1].firstChild, -x, -y, "0ms");

        this._sync_scroll(-x, -y, "0ms");

        this.callEvent("onAfterScroll", [{
          e: -x,
          f: -y
        }]);
      },
      _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;
        });
        this.attachEvent("onAfterScroll", function (result) {
          //onAfterScroll may be triggered by some non-touch related logic
          if (!result) return;
          this._scrollLeft = -result.e;
          this._scrollTop = -result.f;
          if (this._x_scroll) this._x_scroll._settings.scrollPos = this._scrollLeft;
          if (this._y_scroll) this._y_scroll._settings.scrollPos = this._scrollTop;
        });
      },
      _sync_scroll: function (x, y, t) {
        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 () {}
    },
    _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("onAfterRender", function () {
          if (this._x_scroll && this._settings.scrollX) this._x_scroll._fixSize();
          if (this._y_scroll && this._settings.scrollY) this._y_scroll._fixSize();
        });
        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, 0);

          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._settings.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.minWidth || this._settings.minColumnWidth; //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
      }

      max = max + (env.isIE ? $active.layoutPadding.space : 0);
      return Math.min(max, config.maxWidth || this._settings.maxColumnWidth || 100000);
    },
    _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) {
      if (id) this._adjustRowHeight(id);else {
        var heights = {};
        var cols = this._settings.columns;

        for (var i = 0; i < cols.length; i++) {
          this._adjustRowHeight(cols[i].id, heights);
        } //adjust size for single columns


        this.data.each(function (obj) {
          obj.$height = heights[obj.id];
        });
      }
      if (!silent) this.refresh();
    },
    _adjustRowHeight: function (id, size) {
      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) {
        var height;
        d.innerHTML = this._getValue(obj, config, 0);
        height = Math.max(d.scrollHeight, this._settings.rowHeight, this._settings.minRowHeight || 0);
        height = Math.min(height, this._settings.maxRowHeight || 100000);
        if (size) size[obj.id] = Math.max(height, size[obj.id] || 0);else obj.$height = height;
      }, this);
      d = remove(d);
      if (container) remove(container);
    }
  };

  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._math_recalc = {};

          this._parse_cell_math(id, this._columns[_i].id);
        }

        this._math_recalc = {};
      }
    },
    _parse_cell_math: function (row, col) {
      var item = this.getItem(row);
      var value; // use previosly saved formula if available

      value = item[this._math_pref + col] || item[col];

      if (value && value.length > 0 && value.toString().substr(0, 1) === "=") {
        // calculate math value
        item[this._math_pref + col] = value;
        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]);
          }
        }
      }
    },
    _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._math_recalc = {};

          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) {
      var hidden = this._hidden_column_order;

      if (all && hidden.length) {
        for (var i = 0; i < hidden.length; i++) {
          var column = this.getColumnConfig(hidden[i]);
          handler.call(this, column.id, column);
        }
      } else {
        var columns = [].concat(this._columns);

        for (var _i = 0; _i < columns.length; _i++) {
          var _column = columns[_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 = _to_array();
      this._hidden_split = [0, 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 cindex = this.getColumnIndex(id);
      if (cindex == index || cindex == -1) return false; //already in place

      var horder = this._hidden_column_order;
      var columns = this._settings.columns; // remove from the previous place

      var hindex;
      var col = columns.splice(cindex, 1)[0];

      if (horder.length) {
        hindex = horder.find(id);
        horder.removeAt(hindex);
      }

      this._updateSplit(cindex, hindex, -1); // paste into new


      var nindex = index - (index > cindex ? 1 : 0);

      _power_array.insertAt.call(columns, col, nindex);

      var pos;

      if (horder.length) {
        var prev = columns[nindex - 1];
        pos = prev && prev.id ? horder.find(prev.id) + 1 : 0;
        horder.insertAt(col.id, pos);
      }

      this._updateSplit(nindex, pos, 1);

      this._refresh_columns();
    },
    _init_horder: function (horder, cols) {
      if (!horder.length) {
        for (var i = 0; i < cols.length; i++) {
          horder[i] = cols[i].id;
        }

        this._hidden_split = [this._settings.leftSplit, this._rightSplit, this._settings.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,
          span = 1;
      opts = opts || {};

      if (mode !== false) {
        var index = this.getColumnIndex(id);
        assert(index != -1, "hideColumn: invalid ID or already hidden"); //in case of second call to hide the same column, command will be ignored

        if (index == -1 || !this.callEvent("onBeforeColumnHide", [id])) return;

        this._init_horder(horder, cols);

        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);
            }
          }
        }

        this._fixSplit(index, span, -1);

        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;
          column.hidden = true;
          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 hindex = 0;

        for (var _i3 = 0; _i3 < horder.length; _i3++) {
          if (horder[_i3] == id) {
            hindex = _i3;
            break;
          }

          if (!hhash[horder[_i3]]) prev = horder[_i3];
        }

        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);
            }
          }
        }

        var _index = prev ? this.getColumnIndex(prev) + 1 : 0;

        for (var _i5 = hindex + span - 1; _i5 >= hindex; _i5--) {
          var col = hhash[horder[_i5]];

          if (col) {
            //can be already shown by another action
            _power_array.insertAt.call(cols, col, _index);

            delete col.hidden;
            delete hhash[col.id];
            this._columns_pull[col.id] = col;
          } else span--;
        }

        this._fixSplit(hindex, span, 1, true);

        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 (horder.length === cols.length) this._clear_hidden_state();
      if (!silent) this._refresh_columns();
    },
    showColumn: function (id, opts, silent) {
      return this.hideColumn(id, opts, silent, false);
    },
    _fixSplit: function (index, span, op, hidden) {
      var _ref = hidden ? this._hidden_split : [this._settings.leftSplit, this._rightSplit],
          _ref2 = _slicedToArray(_ref, 2),
          lSplit = _ref2[0],
          rSplit = _ref2[1];

      if (index < lSplit) this._settings.leftSplit += op * span;
      if (index >= rSplit) this._settings.rightSplit += op * span;else this._rightSplit += op * span;
    },
    _updateSplit: function (index, hindex, op) {
      if (index >= 0) {
        if (index < this._settings.leftSplit) this._settings.leftSplit += op;
        if (this._settings.rightSplit && index >= this._rightSplit) this._settings.rightSplit += op;else this._rightSplit += op;
      }

      var horder = this._hidden_column_order;

      if (horder.length && hindex >= 0) {
        if (hindex < this._hidden_split[0]) this._hidden_split[0] += op;
        if (this._hidden_split[2] && hindex >= this._hidden_split[1]) this._hidden_split[2] += op;else this._hidden_split[1] += op;
      }
    },
    _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) {
      this._dtable_column_refresh = true;

      if (columns) {
        this._clear_hidden_state();

        this._filter_elements = {};
      }

      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.config.rightSplit || 0);
      this._dtable_fully_ready = 0;

      this._define_structure();

      this._update_scroll();

      this.callEvent("onStructureUpdate");
      this.render();
      this._dtable_column_refresh = false;
    },
    _refresh_columns: function () {
      this._dtable_fully_ready = 0;
      this.callEvent("onStructureUpdate");

      this._apply_headers();

      this.render();
    },
    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 (config) {
      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");
      }); //open selected node in tree-like tables

      if (config.select != "column" && (this.open || this._subViewStorage)) this.attachEvent("onEnter", function () {
        var sel = this.getSelectedId(true);

        if (sel.length == 1 && !(this.getEditor && this.getEditor())) {
          var node = this.getItemNode(sel[0]);
          sel = sel[0].row;

          if (this.config.select == "row" || node.querySelector(".webix_tree_open") || node.querySelector(".webix_tree_close")) {
            if (this.isBranchOpen(sel)) this.close(sel);else this.open(sel);
          }
        }
      });
    },
    _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 (sel[i].column && this.isColumnVisible(sel[i].column)) return this.getItemNode(sel[i]);
      }

      return null;
    },
    moveSelection: function (mode, details, focus) {
      this._delayRender();

      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 = {
    _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;
    },
    _add_css: function (source, css, mark) {
      var context = DragControl._drag_context;
      if (!this._settings.prerender && !mark) 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, css);
        }

        this.data.addMark(source[i], css, 1, 1, true);
      }
    },
    _remove_css: function (source, css, mark) {
      var context = DragControl._drag_context;
      if (!this._settings.prerender && !mark) 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, css);
        }

        this.data.removeMark(source[i], css, 1, true);
      }
    },
    _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 groupNode = group.childNodes[hind];
        var row =
        /*@attr*/
        "column";
        var column =
        /*@attr*/
        "row";
        return groupNode.querySelector("div[".concat(row, "=\"").concat(ind, "\"][").concat(column, "=\"").concat(row_index, "\"]"));
      }

      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.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;
            this._auto_scroll_force = true;
            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 _this = this;

            var context = DragControl.getContext();
            var box = offset(this.$view);
            node.style.display = "none";
            var html = document.elementFromPoint(pos$$1.x, box.y + this._settings.headerRowHeight / 2);
            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;
            if (this._auto_scroll_delay) this._auto_scroll_delay = window.clearTimeout(this._auto_scroll_delay);
            if (this._settings.dragscroll !== false) this._auto_scroll_delay = delay(function (pos$$1) {
              return _this._auto_scroll_column(pos$$1);
            }, this, [pos(e)], 250);
          }, this),
          $dragDestroy: bind(function (a, node) {
            this._auto_scroll_force = null;
            if (this._auto_scroll_delay) this._auto_scroll_delay = window.clearTimeout(this._auto_scroll_delay);
            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;
              }
            }

            this._auto_scroll_force = true;
            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 _this2 = this;

            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;

            while ((target.className || "").indexOf("webix_hcell") == -1) {
              target = target.parentNode;
              if (!target) return;
            }

            if (this._auto_scroll_delay) this._auto_scroll_delay = window.clearTimeout(this._auto_scroll_delay);
            if (this._settings.dragscroll !== false) this._auto_scroll_delay = delay(function (pos$$1) {
              return _this2._auto_scroll_column(pos$$1);
            }, this, [pos(e)], 250);

            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) {
            this._auto_scroll_force = null;
            if (this._auto_scroll_delay) this._auto_scroll_delay = window.clearTimeout(this._auto_scroll_delay);
            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);
        this.attachEvent("onDestruct", function () {
          return DragControl.unlink(control);
        });
        if (!this._auto_scroll) exports.extend(this, AutoScroll, true);
      }

      return value;
    }
  };

  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 = options.fit == "page" ? Infinity : this._getPageWidth(options);
      var rightRestriction = 0;
      var bottomRestriction = 0;
      var tableArray = [];
      var newTableStart = 0;
      var widths = [];
      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 (rowIndex === 0) widths.push(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 || cellValue === 0) {
                  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");
        if (options.fit == "page") this._correctWidth(base, widths, rightRestriction, options);
      }
      return base;
    },
    //a single grid tries to fit to page size - set column width to auto
    _correctWidth: function (base, widths, rightRestriction, options) {
      if (rightRestriction && options.trim) widths.length = rightRestriction;
      var rwidth = 0;

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

      if (rwidth > this._getPageWidth(options)) {
        base[0].forEach(function (item) {
          for (var _i = 0; _i < item.length; _i++) {
            if (item[_i] && item[_i].style && item[_i].style.width) item[_i].style.width = "auto";
          }
        });
      }
    },
    _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
        });
      var mode = options.export_mode;
      if (mode != "pdf" && 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 = mode == "pdf" ? toPDF(this, options) : toExcel(this, options);
        data[0].styles = this._getExportStyles(options);
        delete options.dataOnly;
        return data;
      }
    },
    _getExportStyles: function (options) {
      var type = options.export_mode;
      var columns = this.config.columns,
          styles = [];
      this._style_hash = this._style_hash || {};
      if (options.docHeader && type == "excel") styles = [{
        0: this._getExportDocStyle(options.docHeader.css)
      }, {
        0: {}
      }];
      if (options.header !== false) styles = this._getExportHStyles(options, "header", styles, type);
      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 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].join(":"), type);
        }

        styles[styles.length] = row;
      }, this);
      if (options.footer !== false && this.config.footer) styles = this._getExportHStyles(options, "footer", styles, type);
      if (options.docFooter && type == "excel") styles = styles.concat([{
        0: {}
      }, {
        0: this._getExportDocStyle(options.docFooter.css)
      }]);
      return styles;
    },
    _getExportHStyles: function (options, group, styles, type) {
      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.className, header.css || "", group];
              hrow[i] = this._getExportCellStyle(node, name.join(":"), type);
              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;
    },
    _getBorderColor: function (styles, defaultColor, type) {
      return styles["border-".concat(type, "-width")] == "0px" ? null : color.rgbToHex(styles["border-".concat(type, "-color")]) || defaultColor;
    },
    _getExportCellStyle: function (node, name, type) {
      if (this._style_hash[name]) return this._style_hash[name];else {
        var cellStyle = this._getRules(node);

        var bg = color.rgbToHex(cellStyle["background-color"]) || "FFFFFF";
        var common = {
          backgroundColor: bg,
          fontSize: cellStyle["font-size"].replace("px", "") * 0.75,
          //px to pt conversion
          color: color.rgbToHex(cellStyle["color"]),
          textAlign: cellStyle["text-align"],
          borderRightColor: this._getBorderColor(cellStyle, bg, "right"),
          borderLeftColor: this._getBorderColor(cellStyle, bg, "left"),
          borderBottomColor: this._getBorderColor(cellStyle, bg, "bottom"),
          borderTopColor: this._getBorderColor(cellStyle, bg, "top")
        };
        var rules = type == "pdf" ? common : this._getExcelCellRules(cellStyle, node, common);
        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;
    },
    _getExcelCellRules: function (cellStyle, node, common) {
      var rules = {
        font: {},
        alignment: {},
        border: {}
      }; //font

      rules.font.name = cellStyle["font-family"].replace(/,.*$/, ""); // cut off fallback font;

      rules.font.sz = common.fontSize;
      rules.font.color = {
        rgb: common.color
      };
      if (cellStyle["font-weight"] !== "normal" && cellStyle["font-weight"] != 400) rules.font.bold = true;
      if (cellStyle["text-decoration-line"] === "underline") rules.font.underline = true;
      if (cellStyle["font-style"] === "italic") rules.font.italic = true;
      if (cellStyle["text-decoration-line"] === "line-through") rules.font.strike = true; //alignment

      rules.alignment.horizontal = common.textAlign;
      rules.alignment.vertical = cellStyle["height"] == cellStyle["line-height"] ? "center" : "top";
      if (cellStyle["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

      rules.fill = {
        fgColor: {
          rgb: common.backgroundColor
        }
      }; //borders

      if (common.borderRightColor) rules.border.right = {
        style: "thin",
        color: {
          rgb: common.borderRightColor
        }
      };
      if (common.borderBottomColor) rules.border.bottom = {
        style: "thin",
        color: {
          rgb: common.borderBottomColor
        }
      };
      if (common.borderLeftColor) rules.border.left = {
        style: "thin",
        color: {
          rgb: common.borderLeftColor
        }
      };
      if (common.borderTopColor) rules.border.top = {
        style: "thin",
        color: {
          rgb: common.borderTopColor
        }
      };
      return rules;
    },
    _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$1h = {
    name: "datatable",
    defaults: {
      leftSplit: 0,
      rightSplit: 0,
      topSplit: 0,
      columnWidth: 100,
      sort: true,
      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.minRowHeight = $active.rowHeight - 6;
      this.defaults.headerRowHeight = $active.barHeight - $active.borderWidth * 2;
      this.defaults.minColumnWidth = $active.dataPadding * 2 + $active.borderWidth;
    },
    on_click: {
      webix_excel_filter: function () {
        return false;
      },
      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; //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' section='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' section='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._headers = [];
      this._footers = [];
      this._rows_cache = [];
      this._active_headers = {};
      this._filter_elements = {};
      this._sort_signs = {};
      this._sort_signs_order = [];
      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;
      var hheight = this._settings.headerRowHeight + 1;
      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:" + hheight + "px; line-height:" + hheight + "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();
    },
    _delayRender: function () {
      var _this = this;

      clearTimeout(this._renderDelay);
      this._renderDelay = delay(function () {
        _this._renderDelay = 0;

        if (!isUndefined(_this._delayedLeftScroll)) {
          _this._setLeftScroll(_this._delayedLeftScroll);

          delete _this._delayedLeftScroll;
        }

        _this.render();
      });
    },
    render: function (id, data, mode) {
      //pure data saving call
      if (mode == "save" || this._renderDelay) 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 (!id || mode != "update") {
          this._dtable_height = this._get_total_height();

          this._set_split_sizes_y();
        }

        if (this._content_width) {
          if (fast_mode && (mode == "paint" || mode == "update") && id) this._repaint_single_row(id);else this._check_rendered_cols(true, true);
        } //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;
      }
    },
    getColumns: function (all) {
      var horder = this._hidden_column_order;

      if (all && horder.length) {
        var columns = [];

        for (var i = 0; i < horder.length; i++) {
          columns.push(this.getColumnConfig(horder[i]));
        }

        return columns;
      } else return _toConsumableArray(this._columns);
    },
    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) {
      this._create_scheme_init();

      if (config.columns && this._dtable_fully_ready) this.refreshColumns(config.columns);
    },
    _define_structure: function () {
      if (this._settings.columns) {
        this._columns_pull = {};
        this._columns = this._settings.columns;
        this._rightSplit = this._columns.length - (this.config.rightSplit || 0);

        for (var i = 0; i < this._columns.length; i++) {
          this._prepare_single_column(this._columns[i]);
        }

        this._normalize_headers("header", this._headers);

        this._normalize_headers("footer", this._footers);

        this.callEvent("onStructureLoad", []);
      }
    },
    _prepare_single_column: function (col) {
      this._columns_pull[col.id] = col;
      if (isUndefined(col.header)) col.header = col.id;
      var format = col.cssFormat;
      if (format) col.cssFormat = toFunctor(format, this.$scope);
      col.width = this._correctColumnWidth(col.width || this._settings.columnWidth, col);
      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);
      }
    },
    _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.config.rightSplit || 0);
      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();

      for (var i = 0; i < this._sort_signs_order.length; i++) {
        var col_id = this._sort_signs_order[i];

        this._render_single_mark(col_id, this._sort_signs[col_id]);
      }
    },
    _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; // clear array of previous values

      heights.length = 0;

      for (var i = 0; i < this._columns.length; i++) {
        var data = this._columns[i][collection] || "";
        if (!isArray(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] = Math.max(heights[j] || 0, data[j].height);
          if (data[j] && data[j].autoheight) heights[j] = Math.max(heights[j] || 0, this._getHeaderHeight(data[j], this._columns[i], i));
          if (data[j] && data[j].css && _typeof(data[j].css) === "object") data[j].css = createCss(data[j].css);
        }

        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 attr =
      /*@attr*/
      "active_id";
      return sec.querySelector("DIV[".concat(attr, "=\"").concat(id, "\"]"));
    },
    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;
      }
    },
    _render_subheader: function (start, end, width, name, heights) {
      if (start == end) return "";
      var html = "";
      var spans = "";
      var count = this._columns[0][name].length;
      var left = 0;

      for (var i = start; i < end; i++) {
        var _width = this._columns[i].width;
        var top = 0;
        var abs = i == start ? "position:static;" : "position:absolute; top:".concat(top, "px; left:").concat(left, "px;");
        html += "<div class=\"webix_hcolumn\" style=\"".concat(abs, "width:").concat(_width, "px;overflow:hidden;\">");

        for (var j = 0; j < count; j++) {
          var isSpan = false;
          var header = this._columns[i][name][j];
          var cell_height = heights[j];

          if (!header) {
            html += "<div class='webix_hcell' style='border-bottom-color:transparent;border-right-color:transparent;height:".concat(cell_height + 1, "px;'></div>");
            top += cell_height + 1;
            continue;
          }

          if (header.content) {
            header.contentId = header.contentId || uid();
            header.columnId = this._columns[i].id;
            header.format = this._columns[i].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;
          }

          var cell = "<div " +
          /*@attr*/
          "row" + "='" + j + "'" +
          /*@attr*/
          "column" + "='" + (header.colspan ? header.colspan - 1 + i : i) + "'";
          var hcss = "webix_hcell";
          if (header.css) hcss += " " + header.css;
          if (this._columns[i].$selected) hcss += " webix_sel_hcell";
          if (i == start) hcss += " webix_first";
          var column_pos = i + (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";
          var sheight = "";
          if (header.contentId) cell += " " +
          /*@attr*/
          "active_id" + "='" + header.contentId + "'";
          var cheight = cell_height;

          if (header.colspan || header.rowspan) {
            var cwidth = this._summ_right(this._columns, i, header.colspan) || _width;

            cheight = this._summ_next(heights, j, header.rowspan);
            if (cheight <= 0) cheight = cell_height;
            hcss += " webix_span";
            sheight = " colspan='".concat(header.colspan || 1, "' style='position:absolute; top:").concat(top, "px; left:").concat(left, "px; line-height:").concat(cheight + 1, "px; width:").concat(cwidth, "px; height:").concat(cheight + 1, "px;'");
            html += "<div class='webix_hcell' style='border-bottom-color:transparent;border-right-color:transparent;height:".concat(cell_height + 1, "px;'></div>");
            isSpan = true;
          } else {
            if (cell_height != this._settings.headerRowHeight) sheight = " style='line-height:".concat(cell_height + 1, "px; height:").concat(cell_height + 1, "px;'");
          }

          if (hcss) cell += " class=\"" + hcss + "\"";
          top += cell_height + 1;
          cell += " " + sheight + ">";
          var text = header.text === "" ? "&nbsp;" : header.text;
          if (header.rotate) text = "<div class='webix_rotate' style='width:" + (cheight - 10) + "px; transform-origin:center " + (cheight - 15) / 2 + "px;-webkit-transform-origin:center " + (cheight - 15) / 2 + "px;'>" + text + "</div>";
          cell += text + "</div>";
          if (isSpan) spans += cell;else html += cell;
        }

        left += _width;
        html += "</div>";
      }

      return html + spans;
    },
    _summ_next: function (heights, start, i) {
      var summ = -1;
      i += start;

      for (start; start < i; start++) {
        summ += heights[start] + 1;
      }

      return summ;
    },
    _summ_right: function (columns, start, i) {
      var summ = 0;
      i += start;

      for (start; start < i; start++) {
        summ += columns[start].width;
      }

      return summ;
    },
    showItemByIndex: function (row_ind, column_ind) {
      var pager = this._settings.pager;

      if (pager && row_ind >= this._settings.topSplit) {
        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 >= this._settings.topSplit || this._settings.prerender && 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 dataHeight = this._dtable_offset_height + 1;

          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) - (this._top_split_height || 0);
          } else if (summ + itemHeight > dataHeight) {
            //scroll bottom - show row at bottom of screen
            summ += itemHeight - dataHeight; //because of row rounding we need to scroll some extra

            for (var cur_ind = row_ind; cur_ind > 0 && dataHeight > 0; cur_ind--) {
              dataHeight -= this._getHeightByIndex(cur_ind);
            }

            if (row_ind > 0 && dataHeight) summ += this._getHeightByIndex(row_ind + 1);
          } else {
            summ = scroll.y;
          }

          scroll.y = summ;
        }
      }

      if (column_ind != -1) {
        //ignore split columns - they are always visible
        if (column_ind < this._settings.leftSplit || column_ind >= this._rightSplit) return this.scrollTo(scroll.x, scroll.y); //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;
          }

          var itemWidth = this._columns[column_ind].width;
          /*jsl:ignore*/

          if (column_ind < _state[0] + 1) ; else if (_summ + itemWidth > this._center_width) {
            //scroll to right border
            _summ += itemWidth - this._center_width;
          } else {
            _summ = scroll.x;
          }
          /*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);
    },
    _touch_scroll: "touch",
    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; // on mobile devices scrollLeft can be a non-round value

      if (Math.ceil(header) != Math.ceil(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 tag = "DIV";
        var attr =
        /*@attr*/
        "active_id";
        var cells = sec.querySelectorAll("".concat(tag, "[").concat(attr, "]"));

        for (var i = 0; i < cells.length; i++) {
          var activeId = cells[i].getAttribute(
          /*@attr*/
          "active_id");

          if (activeId) {
            var obj = this._active_headers[activeId];
            if (byId && byId != obj.columnId) continue;
            var content = datafilter[obj.content];

            if (getOnly) {
              if (content.getValue) {
                obj.value = content.getValue(cells[i]);
              }
            } else if (!cellTrackOnly || content.trackCells) {
              content.refresh(this, cells[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 != $$(node)) return null;
      node = node.target || 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();

      this._set_split_sizes_y();

      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];
      width = this._correctColumnWidth(width, column);
      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;
    },
    _correctColumnWidth: function (width, config) {
      width = Math.max(width, config.minWidth || this._settings.minColumnWidth || 0);
      width = Math.min(width, config.maxWidth || this._settings.maxColumnWidth || 100000);
      return width;
    },
    _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 Math.max(index2 - index1, 0) * this._settings.rowHeight;else {
        var summ = 0;

        for (; index1 < index2; index1++) {
          summ += this._getHeightByIndex(index1);
        }

        return summ;
      }
    },
    _cellPosition: function (row, column, edit) {
      if (row.row) {
        column = row.column;
        row = row.row;
      }

      var width = this.getColumnConfig(column).width;

      var height = this.getItem(row).$height || this._settings.rowHeight;

      if (this.config.spans && edit) {
        var span = this.getSpan(row, column);

        if (span) {
          var spanNode = this.getSpanNode({
            row: span[0],
            column: span[1]
          });
          width = spanNode.offsetWidth;
          height = spanNode.offsetHeight;
        }
      }

      var left = 0;
      var parent = 0;

      for (var _index = 0; _index < this._columns.length; _index++) {
        if (_index == this._settings.leftSplit || _index == this._rightSplit) left = 0;
        var leftcolumn = this._columns[_index];

        if (leftcolumn.id == column) {
          var split_column = _index < this._settings.leftSplit ? 0 : _index >= this._rightSplit ? 2 : 1;
          parent = this._body.childNodes[split_column].firstChild;
          break;
        }

        left += leftcolumn.width;
      }

      var index$$1 = this.getIndexById(row);
      var top;
      if (index$$1 < this._settings.topSplit) top = this._getHeightByIndexSumm(0, index$$1);else {
        var first = this._render_scroll_top || 0;
        top = this._getHeightByIndexSumm(first, index$$1) + (this._render_scroll_shift || 0) + (index$$1 >= first ? this._top_split_height || 0 : 0);
      }
      return {
        parent: parent,
        top: top,
        left: left,
        width: width,
        height: height
      };
    },
    _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;
      height = Math.max(height, this._settings.minRowHeight || 0);
      height = Math.min(height, this._settings.maxRowHeight || 100000);
      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;
      if (this._renderDelay) this._delayedLeftScroll = value;else 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;
        }
      }

      var xind = start + this._settings.topSplit; //in case of autoheight - request full rendering

      if (full || this._settings.autoheight) return [xind, end, 0];
      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._settings.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 || this._renderDelay) 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 _i5 = xr[1]; _i5 < this._rightSplit; _i5++) {
          this._hideColumn(_i5, force);
        }
      }

      this._render_full_rows = [];

      for (var _i6 = 0; _i6 < this._settings.leftSplit; _i6++) {
        this._renderColumn(_i6, yr, force);
      }

      for (var _i7 = xr[0]; _i7 < xr[1]; _i7++) {
        this._renderColumn(_i7, yr, force, _i7 == xr[0]);
      }

      for (var _i8 = this._rightSplit; _i8 < this._columns.length; _i8++) {
        this._renderColumn(_i8, 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 _i9 = 0; _i9 < this._render_full_rows.length; _i9++) {
        var info = this._render_full_rows[_i9];
        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), _i9);
        }

        var _row = this._rows_cache[_i9] = 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[_i9].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 _i10 = Math.max(yr[0], this._settings.topSplit); _i10 < yr[1]; _i10++) {
        html += this._render_single_cell(_i10, 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$1.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.config.scrollPos = this._y_scroll.config.scrollPos = 0;
        this.scrollTo(state.x, state.y);
      }
    },
    $setSize: function () {
      var oldw = this._content_width;
      var oldh = this._content_height;

      if (base$1.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, e) {
      var col = this.getColumnConfig(column);
      if (!this._settings.sort || !col.sort) return;
      var dir = "asc";

      if (this._last_sorted[col.id]) {
        dir = this._last_sorted[col.id].dir == "asc" ? "desc" : "asc";
      } else if (this._sort_signs[col.id]) {
        dir = this._sort_signs[col.id].getAttribute("webix_sort_dir") == "asc" ? "desc" : "asc";
      }

      this._sort(col.id, dir, col.sort, e.ctrlKey || e.metaKey);
    },
    markSorting: function (column, direction, preserve) {
      direction = direction || "asc";

      if (!preserve) {
        this._sort_signs_order = [];

        for (var i in this._sort_signs) {
          if (i !== column) {
            var parent = this._sort_signs[i].parentNode;

            if (parent) {
              parent.removeAttribute("aria-sort");
              parent.removeAttribute("tabindex");
            }

            remove(this._sort_signs[i]);
            delete this._sort_signs[i];
          } else {
            this._sort_signs_order.push(i);

            this._sort_signs[i].firstChild.innerHTML = "1";
          }
        }
      }

      if (!column) return;

      if (this._sort_signs[column]) {
        this._sort_signs[column].className = "webix_ss_sort_".concat(direction);
      } else {
        var sign = create("div", {
          webix_sort_dir: direction,
          class: "webix_ss_sort_".concat(direction)
        }, "<div class=\"webix_ss_sort_num\">".concat(this._sort_signs_order.length + 1, "</div>"));

        this._render_single_mark(column, sign, direction);

        this._sort_signs[column] = sign;

        this._sort_signs_order.push(column);
      }

      var first = this._sort_signs[this._sort_signs_order[0]];

      switch (this._sort_signs_order.length) {
        case 1:
          addCss(first, "webix_ss_sort_single");
          break;

        case 2:
          removeCss(first, "webix_ss_sort_single");
      }
    },
    _render_single_mark: function (column, sign, direction) {
      direction = direction || sign.getAttribute("webix_sort_dir");

      var cell = this._get_header_cell(this.getColumnIndex(column));

      if (cell) {
        if (cell.style.position != "absolute") cell.style.position = "relative";
        cell.appendChild(sign);
        cell.setAttribute("aria-sort", direction + "ending");
        cell.setAttribute("tabindex", "0");
      }
    },
    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 attr =
      /*@attr*/
      "column";

      var cells = this._header.querySelectorAll("div[".concat(attr, "=\"").concat(column, "\"]"));

      var maybe = null;

      for (var i = 0; i < cells.length; i++) {
        var activeId = cells[i].getAttribute(
        /*@attr*/
        "active_id");
        if (activeId && !datafilter[this._active_headers[activeId].content].$icon) return null;
        if (!cells[i].innerHTML) continue;
        maybe = cells[i];
        if ((cells[i].colSpan || 0) < 2) return maybe;
      }

      return maybe;
    },
    _last_order: [],
    _last_sorted: {},
    _sort: function (col_id, direction, type$$1, preserve) {
      var _this2 = this;

      preserve = this._settings.sort === "multi" && preserve;
      direction = direction || "asc";

      if (!preserve) {
        this._last_order = [];
        this._last_sorted = {};
      }

      var col = this.getColumnConfig(col_id);
      var config = typeof col.sort == "function" ? {
        as: col.sort,
        dir: direction
      } : {
        by: col.id,
        dir: direction,
        as: col.sort
      };
      if (!this._last_sorted[col.id]) this._last_order.push(col.id);
      this._last_sorted[col.id] = config;

      if (type$$1 == "server") {
        var parameters = [col.id, direction, type$$1];
        if (this._last_order.length > 1) parameters = [this._last_order.map(function (id) {
          return _this2._last_sorted[id];
        })];
        this.callEvent("onBeforeSort", parameters);
        this.loadNext(0, 0, 0, 0, 1).then(function (data) {
          _this2.clearAll(true);

          _this2.parse(data);

          _this2.callEvent("onAfterSort", parameters);
        });
      } else {
        if (type$$1 == "text") {
          var new_id = "$text_" + col.id;
          this.data.each(function (obj) {
            obj[new_id] = this.getText(obj.id, col.id);
          }, this);
          config.as = "string";
          config.by = new_id;
        }

        if (this._last_order.length > 1) this.data.sort(this._last_order.map(function (id) {
          return _this2._last_sorted[id];
        }));else this.data.sort(config);
      }

      this.markSorting(col.id, config.dir, preserve);
    },
    _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;
      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 = this._body.contains(trg); //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) {
                  var pager = this._settings.pager;
                  if (pager || !this._settings.prerender && !this._settings.autoheight) 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 = this._header.contains(trg);

              if (isHeader && this.callEvent("onHeaderClick", [id, e, trg])) this._on_header_click(id.column, e);
            }

            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;
        var section = node;
        var pos$$1;
        var cind = id.cind - (id.span ? id.span - 1 : 0);
        var rind = -1;

        while (section && !pos$$1) {
          section = section.parentNode;
          pos$$1 = section.getAttribute("section");
        }

        while (node && rind < 0) {
          var row = node.getAttribute("row");
          if (row) rind = row;
          node = node.parentNode;
        }

        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); //empty tooltip - ignoring


        if (!_config.tooltip && _config.tooltip !== undefined) return null;

        if (_config.tooltip === true || !_config.tooltip && isUndefined(this._settings.tooltip.template)) {
          data = this.getText(id.row, id.column).toString();
        } else if (_config.tooltip) {
          var area = e.target.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$1h = exports.protoUI(api$1h, 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$1.view, EventSystem, Settings);
  var datatable = {
    api: api$1h,
    view: view$1h
  };

  var api$1i = {
    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$1i = exports.protoUI(api$1i, TreeAPI, TreeStateCheckbox, TreeDataLoader, datatable.view);

  exports.protoUI({
    name: "filter",
    defaults: {
      template: "GPL version doesn't support filter <br> You need Webix PRO"
    }
  }, template$1.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, config) {
      this.data = update ? exports.extend(this.data, data, true) : data;
      this.callEvent("onChange", [data, config]);
    },
    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, config) {
      this.data = value;
      this.callEvent("onChange", [value, config]);
    },
    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.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.storage = storage;
  exports.template = template;
  exports.type = type;
  exports.editors = editors;
  exports.animate = animate;
  exports.print = print;
  exports.rules = rules;
  exports.filters = filters;
  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.toFunctor = toFunctor;
  exports.isArray = isArray;
  exports.isDate = isDate;
  exports._to_array = _to_array;
  exports._power_array = _power_array;
  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.prompt = prompt;
  exports.message = message;
  exports.editStop = editStop;
  exports.ajax = ajax;
  exports.DataDriver = DataDriver;
  exports.dp = dp;
  exports.DataProcessor = DataProcessor;
  exports.remote = remote;
  exports.require = require;
  exports.proxy = proxy$5;
  exports.send = send;
  exports.ui = ui;
  exports.$$ = $$;

  Object.defineProperty(exports, '__esModule', { value: true });

})));
