/* Product Name: dhtmlxSuite Version: 5.2.0 Edition: Professional License: content of this file is covered by DHTMLX Commercial or Enterprise license. Usage without proper license is prohibited. To obtain it contact sales@dhtmlx.com Copyright UAB Dinamenta http://www.dhtmlx.com */ /* Copyright DHTMLX LTD. http://www.dhtmlx.com You allowed to use this component or parts of it under GPL terms To use it on other terms or get Professional edition of the component please contact us at sales@dhtmlx.com */ /* 2014 March 19 */ /* DHX DEPEND FROM FILE 'assert.js'*/ if (!window.dhtmlx) dhtmlx={}; //check some rule, show message as error if rule is not correct dhtmlx.assert = function(test, message){ if (!test) dhtmlx.error(message); }; dhtmlx.assert_enabled=function(){ return false; }; //register names of event, which can be triggered by the object dhtmlx.assert_event = function(obj, evs){ if (!obj._event_check){ obj._event_check = {}; obj._event_check_size = {}; } for (var a in evs){ obj._event_check[a.toLowerCase()]=evs[a]; var count=-1; for (var t in evs[a]) count++; obj._event_check_size[a.toLowerCase()]=count; } }; dhtmlx.assert_method_info=function(obj, name, descr, rules){ var args = []; for (var i=0; i < rules.length; i++) { args.push(rules[i][0]+" : "+rules[i][1]+"\n "+rules[i][2].describe()+(rules[i][3]?"; optional":"")); } return obj.name+"."+name+"\n"+descr+"\n Arguments:\n - "+args.join("\n - "); }; dhtmlx.assert_method = function(obj, config){ for (var key in config) dhtmlx.assert_method_process(obj, key, config[key].descr, config[key].args, (config[key].min||99), config[key].skip); }; dhtmlx.assert_method_process = function (obj, name, descr, rules, min, skip){ var old = obj[name]; if (!skip) obj[name] = function(){ if (arguments.length != rules.length && arguments.length < min) dhtmlx.log("warn","Incorrect count of parameters\n"+obj[name].describe()+"\n\nExpecting "+rules.length+" but have only "+arguments.length); else for (var i=0; i= 0) return true; return false; }; dhtmlx.assert_rule_dimension.describe=function(){ return "{Integer} value must be a positive number"; }; dhtmlx.assert_rule_number=function(check){ if (typeof check == "number") return true; return false; }; dhtmlx.assert_rule_number.describe=function(){ return "{Integer} value must be a number"; }; dhtmlx.assert_rule_function=function(check){ if (typeof check == "function") return true; return false; }; dhtmlx.assert_rule_function.describe=function(){ return "{Function} value must be a custom function"; }; dhtmlx.assert_rule_any=function(check){ return true; }; dhtmlx.assert_rule_any.describe=function(){ return "Any value"; }; dhtmlx.assert_rule_mix=function(a,b){ var t = function(check){ if (a(check)||b(check)) return true; return false; }; t.describe = function(){ return a.describe(); }; return t; }; } /* DHX DEPEND FROM FILE 'dhtmlx.js'*/ /*DHX:Depend assert.js*/ /* Common helpers */ dhtmlx.codebase="./"; //coding helpers dhtmlx.copy = function(source){ var f = dhtmlx.copy._function; f.prototype = source; return new f(); }; dhtmlx.copy._function = function(){}; //copies methods and properties from source to the target dhtmlx.extend = function(target, source){ for (var method in source) target[method] = source[method]; //applying asserts if (dhtmlx.assert_enabled() && source._assert){ target._assert(); target._assert=null; } dhtmlx.assert(target,"Invalid nesting target"); dhtmlx.assert(source,"Invalid nesting source"); //if source object has init code - call init against target if (source._init) target._init(); return target; }; dhtmlx.proto_extend = function(){ var origins = arguments; var compilation = origins[0]; var construct = []; for (var i=origins.length-1; i>0; i--) { if (typeof origins[i]== "function") origins[i]=origins[i].prototype; for (var key in origins[i]){ if (key == "_init") construct.push(origins[i][key]); else if (!compilation[key]) compilation[key] = origins[i][key]; } }; if (origins[0]._init) construct.push(origins[0]._init); compilation._init = function(){ for (var i=0; i handler this._handlers = {}; //hash of event handlers, ID => handler this._map = {}; }, //temporary block event triggering block : function(){ this._events._block = true; }, //re-enable event triggering unblock : function(){ this._events._block = false; }, mapEvent:function(map){ dhtmlx.extend(this._map, map); }, //trigger event callEvent:function(type,params){ if (this._events._block) return true; type = type.toLowerCase(); dhtmlx.assert_event_call(this, type, params); var event_stack =this._events[type.toLowerCase()]; //all events for provided name var return_value = true; if (dhtmlx.debug) //can slowdown a lot dhtmlx.log("info","["+this.name+"] 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; ito){ //can be in case of backward shift-selection var a=to; to=from; from=a; } return this.getIndexRange(from,to); }, //converts range of indexes to array of all IDs between them getIndexRange:function(from,to){ to=Math.min((to||Infinity),this.dataCount()-1); var ret=dhtmlx.toArray(); //result of method is rich-array for (var i=(from||0); i <= to; i++) ret.push(this.item(this.order[i])); return ret; }, //returns total count of elements dataCount:function(){ return this.order.length; }, //returns truy if item with such ID exists exists:function(id){ return !!(this.pull[id]); }, //nextmethod is not visible on component level, check DataMove.move //moves item from source index to the target index move:function(sindex,tindex){ if (sindex<0 || tindex<0){ dhtmlx.error("DataStore::move","Incorrect indexes"); return; } var id = this.idByIndex(sindex); var obj = this.item(id); this.order.removeAt(sindex); //remove at old position //if (sindex data_size){ dhtmlx.log("Warning","DataStore:add","Index of out of bounds"); index = Math.min(this.order.length,index); } if (this.callEvent("onBeforeAdd", [id, obj, index]) === false) return false; if (this.exists(id)) return dhtmlx.error("Not unique ID"); this.pull[id]=obj; this.order.insertAt(id,index); if (this._filter_order){ //adding during filtering //we can't know the location of new item in full dataset, making suggestion //put at end by default var original_index = this._filter_order.length; //put at start only if adding to the start and some data exists if (!index && this.order.length) original_index = 0; this._filter_order.insertAt(id,original_index); } this.callEvent("onafterAdd",[id,index]); //repaint signal this.callEvent("onStoreUpdated",[id,obj,"add"]); return id; }, //removes element from datastore remove:function(id){ //id can be an array of IDs - result of getSelect, for example if (id instanceof Array){ for (var i=0; i < id.length; i++) this.remove(id[i]); return; } if (this.callEvent("onBeforeDelete",[id]) === false) return false; if (!this.exists(id)) return dhtmlx.error("Not existing ID",id); var obj = this.item(id); //save for later event //clear from collections this.order.remove(id); if (this._filter_order) this._filter_order.remove(id); delete this.pull[id]; this.callEvent("onafterdelete",[id]); //repaint signal this.callEvent("onStoreUpdated",[id,obj,"delete"]); }, //deletes all records in datastore clearAll:function(){ //instead of deleting one by one - just reset inner collections this.pull = {}; this.order = dhtmlx.toArray(); this.feed = null; this._filter_order = null; this.callEvent("onClearAll",[]); this.refresh(); }, //converts id to index idByIndex:function(index){ if (index>=this.order.length || index<0) dhtmlx.log("Warning","DataStore::idByIndex Incorrect index"); return this.order[index]; }, //converts index to id indexById:function(id){ var res = this.order.find(id); //slower than idByIndex //if (!this.pull[id]) // dhtmlx.log("Warning","DataStore::indexById Non-existing ID: "+ id); return res; }, //returns ID of next element next:function(id,step){ return this.order[this.indexById(id)+(step||1)]; }, //returns ID of first element first:function(){ return this.order[0]; }, //returns ID of last element last:function(){ return this.order[this.order.length-1]; }, //returns ID of previous element previous:function(id,step){ return this.order[this.indexById(id)-(step||1)]; }, /* sort data in collection by - settings of sorting or by - sorting function dir - "asc" or "desc" or by - property dir - "asc" or "desc" as - type of sortings Sorting function will accept 2 parameters and must return 1,0,-1, based on desired order */ sort:function(by, dir, as){ var sort = by; if (typeof by == "function") sort = {as:by, dir:dir}; else if (typeof by == "string") sort = {by:by, dir:dir, as:as}; var parameters = [sort.by, sort.dir, sort.as]; if (!this.callEvent("onbeforesort",parameters)) return; if (this.order.length){ var sorter = dhtmlx.sort.create(sort); //get array of IDs var neworder = this.getRange(this.first(), this.last()); neworder.sort(sorter); this.order = neworder.map(function(obj){ return this.id(obj); },this); } //repaint self this.refresh(); this.callEvent("onaftersort",parameters); }, /* Filter datasource text - property, by which filter value - filter mask or text - filter method Filter method will receive data object and must return true or false */ filter:function(text,value){ if (!this.callEvent("onBeforeFilter", [text, value])) return; //remove previous filtering , if any if (this._filter_order){ this.order = this._filter_order; delete this._filter_order; } if (!this.order.length) return; //if text not define -just unfilter previous state and exit if (text){ var filter = text; value = value||""; if (typeof text == "string"){ text = dhtmlx.Template.fromHTML(text); value = value.toString().toLowerCase(); filter = function(obj,value){ //default filter - string start from, case in-sensitive return text(obj).toLowerCase().indexOf(value)!=-1; }; } var neworder = dhtmlx.toArray(); for (var i=0; i < this.order.length; i++){ var id = this.order[i]; if (filter(this.item(id),value)) neworder.push(id); } //set new order of items, store original this._filter_order = this.order; this.order = neworder; } //repaint self this.refresh(); this.callEvent("onAfterFilter", []); }, /* Iterate through collection */ each:function(method,master){ for (var i=0; ib?1:(ab?1:(ab?1:(a value // {obj.attr} => named attribute or value of sub-tag in case of xml // {obj.attr?some:other} conditional output // {-obj => sub-template str=(str||"").toString(); str=str.replace(/[\r\n]+/g,"\\n"); 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(obj):\"\")+\""); str=str.replace(/\{obj\.([^}]*)\}/g,"\"+obj.$1+\""); str=str.replace(/#([a-z0-9_]+)#/gi,"\"+obj.$1+\""); str=str.replace(/\{obj\}/g,"\"+obj+\""); str=str.replace(/\{-obj/g,"{obj"); str=str.replace(/\{-common/g,"{common"); str="return \""+str+"\";"; return this._cache[str]= Function("obj","common",str); } }; dhtmlx.Type={ /* adds new template-type obj - object to which template will be added data - properties of template */ add:function(obj, data){ //auto switch to prototype, if name of class was provided if (!obj.types && obj.prototype.types) obj = obj.prototype; //if (typeof data == "string") // data = { template:data }; if (dhtmlx.assert_enabled()) this.assert_event(data); var name = data.name||"default"; //predefined templates - autoprocessing this._template(data); this._template(data,"edit"); this._template(data,"loading"); obj.types[name]=dhtmlx.extend(dhtmlx.extend({},(obj.types[name]||this._default)),data); return name; }, //default template value - basically empty box with 5px margin _default:{ css:"default", template:function(){ return ""; }, template_edit:function(){ return ""; }, template_loading:function(){ return "..."; }, width:150, height:80, margin:5, padding:0 }, //template creation helper _template:function(obj,name){ name = "template"+(name?("_"+name):""); var data = obj[name]; //if template is a string - check is it plain string or reference to external content if (data && (typeof data == "string")){ if (data.indexOf("->")!=-1){ data = data.split("->"); switch(data[0]){ case "html": //load from some container on the page data = dhtmlx.html.getValue(data[1]).replace(/\"/g,"\\\""); break; case "http": //load from external file data = new dhtmlx.ajax().sync().get(data[1],{uid:(new Date()).valueOf()}).responseText; break; default: //do nothing, will use template as is break; } } obj[name] = dhtmlx.Template.fromHTML(data); } } }; /* DHX DEPEND FROM FILE 'single_render.js'*/ /* REnders single item. Can be used for elements without datastore, or with complex custom rendering logic @export render */ /*DHX:Depend template.js*/ dhtmlx.SingleRender={ _init:function(){ }, //convert item to the HTML text _toHTML:function(obj){ /* this one doesn't support per-item-$template it has not sense, because we have only single item per object */ return this.type._item_start(obj,this.type)+this.type.template(obj,this.type)+this.type._item_end; }, //render self, by templating data object render:function(){ if (!this.callEvent || this.callEvent("onBeforeRender",[this.data])){ if (this.data) this._dataobj.innerHTML = this._toHTML(this.data); if (this.callEvent) this.callEvent("onAfterRender",[]); } } }; /* DHX DEPEND FROM FILE 'tooltip.js'*/ /* UI: Tooltip @export show hide */ /*DHX:Depend tooltip.css*/ /*DHX:Depend template.js*/ /*DHX:Depend single_render.js*/ dhtmlx.ui.Tooltip=function(container){ this.name = "Tooltip"; if (dhtmlx.assert_enabled()) this._assert(); if (typeof container == "string"){ container = { template:container }; } dhtmlx.extend(this, dhtmlx.Settings); dhtmlx.extend(this, dhtmlx.SingleRender); this._parseSettings(container,{ type:"default", dy:0, dx:20 }); //create container for future tooltip this._dataobj = this._obj = document.createElement("DIV"); this._obj.className="dhx_tooltip"; dhtmlx.html.insertBefore(this._obj,document.body.firstChild); }; dhtmlx.ui.Tooltip.prototype = { //show tooptip //pos - object, pos.x - left, pox.y - top show:function(data,pos){ if (this._disabled) return; //render sefl only if new data was provided if (this.data!=data){ this.data=data; this.render(data); } //show at specified position this._obj.style.top = pos.y+this._settings.dy+"px"; this._obj.style.left = pos.x+this._settings.dx+"px"; this._obj.style.display="block"; }, //hide tooltip hide:function(){ this.data=null; //nulify, to be sure that on next show it will be fresh-rendered this._obj.style.display="none"; }, disable:function(){ this._disabled = true; }, enable:function(){ this._disabled = false; }, types:{ "default":dhtmlx.Template.fromHTML("{obj.id}") }, template_item_start:dhtmlx.Template.empty, template_item_end:dhtmlx.Template.empty }; /* DHX DEPEND FROM FILE 'autotooltip.js'*/ /* Behavior: AutoTooltip - links tooltip to data driven item */ /*DHX:Depend tooltip.js*/ dhtmlx.AutoTooltip = { tooltip_setter:function(value){ var t = new dhtmlx.ui.Tooltip(value); this.attachEvent("onMouseMove",function(id,e){ //show tooltip on mousemove t.show(this.get(id),dhtmlx.html.pos(e)); }); this.attachEvent("onMouseOut",function(id,e){ //hide tooltip on mouseout t.hide(); }); this.attachEvent("onMouseMoving",function(id,e){ //hide tooltip just after moving start t.hide(); }); return t; } }; /* DHX DEPEND FROM FILE 'compatibility.js'*/ /* Collection of compatibility hacks */ /*DHX:Depend dhtmlx.js*/ dhtmlx.compat=function(name, obj){ //check if name hash present, and applies it when necessary if (dhtmlx.compat[name]) dhtmlx.compat[name](obj); }; /* DHX DEPEND FROM FILE 'compatibility_layout.js'*/ /*DHX:Depend dhtmlx.js*/ /*DHX:Depend compatibility.js*/ if (!dhtmlx.attaches) dhtmlx.attaches = {}; dhtmlx.attaches.attachAbstract=function(name, conf){ var obj = document.createElement("DIV"); obj.id = "CustomObject_"+dhtmlx.uid(); obj.style.width = "100%"; obj.style.height = "100%"; obj.cmp = "grid"; document.body.appendChild(obj); this.attachObject(obj.id); conf.container = obj.id; var that = this.vs[this.av]; that.grid = new window[name](conf); that.gridId = obj.id; that.gridObj = obj; that.grid.setSizes = function(){ if (this.resize) this.resize(); else this.render(); }; var method_name="_viewRestore"; return this.vs[this[method_name]()].grid; }; dhtmlx.attaches.attachDataView = function(conf){ return this.attachAbstract("dhtmlXDataView",conf); }; dhtmlx.attaches.attachChart = function(conf){ return this.attachAbstract("dhtmlXChart",conf); }; dhtmlx.compat.layout = function(){};