/* @license webix UI v.4.3.0 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 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* jshint ignore:start */ (function (self) { var now = typeof setImmediate !== 'undefined' ? setImmediate : function(cb) { setTimeout(cb, 0) } /** * @constructor */ function promise(fn, er) { var self = this self.promise = self self.state = 'pending' self.val = null self.fn = fn || null self.er = er || null self.next = []; } promise.prototype.resolve = function (v) { var self = this if (self.state === 'pending') { self.val = v self.state = 'resolving' now(function () { self.fire() }) } } promise.prototype.reject = function (v) { var self = this if (self.state === 'pending') { self.val = v self.state = 'rejecting' now(function () { self.fire() }) } } promise.prototype.then = function (fn, er) { var self = this var p = new promise(fn, er) self.next.push(p) if (self.state === 'resolved') { p.resolve(self.val) } if (self.state === 'rejected') { p.reject(self.val) } return p } promise.prototype.fail = function (er) { return this.then(null, er) } promise.prototype.finish = function (type) { var self = this self.state = type if (self.state === 'resolved') { for (var i = 0; i < self.next.length; i++) self.next[i].resolve(self.val); } if (self.state === 'rejected') { for (var i = 0; i < self.next.length; i++) self.next[i].reject(self.val); if (webix.assert && !self.next.length) throw(self.val); } } // ref : reference to 'then' function // cb, ec, cn : successCallback, failureCallback, notThennableCallback promise.prototype.thennable = function (ref, cb, ec, cn, val) { var self = this val = val || self.val if (typeof val === 'object' && typeof ref === 'function') { try { // cnt protects against abuse calls from spec checker var cnt = 0 ref.call(val, function(v) { if (cnt++ !== 0) return cb(v) }, function (v) { if (cnt++ !== 0) return ec(v) }) } catch (e) { ec(e) } } else { cn(val) } } promise.prototype.fire = function () { var self = this // check if it's a thenable var ref; try { ref = self.val && self.val.then } catch (e) { self.val = e self.state = 'rejecting' return self.fire() } self.thennable(ref, function (v) { self.val = v self.state = 'resolving' self.fire() }, function (v) { self.val = v self.state = 'rejecting' self.fire() }, function (v) { self.val = v if (self.state === 'resolving' && typeof self.fn === 'function') { try { self.val = self.fn.call(undefined, self.val) } catch (e) { self.val = e return self.finish('rejected') } } if (self.state === 'rejecting' && typeof self.er === 'function') { try { self.val = self.er.call(undefined, self.val) self.state = 'resolving' } catch (e) { self.val = e return self.finish('rejected') } } if (self.val === self) { self.val = TypeError() return self.finish('rejected') } self.thennable(ref, function (v) { self.val = v self.finish('resolved') }, function (v) { self.val = v self.finish('rejected') }, function (v) { self.val = v self.state === 'resolving' ? self.finish('resolved') : self.finish('rejected') }) }) } promise.prototype.done = function () { if (this.state = 'rejected' && !this.next) { throw this.val } return null } promise.prototype.nodeify = function (cb) { if (typeof cb === 'function') return this.then(function (val) { try { cb(null, val) } catch (e) { setImmediate(function () { throw e }) } return val }, function (val) { try { cb(val) } catch (e) { setImmediate(function () { throw e }) } return val }) return this } promise.prototype.spread = function (fn, er) { return this.all().then(function (list) { return typeof fn === 'function' && fn.apply(null, list) }, er) } promise.prototype.all = function() { var self = this return this.then(function(list){ var p = new promise() if(!(list instanceof Array)) { p.reject(TypeError) return p } var cnt = 0 var target = list.length function done() { if (++cnt === target) p.resolve(list) } for(var i=0, l=list.length; 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)=="!"){ temp_res.push({pos: pos, str: search, fn: function(obj,common){ s = s.substr(1); if(s.indexOf(".")!= -1) obj = webix.CodeParser.collapseNames(obj); // apply complex properties return webix.template.escape(obj[s.substr(1)]); }}); } 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; this.callEvent("onBeforeLoad",[]); 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); this.callEvent("onBeforeLoad",[]); 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); this.callEvent("onBeforeLoad",[]); 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(){ // 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; } } } 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", 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