/* @license webix UI v.5.0.1 This software is covered by Webix Commercial License. Usage without proper license is prohibited. (c) XB Software Ltd. */ if (!window.webix) webix={}; //check some rule, show message as error if rule is not correct webix.assert = function(test, message){ if (!test){ webix.assert_error(message); } }; webix.assert_config = function(obj){ var coll = obj.cells || obj.rows || obj.elements || obj.cols; if (coll) for (var i=0; i1){ target = arguments[0]; source = arguments[1]; } else target = (webix.isArray(source)?[]:{}); for (var method in source){ var from = source[method]; if(from && typeof from == "object" && !(from instanceof RegExp)){ if (!webix.isDate(from)){ target[method] = (webix.isArray(from)?[]:{}); webix.copy(target[method],from); } else target[method] = new Date(from); } else { target[method] = from; } } webix.assert_level_out(); return target; }; webix.single = function(source){ var instance = null; var t = function(config){ if (!instance) instance = new source({}); if (instance._reinit) instance._reinit.apply(instance, arguments); return instance; }; return t; }; webix.protoUI = function(){ if (webix.debug_proto) webix.log("UI registered: "+arguments[0].name); var origins = arguments; var selfname = origins[0].name; var t = function(data){ if (!t) return webix.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(webix, params[i].name); if (params[i].prototype && params[i].prototype.name) webix.ui[params[i].prototype.name] = params[i]; } webix.ui[selfname] = webix.proto.apply(webix, params); if (t._webix_type_wait) for (var i=0; i < t._webix_type_wait.length; i++) webix.type(webix.ui[selfname], t._webix_type_wait[i]); t = origins = null; } if (this != webix) return new webix.ui[selfname](data); else return webix.ui[selfname]; }; t.$protoWait = Array.prototype.slice.call(arguments, 0); return (webix.ui[selfname]=t); }; webix.proto = function(){ if (webix.debug_proto) webix.log("Proto chain:"+arguments[0].name+"["+arguments.length+"]"); var origins = arguments; var compilation = origins[0]; var has_constructor = !!compilation.$init; var construct = []; webix.assert(compilation,"Invalid mixing target"); for (var i=origins.length-1; i>0; i--) { webix.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 (webix.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 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){ webix.extend(this._evs_map, map, true); }, on_setter:function(config){ if(config){ for(var i in config){ var method = webix.toFunctor(config[i], this.$scope); var sub = i.indexOf("->"); if (sub !== -1){ this[i.substr(0,sub)].attachEvent(i.substr(sub+2), webix.bind(method, this)); } else this.attachEvent(i, method); } } }, //trigger event callEvent:function(type,params){ 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 (webix.log) if ((webix.debug || this.debug) && !webix.debug_blacklist[type]) //can slowdown a lot webix.log("info","["+this.name+"@"+((this._settings||{}).id)+"] event:"+type,params); if (event_stack) for(var i=0; i=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 { var b = this.splice(pos,(this.length-pos)); this[pos] = data; this.push.apply(this,b); //reconstruct array without loosing this pointer } }, //return index of element, -1 if it doesn't exists find:function(data){ for (var i=0; i -1){ webix.env.cssPrefix = css_list[found_index]; var jp = webix.env.jsPrefix = js_list[found_index]; webix.env.transform = jp ? jp+"Transform" : "transform"; webix.env.transition = jp ? jp+"Transition" : "transition"; webix.env.transitionDuration = jp ? jp+"TransitionDuration" : "transitionDuration"; d.style[webix.env.transform] = "translate3d(0,0,0)"; webix.env.translate = (d.style[webix.env.transform])?"translate3d":"translate"; webix.env.transitionEnd = ((webix.env.cssPrefix == '-Moz-')?"transitionend":(jp ? jp+"TransitionEnd" : "transitionend")); } webix.env.pointerevents = (!webix.env.isIE ||(new RegExp("Trident/.*rv:11")).exec(agent) !== null); })(); webix.env.svg = (function(){ return document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1"); })(); webix.env.svganimation = (function(){ return document.implementation.hasFeature("https://www.w3.org/TR/SVG11/feature#SVG-animation", "1.1"); })(); //html helpers webix.html={ _native_on_selectstart:0, _style_element:{}, denySelect:function(){ if (!webix._native_on_selectstart) webix._native_on_selectstart = document.onselectstart; document.onselectstart = webix.html.stopEvent; }, allowSelect:function(){ if (webix._native_on_selectstart !== 0){ document.onselectstart = webix._native_on_selectstart||null; } webix._native_on_selectstart = 0; }, index:function(node){ var k=0; //must be =, it is not a comparation! while ((node = node.previousSibling)) k++; return k; }, _style_cache:{}, createCss:function(rule, sufix){ var text = ""; sufix = sufix || ""; for (var key in rule) text+= key+":"+rule[key]+";"; var name = this._style_cache[text+sufix]; if (!name){ name = "s"+webix.uid(); this.addStyle("."+name+(sufix||"")+"{"+text+"}"); this._style_cache[text+sufix] = name; } return name; }, addStyle:function(rule, group){ var style = group ? this._style_element[group] :this._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) this._style_element[group] = style; else this._style_element["default"] = style; } /*IE8*/ if (style.styleSheet) style.styleSheet.cssText += rule; else style.appendChild(document.createTextNode(rule)); }, removeStyle:function(group){ var box = this._style_element[group||"default"]; if (box) box.innerHTML = ""; }, create:function(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 getValue:function(node){ node = webix.toNode(node); if (!node) return ""; return webix.isUndefined(node.value)?node.innerHTML:node.value; }, //remove html node, can process an array of nodes at once remove:function(node){ if (node instanceof Array) for (var i=0; i < node.length; i++) this.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 insertBefore: function(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 locate:function(e,id){ var trg; if (e.tagName) trg = e; else { e=e||event; trg=e.target||e.srcElement; } while (trg){ if (trg.getAttribute){ //text nodes has not getAttribute var test = trg.getAttribute(id); if (test) return test; } trg=trg.parentNode; } return null; }, //returns position of html element on the page offset:function(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 posRelative:function(ev){ ev = ev || event; if (!webix.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 pos:function(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 = ((webix.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 preventEvent:function(e){ if(e && e.preventDefault) e.preventDefault(); if(e) e.returnValue = false; return webix.html.stopEvent(e); }, //stop event bubbling stopEvent:function(e){ e = (e||event); if(e.stopPropagation) e.stopPropagation(); e.cancelBubble=true; return false; }, triggerEvent:function(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 addCss:function(node,name,check){ if (!check || node.className.indexOf(name) === -1) node.className+=" "+name; }, //remove css class from the node removeCss:function(node,name){ node.className=node.className.replace(RegExp(" "+name,"g"),""); }, getTextSize:function(text, css, width){ var d = webix.html.create("DIV",{"class":"webix_view webix_measure_size "+(css||"")},""); d.style.cssText = "height:auto;visibility:hidden; position:absolute; top:0px; left:0px; overflow:hidden;"+(width?("width:"+width+"px;"):"width:auto;white-space:nowrap;"); document.body.appendChild(d); var all = (typeof text !== "object") ? [text] : text; var width = 0; var height = 0; for (var i = 0; i < all.length; i++) { d.innerHTML = all[i]; width = Math.max(width, d.offsetWidth); height = Math.max(height, d.offsetHeight); } webix.html.remove(d); return { width:width, height:height }; }, download:function(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(); webix.delay(function(){ if(objUrl) window.URL.revokeObjectURL(data); document.body.removeChild(link); link.remove(); }); }, _getClassName: function(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; }, setSelectionRange:function(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(); } }, getSelectionRange:function(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}; } } }; webix.ready = function(code){ if (this._ready) code.call(); else this._ready_code.push(code); }; webix.debug_ready = webix.ready; //same command but will work only in dev. build webix._ready_code = []; //autodetect codebase folder (function(){ var temp = document.getElementsByTagName("SCRIPT"); //current script, most probably webix.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); webix.codebase = temp.slice(0, temp.length).join("/")+"/"; } var ready = function(){ if(webix.env.isIE) document.body.className += " webix_ie"; webix.callEvent("onReady",[]); }; var doit = function(){ webix._ready = true; //global plugins if (window.webix_ready && webix.isArray(webix_ready)) webix._ready_code = webix_ready.concat(webix._ready_code); for (var i=0; i < webix._ready_code.length; i++) webix._ready_code[i].call(); webix._ready_code=[]; }; webix.attachEvent("onReady", function(force){ if (force) doit(); else webix.delay(doit); }); if (document.readyState == "complete") ready(); else webix.event(window, "load", ready); })(); webix.locale=webix.locale||{}; webix.assert_core_ready(); webix.ready(function(){ webix.event(document.body,"click", function(e){ webix.callEvent("onClick",[e||event]); }); }); webix.editStop = function(){ webix.callEvent("onEditEnd", []); }; webix.debug_blacklist={ onmousemoving:1 }; /** Bazed on Promiz - A fast Promises/A+ library https://github.com/Zolmeister/promiz The MIT License (MIT) Copyright (c) 2014 Zolmeister */ /* jshint ignore:start */ (function (self) { var global = this 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) {} delete queue[e.data] isRunningTask = false } } }) function nextTick(fn) { if (global.setImmediate) 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, i) { if (v && v.then) v.then(function (r) { done(null, r) }, done) }) } done() return d } Deferred._d = 1 /** * @constructor */ function Deferred(resolver) { 'use strict' 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['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": ">", '"': """, "'": "'", "`": "`" }; var badChars = /[&<>"'`]/g; var escapeChar = function(chr) { return escape[chr] || "&"; }; webix.template = function(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 = webix.html.getValue(teststr[1]); break; case "http": //load from external file str = new webix.ajax().sync().get(teststr[1],{uid:webix.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(webix.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){ temp_res.push({pos: pos, str: search, fn: function(obj,common){ return obj[s1]?s2:s3; }}); }); str.replace(/\{common\.([^}\(]*)\}/g,function(search,s,pos){ temp_res.push({pos: pos, str: search, fn: function(obj,common){ return common[s]||''; }}); }); str.replace(/\{common\.([^\}\(]*)\(\)\}/g,function(search,s,pos){ temp_res.push({pos: pos, str: search, fn: function(obj,common){ return (common[s]?common[s].apply(this, arguments):""); }}); }); str.replace(/\{obj\.([^:}]*)\}/g,function(search,s,pos){ temp_res.push({pos: pos, str: search, fn: function(obj,common){ return obj[s]; }}); }); str.replace("{obj}",function(search,s,pos){ temp_res.push({pos: pos, str: search, fn: function(obj,common){ return obj; }}); }); str.replace(/#([^#'";, ]+)#/gi,function(search,s,pos){ if(s.charAt(0)=="!"){ s = s.substr(1); temp_res.push({pos: pos, str: search, fn: function(obj,common){ if(s.indexOf(".")!= -1) obj = webix.CodeParser.collapseNames(obj); // apply complex properties return webix.template.escape(obj[s]); }}); } else{ temp_res.push({pos: pos, str: search, fn: function(obj,common){ if(s.indexOf(".")!= -1) obj = webix.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 = temp_res[i].pos; addStr(str,lastPos,pos); _csp_cache[str].push(temp_res[i].fn); lastPos = pos + 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; }; } 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)=="!") return "\"+webix.template.escape(obj."+key.substr(1)+")+\""; else return "\"+(obj."+key+")+\""; }); try { _cache[str] = Function("obj","common","return \""+str+"\";"); } catch(e){ webix.assert_error("Invalid template:"+str); } return _cache[str]; }; webix.template.escape = function(str){ if (str === webix.undefined || str === null) return ""; return (str.toString() || "" ).replace(badChars, escapeChar); }; webix.template.empty=function(){ return ""; }; webix.template.bind =function(value){ return webix.bind(webix.template(value),this); }; /* adds new template-type obj - object to which template will be added data - properties of template */ webix.type=function(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] = webix.clone(data.baseType?obj.types[data.baseType]:obj.type); for(var key in data){ if (key.indexOf("template")===0) type[key] = webix.template(data[key]); else type[key]=data[key]; } return name; }; })(); webix.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 = webix.extend(settings,initial); //code below will copy all properties over default one if (typeof obj == "object" && !obj.tagName) webix.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; } }; /* ajax operations can be used for direct loading as webix.ajax(ulr, callback) or webix.ajax().getItem(url) webix.ajax().post(url) */ webix.proxy = function(name, source, extra){ webix.assert(webix.proxy[name], "Invalid proxy name: "+name); var copy = webix.copy(webix.proxy[name]); copy.source = source; if (extra) webix.extend(copy, extra, true); if (copy.init) copy.init(); return copy; }; webix.proxy.$parse = function(value){ if (typeof value == "string" && value.indexOf("->") != -1){ var parts = value.split("->"); return webix.proxy(parts[0], parts[1]); } return value; }; webix.proxy.post = { $proxy:true, load:function(view, callback, params){ params = webix.extend(params||{}, this.params || {}, true); webix.ajax().bind(view).post(this.source, params, callback); } }; webix.proxy.sync = { $proxy:true, load:function(view, callback){ webix.ajax().sync().bind(view).get(this.source, null, callback); } }; webix.proxy.connector = { $proxy:true, connectorName:"!nativeeditor_status", load:function(view, callback){ webix.ajax(this.source, callback, view); }, saveAll:function(view, updates, dp, callback){ var url = this.source; var data = {}; var ids = []; for (var i = 0; i < updates.length; i++) { var action = updates[i]; ids.push(action.id); for (var j in action.data) if (j.indexOf("$")!==0) data[action.id+"_"+j] = action.data[j]; data[action.id+"_"+this.connectorName] = action.operation; } data.ids = ids.join(","); data.webix_security = webix.securityKey; url += (url.indexOf("?") == -1) ? "?" : "&"; url += "editing=true"; webix.ajax().post(url, data, callback); }, result:function(state, view, dp, text, data, loader){ data = data.xml(); if (!data) return dp._processError(null, text, data, loader); var actions = data.data.action; if (!actions.length) actions = [actions]; var hash = []; for (var i = 0; i < actions.length; i++) { var obj = actions[i]; hash.push(obj); obj.status = obj.type; obj.id = obj.sid; obj.newid = obj.tid; dp.processResult(obj, obj, {text:text, data:data, loader:loader}); } return hash; } }; webix.proxy.debug = { $proxy:true, load:function(){}, save:function(v,u,d,c){ webix.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); }); } }; webix.proxy.rest = { $proxy:true, load:function(view, callback){ webix.ajax(this.source, callback, view); }, save:function(view, update, dp, callback){ return webix.proxy.rest._save_logic.call(this, view, update, dp, callback, webix.ajax()); }, _save_logic:function(view, update, dp, callback, ajax){ 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"){ ajax.put(url + data.id + query, data, callback); } else if (mode == "delete") { ajax.del(url + data.id + query, data, callback); } else { ajax.post(url + query, data, callback); } } }; webix.proxy.json = { $proxy:true, load:function(view, callback){ webix.ajax(this.source, callback, view); }, save:function(view, update, dp, callback){ var ajax = webix.ajax().headers({ "Content-Type":"application/json" }); return webix.proxy.rest._save_logic.call(this, view, update, dp, callback, ajax); } }; webix.proxy.faye = { $proxy:true, init:function(){ this.clientId = this.clientId || webix.uid(); }, load:function(view){ var selfid = this.clientId; this.client.subscribe(this.source, function(update){ if (update.clientId == selfid) return; webix.dp(view).ignore(function(){ if (update.operation == "delete") view.remove(update.data.id); else if (update.operation == "insert") view.add(update.data); else if (update.operation == "update"){ var item = view.getItem(update.data.id); if (item){ webix.extend(item, update.data, true); view.refresh(item.id); } } }); }); }, save:function(view, update, dp, callback){ update.clientId = this.clientId; this.client.publish(this.source, update); } }; //indexdb->database/collection webix.proxy.indexdb = { $proxy:true, create:function(db, config, version, callback){ this.source = db + "/"; this._get_db(callback, version, function(e){ var db = e.target.result; for (var key in config){ var data = config[key]; var store = db.createObjectStore(key, { keyPath: "id", autoIncrement:true }); for (var i = 0; i < data.length; i++) store.put(data[i]); } }); }, _get_db:function(callback, version, upgrade){ if (this.source.indexOf("/") != -1){ var parts = this.source.split("/"); this.source = parts[1]; version = version || parts[2]; var _index = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB; var db; if (version) db = _index.open(parts[0], version); else db = _index.open(parts[0]); if (upgrade) db.onupgradeneeded = upgrade; db.onerror = function(){ }; db.onblocked = function(){ }; db.onsuccess = webix.bind(function(e){ this.db = e.target.result; if (callback) callback.call(this); },this); } else if (this.db) callback.call(this); else webix.delay(this._get_db, this, [callback], 50); }, load:function(view, callback){ this._get_db(function(){ var store = this.db.transaction(this.source).objectStore(this.source); var data = []; store.openCursor().onsuccess = function(e) { var result = e.target.result; if(result){ data.push(result.value); result["continue"](); } else { view.parse(data); webix.ajax.$callback(view, callback, "[]", data); } }; }); }, save:function(view, update, dp, callback){ this._get_db(function(){ var mode = update.operation; var data = update.data; var id = update.id; var store = this.db.transaction([this.source], "readwrite").objectStore(this.source); var req; if (mode == "delete") req = store["delete"](id); else if (mode == "update") req = store.put(data); else if (mode == "insert"){ delete data.id; req = store.add(data); } req.onsuccess = function(e) { var result = { status: mode, id:update.id }; if (mode == "insert") result.newid = e.target.result; dp.processResult(result, result); }; }); } }; webix.proxy.binary = { $proxy:true, load:function(view, callback){ var parts = this.source.split("@"); var ext = parts[0].split(".").pop(); return webix.ajax().response("arraybuffer").get(parts[0]).then(function(res){ var options = { ext:ext, dataurl : parts[1] }; webix.ajax.$callback(view, callback, "", { data:res, options:options }, -1); }); } }; webix.ajax = function(url,params,call){ //if parameters was provided - made fast call if (arguments.length!==0) return (new webix.ajax()).get(url,params,call); if (!this.getXHR) return new webix.ajax(); //allow to create new instance without direct new declaration return this; }; webix.ajax.count = 0; webix.ajax.prototype={ master:null, //creates xmlHTTP object getXHR:function(){ return new XMLHttpRequest(); }, stringify:function(obj){ return webix.stringify(obj); }, /* send data to the server params - hash of properties which will be added to the url call - callback, can be an array of functions */ _send:function(url, params, call, mode){ var master; if (params && (webix.isArray(params) || (typeof (params.success || params.error || params) == "function"))){ master = call; call = params; params = null; } var defer = webix.promise.defer(); var x=this.getXHR(); if (!webix.isArray(call)) call = [call]; call.push({ success: function(t, d){ defer.resolve(d); }, error: function(t, d){ defer.reject(x); }}); var headers = this._header || {}; if (!webix.callEvent("onBeforeAjax", [mode, url, params, x, headers, null, defer])) return; //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) 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 === webix.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){ if (webix.debug_time) webix.log_full_time("data_loading"); //log rendering time webix.ajax.count++; if (call && self && !x.aborted){ //IE8 and IE9, handling .abort call if (webix._xhr_aborted.find(x) != -1) return webix._xhr_aborted.remove(x); var owner = self.master||self; 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); } webix.ajax.$callback(owner, call, text, data, x, is_error); } if (self) self.master=null; call=self=master=null; //anti-leak } }; if (this._timeout) x.timeout = this._timeout; //IE can use sync mode sometimes, fix it if (!this._sync) setTimeout(function(){ if (!x.aborted){ //abort handling in IE9 if (webix._xhr_aborted.find(x) != -1) webix._xhr_aborted.remove(x); else x.send(params||null); } }, 1); else x.send(params||null); if (this.master && this.master._ajax_queue) this.master._ajax_queue.push(x); return this._sync?x:defer; //return XHR, which can be used in case of sync. mode }, _data:function(x){ return { xml:function(){ try{ return webix.DataDriver.xml.tagToObject(webix.DataDriver.xml.toObject(x.responseText, this)); } catch(e){ webix.log(x.responseText); webix.log(e.toString()); webix.assert_error("Invalid xml data for parsing"); } }, rawxml:function(){ if (!window.XPathResult) return webix.DataDriver.xml.fromString(x.responseText); return x.responseXML; }, text:function(){ return x.responseText; }, json:function(){ return webix.DataDriver.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; }, //deprecated, remove in 3.0 //[DEPRECATED] header:function(header){ webix.assert(false, "ajax.header is deprecated in favor of ajax.headers"); this._header = header; return this; }, headers:function(header){ this._header = webix.extend(this._header||{},header); return this; }, bind:function(master){ this.master = master; return this; } }; webix.ajax.$callback = function(owner, call, text, data, x, is_error){ if (owner.$destructed) return; if (x === -1 && data && typeof data.json == "function") data = data.json(); if (is_error) webix.callEvent("onAjaxError", [x]); if (!webix.isArray(call)) call = [call]; if (!is_error) for (var i=0; i < call.length; i++){ if (call[i]){ var before = call[i].before; if (before) before.call(owner, text, data, x); } } for (var i=0; i < call.length; i++) //there can be multiple callbacks if (call[i]){ var method = (call[i].success||call[i]); if (is_error) method = call[i].error; if (method && method.call) method.call(owner,text,data,x); } }; /*submits values*/ webix.send = function(url, values, method, target){ var form = webix.html.create("FORM",{ "target":(target||"_self"), "action":url, "method":(method||"POST") },""); for (var k in values) { var field = webix.html.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); }; webix.AtomDataLoader={ $init:function(config){ //prepare data store this.data = {}; this.waitData = webix.promise.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 = webix.proxy.$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 details = arguments[2] || null; if(!this.callEvent("onBeforeLoad",[])) return webix.promise.reject(); if (typeof call == "string"){ //second parameter can be a loading type or callback //we are not using setDriver as data may be a non-datastore here this.data.driver = webix.DataDriver[call]; call = arguments[2]; } else if (!this.data.driver) this.data.driver = webix.DataDriver.json; //load data by async ajax call //loading_key - can be set by component, to ignore data from old async requests var callback = [{ success: this._onLoad, error: this._onLoadError }]; if (call){ if (webix.isArray(call)) callback.push.apply(callback,call); else callback.push(call); } //proxy url = webix.proxy.$parse(url); if (url.$proxy && url.load) return url.load(this, callback, details); //promize if (typeof url === "function"){ return url(details).then( webix.bind(function(data){ webix.ajax.$callback(this, callback, "", data, -1); }, this), webix.bind(function(x){ webix.ajax.$callback(this, callback, "", null, x, true); }, this) ); } //normal url return webix.ajax(url,callback,this); }, //loads data from object parse:function(data,type){ //[webix.remote] if (data && data.then && typeof data.then == "function"){ return data.then(webix.bind(function(data){ if (data && typeof data.json == "function") data = data.json(); this.parse(data, type); }, this)); } //loading data from other component if (data && data.sync && this.sync) return this._syncData(data); if(!this.callEvent("onBeforeLoad",[])) return webix.promise.reject(); this.data.driver = webix.DataDriver[type||"json"]; this._onLoad(data,null); }, _syncData: function(data){ if(this.data) this.data.attachEvent("onSyncApply",webix.bind(function(){ if(this._call_onready) this._call_onready(); },this)); this.sync(data); }, _parse:function(data){ var parsed, record, driver = this.data.driver; record = driver.getRecords(data)[0]; parsed = record?driver.getDetails(record):{}; if (this.setValues) this.setValues(parsed); else this.data = parsed; }, _onLoadContinue:function(data, text, response, loader){ 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(text, response, loader); //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(text, response, loader){ var driver = this.data.driver; var data; if (loader === -1) data = driver.toObject(response); else{ //ignore data loading command if data was reloaded if(this._ajax_queue) this._ajax_queue.remove(loader); data = driver.toObject(text, response); } if(!data || !data.then) this._onLoadContinue(data); else if(data.then && typeof data.then == "function") data.then(webix.bind(this._onLoadContinue, this)); }, _onLoadError:function(text, xml, xhttp){ this.callEvent("onAfterLoad",[]); this.callEvent("onLoadError",arguments); webix.callEvent("onLoadError", [text, xml, xhttp, this]); }, _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; webix.ajax(url, function(text,xml,loader){ this._ignore_feed=true; var driver = webix.DataDriver.json; var data = driver.toObject(text, xml); if (data) this.setValues(driver.getDetails(driver.getRecords(data)[0])); else this._onLoadError(text,xml,loader); this._ignore_feed=false; this.callEvent("onAfterLoad",[]); }, this); return false; } }; /* Abstraction layer for different data types */ webix.DataDriver={}; webix.DataDriver.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){ webix.log(e); webix.log(data); webix.assert_error("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 && !webix.isArray(data)) return [data]; return data; }, //get hash of properties for single record getDetails:function(data){ if (typeof data == "string") return { id:(data||webix.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||0), parent:(data.parent||0), config:(data.config), key:(data.webix_security) }; }, child:"data", parseDates:false }; webix.DataDriver.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 = webix.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 webix.DataDriver.xml.tagToObject(data); }, getOptions:function(){ return false; }, //dyn loading is not supported by HTML data source getInfo:function(data){ return { size:0, from:0 }; }, tag: "LI" }; webix.DataDriver.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(data){ return { size:0, from:0 }; }, idColumn:null }; webix.DataDriver.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(data){ return { size:0, from: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 }; webix.DataDriver.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")||0), parent:(data.documentElement.getAttribute("parent")||0), config:config, key:(data.documentElement.getAttribute("webix_security")||null) }; }, //xpath helper xpath:function(xml,path){ if (window.XPathResult){ //FF, KHTML, Opera var node=xml; if(xml.nodeName.indexOf("document")==-1) xml=xml.ownerDocument; var res = []; var col = xml.evaluate(path, node, null, XPathResult.ANY_TYPE, null); var temp = col.iterateNext(); while (temp){ res.push(temp); temp = col.iterateNext(); } return res; } else { var test = true; try { if (typeof(xml.selectNodes)=="undefined") test = false; } catch(e){ /*IE7 and below can't operate with xml object*/ } //IE if (test) return xml.selectNodes(path); else { //there is no interface to do XPath //use naive approach var name = path.split("/").pop(); return xml.getElementsByTagName(name); } } }, assignTypes:function(obj){ for (var k in obj){ var test = obj[k]; if (typeof test == "object") this.assignTypes(test); else if (typeof test == "string"){ if (test === "") continue; if (test == "true") obj[k] = true; else if (test == "false") obj[k] = false; else if (test == test*1) obj[k] = obj[k]*1; } } return obj; }, //convert xml tag to js object, all subtags and attributes are mapped to the properties of result object tagToObject:function(tag,z){ var isArray = tag.nodeType == 1 && tag.getAttribute("stack"); var hasSubTags = 0; if (!isArray){ z=z||{}; //map attributes var a=tag.attributes; if(a && a.length) for (var i=0; inot as file://", expire:-1 }); else window.alert("Please open sample by http, not as file://"); } }); }); //UI interface webix.BaseBind = { bind:function(target, rule, format){ if (!this.attachEvent) webix.extend(this, webix.EventSystem); if (typeof target == 'string') target = webix.$$(target); if (target._initBindSource) target._initBindSource(); if (this._initBindSource) this._initBindSource(); if (!target.getBindData) webix.extend(target, webix.BindSource); this._bind_ready(); target.addBind(this._settings.id, rule, format); this._bind_source = target._settings.id; if (webix.debug_bind) webix.log("[bind] "+this.name+"@"+this._settings.id+" <= "+target.name+"@"+target._settings.id); var target_id = this._settings.id; //FIXME - check for touchable is not the best solution, to detect necessary event this._bind_refresh_handler = this.attachEvent(this.touchable?"onAfterRender":"onBindRequest", function(){ return target.getBindData(target_id); }); if (this.refresh && this.isVisible(this._settings.id)) this.refresh(); }, unbind:function(){ if (this._bind_source){ var target = webix.$$(this._bind_source); if (target) target.removeBind(this._settings.id); this.detachEvent(this._bind_refresh_handler); this._bind_source = null; } }, _bind_ready:function(){ var config = this._settings; if (this.filter){ var key = config.id; this.data._on_sync = webix.bind(function(){ webix.$$(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 = webix.$$(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(){}; } }; //bind interface webix.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 (webix.debug_bind) webix.log("[bind:save] "+this.name+"@"+this._settings.id+" <= "+"@"+key); if (this.setValue) this.setValue(data); else if (this.setValues) this.setValues(data); else { var id = this.getCursor(); if (id) this.updateItem(id, data); else this.add(data); } this.callEvent("onBindUpdate", [data, key]); if (this.save) this.save(); if (key) this._ignore_binds[key] = false; }, //fill target with data getBindData:function(key, update){ //fire only if we have data updates from the last time if (this._bind_updated[key]) return false; var target = webix.$$(key); //fill target only when it visible if (target.isVisible(target._settings.id)){ this._bind_updated[key] = true; if (webix.debug_bind) webix.log("[bind:request] "+this.name+"@"+this._settings.id+" => "+target.name+"@"+target._settings.id); 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) webix.extend(this, webix.CollectionBind); else if (obj.setValue) webix.extend(this, webix.ValueBind); else webix.extend(this, webix.RecordBind); }, //inform all binded objects, that source data was updated _update_binds:function(){ if (!this._do_not_update_binds) for (var key in this._bind_hash){ if (this._ignore_binds[key]) continue; this._bind_updated[key] = false; this.getBindData(key, true); } }, //copy data from source to the target _bind_update_common:function(target, rule, data){ if (target.setValue) target.setValue((data&&rule)?data[rule]:data); else if (!target.filter){ if (!data && target.clear) target.clear(); else { if (target._check_data_feed(data)) target.setValues(webix.clone(data)); } } else { target.data.silent(function(){ this.filter(rule,data); }); } target.callEvent("onBindApply", [data,rule,this]); } }; //pure data objects webix.DataValue = webix.proto({ name:"DataValue", isVisible:function(){ return true; }, $init:function(config){ if (!config || webix.isUndefined(config.value)) this.data = config||""; var id = (config&&config.id)?config.id:webix.uid(); this._settings = { id:id }; webix.ui.views[id] = this; }, setValue:function(value){ this.data = value; this.callEvent("onChange", [value]); }, getValue:function(){ return this.data; }, refresh:function(){ this.callEvent("onBindRequest"); } }, webix.EventSystem, webix.BaseBind); webix.DataRecord = webix.proto({ name:"DataRecord", isVisible:function(){ return true; }, $init:function(config){ this.data = config||{}; var id = (config&&config.id)?config.id:webix.uid(); this._settings = { id:id }; webix.ui.views[id] = this; }, getValues:function(){ return this.data; }, setValues:function(data, update){ this.data = update?webix.extend(this.data, data, true):data; this.callEvent("onChange", [data]); }, refresh:function(){ this.callEvent("onBindRequest"); } }, webix.EventSystem, webix.BaseBind, webix.AtomDataLoader, webix.Settings); webix.ValueBind={ $init:function(){ this.attachEvent("onChange", this._update_binds); }, _bind_update:function(target, rule, format){ rule = rule || "value"; var data = this.getValue()||""; if (format) data = format(data); if (target.setValue) target.setValue(data); else if (!target.filter){ var pod = {}; pod[rule] = data; if (target._check_data_feed(data)) target.setValues(pod); } else{ target.data.silent(function(){ this.filter(rule,data); }); } target.callEvent("onBindApply", [data,rule,this]); } }; webix.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); } }; webix.CollectionBind={ $init:function(){ this._cursor = null; this.attachEvent("onSelectChange", function(data){ 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", webix.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", webix.bind(function(){ this._cursor = null; },this)); this.data.attachEvent("onIdChange", webix.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); } } }; /* REnders single item. Can be used for elements without datastore, or with complex custom rendering logic @export render */ webix.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 (webix.debug_render) webix.log("Render: "+this.name+"@"+cfg.id); if (!this.callEvent || this.callEvent("onBeforeRender",[this.data])){ if (this.data && !cfg.content){ //it is critical to have this as two commands //its prevent destruction race in Chrome this._dataobj.innerHTML = ""; this._dataobj.innerHTML = this._toHTML(this.data); } if (this.callEvent) this.callEvent("onAfterRender",[]); } return true; } return false; }, sync:function(source){ this._backbone_sync = false; if (source.name != "DataStore"){ if (source.data && source.name == "DataStore"){ source = source.data; } else { this._backbone_sync = true; } } if (this._backbone_sync) source.bind("change", webix.bind(function(data){ if (data.id == this.data.id){ this.data = data.attributes; this.refresh(); } }, this)); else source.attachEvent("onStoreUpdated", webix.bind(function(id){ if (!id || id == this.data.id){ this.data = source.pull[id]; this.refresh(); } }, this)); }, template_setter:webix.template }; webix.SingleRender=webix.proto({ template_setter:function(value){ this.type.template=webix.template(value); }, //convert item to the HTML text _toHTML:function(obj){ var type = this.type; return (type.templateStart?type.templateStart(obj,type):"") + type.template(obj,type) + (type.templateEnd?type.templateEnd(obj,type):""); }, customize:function(obj){ webix.type(this,obj); } }, webix.AtomRender); webix.UIManager = { _view: null, _hotkeys: {}, _focus_time:0, _controls: { 'enter': 13, 'tab': 9, 'esc': 27, 'escape': 27, 'up': 38, 'down': 40, 'left': 37, 'right': 39, 'pgdown': 34, 'pagedown': 34, 'pgup': 33, 'pageup': 33, 'end': 35, 'home': 36, 'insert': 45, 'delete': 46, 'backspace': 8, 'space': 32, 'meta': 91, 'win': 91, 'mac': 91, 'multiply': 106, 'add': 107, 'subtract': 109, 'decimal': 110, 'divide': 111, 'scrollock':145, 'pausebreak':19, 'numlock':144, '5numlocked':12, 'shift':16, 'capslock':20 }, _inputs:{ "input": 1, "button":1, "textarea":1, "select":1 }, _enable: function() { // attaching events here webix.event(document.body, "click", webix.bind(this._focus_click, this)); webix.event(document, "keydown", webix.bind(this._keypress, this)); if (document.body.addEventListener) webix.event(document.body, "focus", this._focus_tab, { capture:true, bind: this }); webix.destructors.push({obj:this}); }, destructor:function(){ webix.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){ //view can be empty view = webix.$$(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 = webix._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]); webix.callEvent("onFocusChange", [view, this._view]); if (this._view && this._view.blur && !only_api) this._view.blur(); this._view = view; if (view && view.focus && !only_api) view.focus(); return true; }, applyChanges: function(element){ var view = this.getFocus(); if (view && view != element && view._applyChanges) view._applyChanges(element); }, hasFocus: function(view) { return (view === this._view) ? true : false; }, _focus: function(e, dont_clear) { var view = webix.html.locate(e, "view_id") || this._focus_was_there; //if html was repainted we can miss the view, so checking last processed one view = webix.$$(view); this._focus_was_there = null; //set timer, to fix issue with Android input focusin webix._focus_time = new Date(); if (view == this._view) return; if (!dont_clear) this._focus_was_there = null; if (view){ view = webix.$$(view); if (this.canFocus(view)){ //[ACTIVECONTENT] focus operations for active content if (view.getNode) view.getNode(e); this.setFocus(view); } } else if (!dont_clear) this.setFocus(null); return true; }, _focus_click:function(e){ // if it was onfocus/onclick less then 100ms behore then we ignore it if ((new Date())-this._focus_time < 100) { this._focus_was_there = null; return false; } return this._focus(e); }, _focus_tab: function(e) { if(!this._inputs[e.target.nodeName.toLowerCase()]) return false; return this._focus(e, true); }, canFocus:function(view){ return view.isVisible() && view.isEnabled(); }, _moveChildFocus: function(check_view){ var focus = this.getFocus(); //we have not focus inside of closing item if (check_view && !this._is_child_of(check_view, focus)) return false; if (!this._focus_logic("getPrev", check_view)) this._view = null; }, _translation_table:{ }, _is_child_of: function(parent, child) { if (!parent) return false; if (!child) return false; while (child) { if (child === parent) return true; child = child.getParentView(); } return false; }, _keypress_timed:function(){ if (this && this.callEvent) this.callEvent("onTimedKeyPress",[]); }, _isNumPad: function(code){ return code < 112 && code>105; }, _keypress: function(e) { var code = e.which || e.keyCode; if(code>95 && code< 106) code -= 48; //numpad support (numbers) code = this._translation_table[code] || code; var ctrl = e.ctrlKey; var shift = e.shiftKey; var alt = e.altKey; var meta = e.metaKey; var codeid = this._keycode(code, ctrl, shift, alt, meta); var view = this.getFocus(); if (view && view.callEvent) { if (view.callEvent("onKeyPress", [code,e]) === false) webix.html.preventEvent(e); if (view.hasEvent("onTimedKeyPress")){ clearTimeout(view._key_press_timeout); view._key_press_timeout = webix.delay(this._keypress_timed, view, [], (view._settings.keyPressTimeout||250)); } } if(!this._isNumPad(code)) codeid = this._keycode(String.fromCharCode(code), ctrl, shift, alt, meta); //flag, that some non-special key was pressed var is_any = !ctrl && !alt && !meta && (code!=9)&&(code!=27)&&(code!=13); if (this._check_keycode(codeid, is_any, e) === false) { webix.html.preventEvent(e); return false; } }, // dir - getNext or getPrev _focus_logic: function(dir) { if (!this.getFocus()) return null; dir = dir || "getNext"; var next = this.getFocus(); var start = next; var marker = webix.uid(); while (true) { next = this[dir](next); // view with focus ability if (next && this.canFocus(next)) return this.setFocus(next); // elements with focus ability not found if (next === start || next.$fmarker == marker) return null; //prevents infinity loop next.$fmarker = marker; } }, _tab_logic:function(view, e){ var mode = !e.shiftKey; webix.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 webix.delay(function(){ webix.UIManager.setFocus(webix.$$(document.activeElement), true); },1); }, getTop: function(id) { var next, view = webix.$$(id); while (view && (next = view.getParentView())) view = next; return view; }, getNext: function(view, _inner_call) { var cells = view.getChildViews(); //tab to first children if (cells.length && !_inner_call) return cells[0]; //unique case - single view without child and parent var parent = view.getParentView(); if (!parent) return view; var p_cells = parent.getChildViews(); if (p_cells.length){ var index = webix.PowerArray.find.call(p_cells, view)+1; while (index < p_cells.length) { //next visible child if (this.canFocus(p_cells[index])) return p_cells[index]; index++; } } //sibling of parent return this.getNext(parent, true); }, getPrev: function(view, _inner_call) { var cells = view.getChildViews(); //last child of last child if (cells.length && _inner_call) return this.getPrev(cells[cells.length - 1], true); if (_inner_call) return view; //fallback from top to bottom var parent = view.getParentView(); if (!parent) return this.getPrev(view, true); var p_cells = parent.getChildViews(); if (p_cells) { var index = webix.PowerArray.find.call(p_cells, view)-1; while (index >= 0) { if (this.canFocus(p_cells[index])) return this.getPrev(p_cells[index], true); index--; } } return parent; }, addHotKey: function(keys, handler, view) { webix.assert(handler, "Hot key handler is not defined"); var pack = this._parse_keys(keys); webix.assert(pack.letter, "Unknown key code"); if (!view) view = null; pack.handler = handler; pack.view = view; var code = this._keycode(pack.letter, pack.ctrl, pack.shift, pack.alt, pack.meta); if (!this._hotkeys[code]) this._hotkeys[code] = []; this._hotkeys[code].push(pack); return keys; }, removeHotKey: function(keys, func, view){ var pack = this._parse_keys(keys); var code = this._keycode(pack.letter, pack.ctrl, pack.shift, pack.alt, pack.meta); if (!func && !view) delete this._hotkeys[code]; else { var t = this._hotkeys[code]; if (t){ for (var i = t.length - 1; i >= 0; i--) { if (view && t[i].view !== view) continue; if (func && t[i].handler !== func) continue; t.splice(i,1); } if (!t.length) delete this._hotkeys[code]; } } }, _keycode: function(code, ctrl, shift, alt, meta) { return code+"_"+["", (ctrl ? '1' : '0'), (shift ? '1' : '0'), (alt ? '1' : '0'), (meta ? '1' : '0')].join(''); }, _check_keycode: function(code, is_any, e){ var focus = this.getFocus(); if (this._hotkeys[code]) return this._process_calls(this._hotkeys[code], focus, e); else if (is_any && this._hotkeys["ANY_0000"]) return this._process_calls(this._hotkeys["ANY_0000"], focus, e); return true; }, _process_calls:function(calls, focus, e){ for (var i = 0; i < calls.length; i++) { var key = calls[i]; var call = false; if ((key.view !== null) && //common hot-key (focus !== key.view) && //hot-key for current view //hotkey for current type of view (typeof(key.view) !== 'string' || !focus || focus.name !== key.view)) continue; var temp_result = key.handler(focus, e); if (!!temp_result === temp_result) return temp_result; } return true; }, _parse_keys: function(keys) { var controls = this._controls; var parts = keys.toLowerCase().split(/[\+\-_]/); var ctrl, shift, alt, meta; ctrl = shift = alt = meta = 0; var letter = ""; for (var i = 0; i < parts.length; i++) { if (parts[i] === 'ctrl') ctrl = 1; else if (parts[i] === 'shift') shift = 1; else if (parts[i] === 'alt') alt = 1; else if (parts[i] === 'command') meta = 1; else { if (controls[parts[i]]) { var code = controls[parts[i]]; if(this._isNumPad(code)) letter = code.toString(); else letter = String.fromCharCode(code); } else { letter = parts[i]; } } } return { letter: letter.toUpperCase(), ctrl: ctrl, shift: shift, alt: alt, meta: meta, debug:keys }; } }; webix.ready(function() { webix.UIManager._enable(); webix.UIManager.addHotKey("enter", function(view, 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]); } }); webix.UIManager.addHotKey("esc", function(view){ if (view){ if (view.editCancel && view._in_edit_mode){ view.editCancel(); return true; } var top = view.getTopParentView(); if (top && top.setPosition) top._hide(); } }); webix.UIManager.addHotKey("shift+tab", webix.UIManager._tab_logic); webix.UIManager.addHotKey("tab", webix.UIManager._tab_logic); }); webix.IdSpace = { $init:function(){ this._elements = {}; this._translate_ids = {}; this.getTopParentView = this._get_self = webix.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(callback){ this._prev_global_col = webix._global_collection; webix._global_collection = this; }, _run_after_inner_init_logic:function(temp){ 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; } webix._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 = webix.ui.apply(webix, arguments); this._run_after_inner_init_logic(); return temp; } }; (function(){ var resize = []; var ui = webix.ui; if (!webix.ui){ ui = webix.ui = function(config, parent, id){ webix._ui_creation = true; var multiset = webix.isArray(config); var node = webix.toNode((config.container||parent)||document.body); // solve problem with non-unique ids if(node._settings) id = _correctId(node, multiset, id); var top_node; var body_child = (node == document.body); if (config._settings || (node && multiset)){ top_node = config; } else { if (node && body_child) config.$topView = true; if (!config._inner) config._inner = {}; top_node = ui._view(config); } if (body_child && !top_node.setPosition && !top_node.$apiOnly) webix.ui._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 && !webix.isArray(top_node)){ id = node; node = node.getParentView(); } //if target supports view adding if (node && node._replace){ //if source supports view removing if (top_node.getParentView && top_node.getParentView()) top_node.getParentView()._remove(top_node); node._replace(top_node, id); } else { var parent = target.$view.parentNode; target.destructor(); _appendDom(parent, top_node, config); } } else webix.assert_error("Not existing parent:"+config.container); } webix._ui_creation = false; return top_node; }; var _appendDom = function(node, top_node, config){ node.appendChild(top_node._viewobj); //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 ) resize.push(top_node._destructor_handler); if (!config.skipResize) top_node.adjust(); }; var _correctId = function(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 = [webix.$$(id)]; _deleteIds(views); return views[0].config.id; } _deleteIds(views); return id; }; var _deleteIds = function(views){ for (var i = views.length - 1; i >= 0; i--){ //remove original id delete webix.ui.views[views[i].config.id]; //create temp id views[i].config.id = "x"+webix.uid(); webix.ui.views[views[i].config.id] = views[i]; //process childs _deleteIds(views[i].getChildViews()); } }; } webix.ui.animate = function(ui, parent, config){ var pobj = webix.$$(parent); if (pobj){ var aniset = config || { type:"slide", direction:"left" }; var d = pobj._viewobj.cloneNode(true); var view = webix.ui(ui, parent); view._viewobj.parentNode.appendChild(d); var line = webix.animate.formLine( view._viewobj, d, aniset ); aniset.callback = function(){ webix.animate.breakLine(line); }; webix.animate(line, aniset); return view; } }; webix.ui.animateView = function(view, stateHandler, config){ view = webix.$$(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 "
"+content+"
"; }; // 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(webix.html.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 = webix.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]; } } }; webix.animate(line, config); return view; } }; /*called in baseview $init for calculate scrollSize*/ webix.ui._detectScrollSize = function(){ var div = webix.html.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 skin = { 110:"air", 120:"aircompact", 130:"clouds", 140:"web", 150:"terrace", 160:"metro", 170:"light", 180:"glamour", 190:"touch", 200:"flat" , 210:"compact", 220:"material", 230: "contrast" }[Math.floor(div.offsetHeight/10)*10]; document.body.removeChild(div); if (skin){ var skinobj = webix.skin[skin]; if (skinobj && skinobj != webix.skin.$active) webix.skin.set(skin); } if (webix.env.$customScroll) return 0; return width; }; webix.ui.scrollSize = ((webix.env.touch||webix.env.$customScroll)?0:17); webix.ready(function(){ var size = webix.ui._detectScrollSize(); webix.ui.scrollSize = webix.env.touch ? 0 : size; }); webix.ui._uid = function(name){ return "$"+name+(this._namecount[name] = (this._namecount[name]||0)+1); }; webix.ui._namecount = {}; webix.ui._fixHeight = function (){ webix.html.addStyle("html, body{ height:100%; }"); document.body.className+=" webix_full_screen"; webix.ui._fixHeight = function(){}; webix.Touch.limit(false); }; webix.ui.resize = function(){ webix.UIManager.applyChanges(); webix.callEvent("onClick",[]); if (!webix.ui.$freeze) for (var i=resize.length - 1; i>=0; i--){ if (resize[i].obj) resize[i].obj.adjust(); } }; webix.ui.each = function(parent, logic, master, include){ if (parent){ var children = include ? [parent] : parent.getChildViews(); for (var i = 0; i < children.length; i++){ if (logic.call((master || webix), children[i]) !== false) webix.ui.each(children[i], logic, master); } } }; webix.event(window, "resize", function() { // check for virtual keyboard if(webix.env.touch && ( webix.edit_open_time && (new Date())-webix.edit_open_time < 750 || webix._focus_time && (new Date())-webix._focus_time < 750)){ //workaround for android chrome bug with scrolling to the focused input if overflow:hidden on container if(webix.env.isWebKit && document.activeElement){ var wactiv = webix.$$(document.activeElement); if (wactiv && wactiv.getInputNode && document.activeElement.scrollIntoView) document.activeElement.scrollIntoView(); } return; } else { webix.ui.resize(); } }); ui._delays = {}; ui.delay = function(config){ webix.ui._delays[config.id] = config; }; ui.hasMethod = function(view, method){ var obj = webix.ui[view]; if (!obj) return false; if (obj.$protoWait) obj = obj.call(webix); return !!webix.ui[view].prototype[method]; }; webix.ui.zIndex = function(){ return webix.ui.zIndexBase++; }; webix.ui.zIndexBase = 100; ui._view = function(config){ webix.assert_config(config); if (config.view){ var view = config.view; webix.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; ix) 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 (webix._responsive_exception){ webix._responsive_exception = false; this.adjust(); } }, resize:function(force){ if (webix._child_sizing_active || webix.ui.$freeze || webix._responsive_tinkery ) return; var parent = this.getParentView(); if (parent){ if (parent.resizeChildren) parent.resizeChildren(); else parent.resize(); } else { this.adjust(); webix.callEvent("onResize",[]); } } }, webix.Settings, webix.Destruction, webix.BaseBind, webix.UIExtension); /* don't render borders itself , but aware of layout , which can set some borders */ webix.protoUI({ name:"view", $init:function(config){ this._set_inner(config); }, //deside, will component use borders or not _set_inner:function(config){ var border_not_set = webix.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 = webix.ui.baseview.prototype.$getSize.call(this, dx, dy); webix.debug_size_box(this, size, true); return size; }, $setSize:function(x,y){ webix.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 webix.ui.baseview.prototype.$setSize.call(this,x,y); } }, webix.ui.baseview); })(); webix.ui.view.call(webix); webix.debug_size_indent = 0; webix.debug_size_step = function(){ var str = ""; for (var i=0; i this._cells.length) target_id = this._cells.length; var prev_node = (this._cells[target_id]||{})._viewobj; webix.PowerArray.insertAt.call(this._cells, new_view, target_id); if (!new_view._settings.hidden) webix.html.insertBefore(new_view._viewobj, prev_node, this._dataobj); } else { source = webix.$$(target_id); target_id = webix.PowerArray.find.call(this._cells, source); webix.assert(target_id!=-1, "Attempt to replace the non-existing view"); var parent = source._viewobj.parentNode; if (parent && !new_view._settings.hidden) parent.insertBefore(new_view._viewobj, source._viewobj); source.destructor(); this._cells[target_id] = new_view; } if (!this._vertical_orientation) this._fix_vertical_layout(new_view); this._cells[target_id]._parent_cell = this; } this.resizeChildren(true); var form = this.elements ? this : this.getFormView(); if (form) form._recollect_elements(); webix.callEvent("onReconstruct",[this]); }, _fix_vertical_layout:function(cell){ cell._viewobj.style.display = "inline-block"; cell._viewobj.style.verticalAlign = "top"; }, addView:function(view, index){ if (webix.isUndefined(index)) index = this._cells.length; var top = this.$$ ? this : this.getTopParentView(); top = (top && top.ui) ? top : webix; return top.ui(view, this, index)._settings.id; }, removeView:function(id){ var view; if (typeof id != "object") view = webix.$$(id) || (this.$$ ? this.$$(id) : null); else view = id; var target = webix.PowerArray.find.call(this._cells, view); if (target >= 0){ if (this._beforeRemoveView) this._beforeRemoveView(target, view); var form = this.elements ? this : this.getFormView(); this._cells.splice(target, 1); if (form) webix.ui.each(view, function(sub){ if (sub.name) delete form.getCleanValues()[sub.config.name]; }, form, true); view.destructor(); this.resizeChildren(true); if (form) form._recollect_elements(); } else webix.assert(false, "Attemp to remove not existing view: "+id); webix.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; webix.html.remove(obj._viewobj); this._hiddencells++; if (!silent && !webix._ui_creation) this.resizeChildren(true); }, _signal_hidden_cells:function(view){ if (view.callEvent) view.callEvent("onViewShow",[]); }, resizeChildren:function(){ if (webix.ui.$freeze) 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]; //maxWidth rule if (x>sizes[1]) nx = sizes[1]; //maxHeight rule if (y>sizes[3]) ny = sizes[3]; this.$setSize(nx,ny); } else this._set_child_size(x,y); if (webix._responsive_exception){ webix._responsive_exception = false; this.resizeChildren(); } webix.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; }, _show:function(obj, settings, silent){ if (!obj._settings.hidden) return; obj._settings.hidden = false; //index of sibling cell, next to which new item will appear var index = this.index(obj)+1; //locate nearest visible cell while (this._cells[index] && this._cells[index]._settings.hidden) index++; var view = this._cells[index] ? this._cells[index]._viewobj : null; webix.html.insertBefore(obj._viewobj, view, (this._dataobj||this._viewobj)); this._hiddencells--; if (!silent){ this.resizeChildren(true); if (obj.refresh) obj.refresh(); } if (obj.callEvent){ obj.callEvent("onViewShow", []); webix.ui.each(obj, this._signal_hidden_cells); } }, showBatch:function(name, mode){ var preserve = typeof mode != "undefined"; mode = mode !== false; if (!preserve){ if (this._settings.visibleBatch == name ) return; this._settings.visibleBatch = name; } else this._settings.visibleBatch = ""; var show = []; for (var i=0; i < this._cells.length; i++){ if (!this._cells[i]._settings.batch) 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=[]; webix.assert(collection,this.name+" was incorrectly defined.

You have missed rows|cols|cells|elements collection"); for (var i=0; i