odoo/addons/web_graph/static/lib/dhtmlxGraph/sources/dhtmlxchart.js

3498 lines
98 KiB
JavaScript

/*
2010 July 02
*/
/* 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<rules.length; i++)
if (!rules[i][3] && !rules[i][2](arguments[i]))
dhtmlx.log("warn","Incorrect method call\n"+obj[name].describe()+"\n\nActual value of "+(i+1)+" parameter: {"+(typeof arguments[i])+"} "+arguments[i]);
return old.apply(this, arguments);
};
obj[name].describe = function(){ return dhtmlx.assert_method_info(obj, name, descr, rules); };
};
dhtmlx.assert_event_call = function(obj, name, args){
if (obj._event_check){
if (!obj._event_check[name])
dhtmlx.log("warn","Not expected event call :"+name);
else if (dhtmlx.isNotDefined(args))
dhtmlx.log("warn","Event without parameters :"+name);
else if (obj._event_check_size[name] != args.length)
dhtmlx.log("warn","Incorrect event call, expected "+obj._event_check_size[name]+" parameter(s), but have "+args.length +" parameter(s), for "+name+" event");
}
};
dhtmlx.assert_event_attach = function(obj, name){
if (obj._event_check && !obj._event_check[name])
dhtmlx.log("warn","Unknown event name: "+name);
};
//register names of properties, which can be used in object's configuration
dhtmlx.assert_property = function(obj, evs){
if (!obj._settings_check)
obj._settings_check={};
dhtmlx.extend(obj._settings_check, evs);
};
//check all options in collection, against list of allowed properties
dhtmlx.assert_check = function(data,coll){
if (typeof data == "object"){
for (var key in data){
dhtmlx.assert_settings(key,data[key],coll);
}
}
};
//check if type and value of property is the same as in scheme
dhtmlx.assert_settings = function(mode,value,coll){
coll = coll || this._settings_check;
//if value is not in collection of defined ones
if (coll){
if (!coll[mode]) //not registered property
return dhtmlx.log("warn","Unknown propery: "+mode);
var descr = "";
var error = "";
var check = false;
for (var i=0; i<coll[mode].length; i++){
var rule = coll[mode][i];
if (typeof rule == "string")
continue;
if (typeof rule == "function")
check = check || rule(value);
else if (typeof rule == "object" && typeof rule[1] == "function"){
check = check || rule[1](value);
if (check && rule[2])
dhtmlx["assert_check"](value, rule[2]); //temporary fix , for sources generator
}
if (check) break;
}
if (!check )
dhtmlx.log("warn","Invalid configuration\n"+dhtmlx.assert_info(mode,coll)+"\nActual value: {"+(typeof value)+"} "+value);
}
};
dhtmlx.assert_info=function(name, set){
var ruleset = set[name];
var descr = "";
var expected = [];
for (var i=0; i<ruleset.length; i++){
if (typeof rule == "string")
descr = ruleset[i];
else if (ruleset[i].describe)
expected.push(ruleset[i].describe());
else if (ruleset[i][1] && ruleset[i][1].describe)
expected.push(ruleset[i][1].describe());
}
return "Property: "+name+", "+descr+" \nExpected value: \n - "+expected.join("\n - ");
};
if (dhtmlx.assert_enabled()){
dhtmlx.assert_rule_color=function(check){
if (typeof check != "string") return false;
if (check.indexOf("#")!==0) return false;
if (check.substr(1).replace(/[0-9A-F]/gi,"")!=="") return false;
return true;
};
dhtmlx.assert_rule_color.describe = function(){
return "{String} Value must start from # and contain hexadecimal code of color";
};
dhtmlx.assert_rule_template=function(check){
if (typeof check == "function") return true;
if (typeof check == "string") return true;
return false;
};
dhtmlx.assert_rule_template.describe = function(){
return "{Function},{String} Value must be a function which accepts data object and return text string, or a sting with optional template markers";
};
dhtmlx.assert_rule_boolean=function(check){
if (typeof check == "boolean") return true;
return false;
};
dhtmlx.assert_rule_boolean.describe = function(){
return "{Boolean} true or false";
};
dhtmlx.assert_rule_object=function(check, sub){
if (typeof check == "object") return true;
return false;
};
dhtmlx.assert_rule_object.describe = function(){
return "{Object} Configuration object";
};
dhtmlx.assert_rule_string=function(check){
if (typeof check == "string") return true;
return false;
};
dhtmlx.assert_rule_string.describe = function(){
return "{String} Plain string";
};
dhtmlx.assert_rule_htmlpt=function(check){
return !!dhtmlx.toNode(check);
};
dhtmlx.assert_rule_htmlpt.describe = function(){
return "{Object},{String} HTML node or ID of HTML Node";
};
dhtmlx.assert_rule_notdocumented=function(check){
return false;
};
dhtmlx.assert_rule_notdocumented.describe = function(){
return "This options wasn't documented";
};
dhtmlx.assert_rule_key=function(obj){
var t = function (check){
return obj[check];
};
t.describe=function(){
var opts = [];
for(var key in obj)
opts.push(key);
return "{String} can take one of next values: "+opts.join(", ");
};
return t;
};
dhtmlx.assert_rule_dimension=function(check){
if (check*1 == check && !isNaN(check) && check >= 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.version="3.0";
dhtmlx.codebase="./";
//coding helpers
//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;
}
//if source object has init code - call init against target
if (source._init)
target._init();
return target;
};
//creates function with specified "this" pointer
dhtmlx.bind=function(functor, object){
return function(){ return functor.apply(object,arguments); };
};
//loads module from external js file
dhtmlx.require=function(module){
if (!dhtmlx._modules[module]){
dhtmlx.assert(dhtmlx.ajax,"load module is required");
//load and exec the required module
dhtmlx.exec( dhtmlx.ajax().sync().get(dhtmlx.codebase+module).responseText );
dhtmlx._modules[module]=true;
}
};
dhtmlx._modules = {}; //hash of already loaded modules
//evaluate javascript code in the global scoope
dhtmlx.exec=function(code){
if (window.execScript) //special handling for IE
window.execScript(code);
else window.eval(code);
};
/*
creates method in the target object which will transfer call to the source object
if event parameter was provided , each call of method will generate onBefore and onAfter events
*/
dhtmlx.methodPush=function(object,method,event){
return function(){
var res = false;
//if (!event || this.callEvent("onBefore"+event,arguments)){ //not used anymore, probably can be removed
res=object[method].apply(object,arguments);
// if (event) this.callEvent("onAfter"+event,arguments);
//}
return res; //result of wrapped method
};
};
//check === undefined
dhtmlx.isNotDefined=function(a){
return typeof a == "undefined";
};
//delay call to after-render time
dhtmlx.delay=function(method, obj, params){
setTimeout(function(){
var ret = method.apply(obj,params);
method = obj = params = null;
return ret;
},1);
};
//common helpers
//generates unique ID (unique per window, nog GUID)
dhtmlx.uid = function(){
if (!this._seed) this._seed=(new Date).valueOf(); //init seed with timestemp
this._seed++;
return this._seed;
};
//resolve ID as html object
dhtmlx.toNode = function(node){
if (typeof node == "string") return document.getElementById(node);
return node;
};
//adds extra methods for the array
dhtmlx.toArray = function(array){
return dhtmlx.extend((array||[]),dhtmlx.PowerArray);
};
//resolve function name
dhtmlx.toFunctor=function(str){
return (typeof(str)=="string") ? eval(str) : str;
};
//dom helpers
//hash of attached events
dhtmlx._events = {};
//attach event to the DOM element
dhtmlx.event=function(node,event,handler,master){
node = dhtmlx.toNode(node);
var id = dhtmlx.uid();
dhtmlx._events[id]=[node,event,handler]; //store event info, for detaching
if (master)
handler=dhtmlx.bind(handler,master);
//use IE's of FF's way of event's attaching
if (node.addEventListener)
node.addEventListener(event, handler, false);
else if (node.attachEvent)
node.attachEvent("on"+event, handler);
return id; //return id of newly created event, can be used in eventRemove
};
//remove previously attached event
dhtmlx.eventRemove=function(id){
if (!id) return;
dhtmlx.assert(this._events[id],"Removing non-existing event");
var ev = dhtmlx._events[id];
//browser specific event removing
if (ev[0].removeEventListener)
ev[0].removeEventListener(ev[1],ev[2],false);
else if (ev[0].detachEvent)
ev[0].detachEvent("on"+ev[1],ev[2]);
delete this._events[id]; //delete all traces
};
//debugger helpers
//anything starting from error or log will be removed during code compression
//add message in the log
dhtmlx.log = function(type,message,details){
if (window.console && console.log){
type=type.toLowerCase();
if (window.console[type])
window.console[type](message);
else
window.console.log(type +": "+message);
if (details)
window.console.log(details);
}
};
//register rendering time from call point
dhtmlx.log_full_time = function(name){
dhtmlx._start_time_log = new Date();
dhtmlx.log("Info","Timing start ["+name+"]");
window.setTimeout(function(){
var time = new Date();
dhtmlx.log("Info","Timing end ["+name+"]:"+(time.valueOf()-dhtmlx._start_time_log.valueOf())/1000+"s");
},1);
};
//register execution time from call point
dhtmlx.log_time = function(name){
var fname = "_start_time_log"+name;
if (!dhtmlx[fname]){
dhtmlx[fname] = new Date();
dhtmlx.log("Info","Timing start ["+name+"]");
} else {
var time = new Date();
dhtmlx.log("Info","Timing end ["+name+"]:"+(time.valueOf()-dhtmlx[fname].valueOf())/1000+"s");
dhtmlx[fname] = null;
}
};
//log message with type=error
dhtmlx.error = function(message,details){
dhtmlx.log("Error",message,details);
};
//event system
dhtmlx.EventSystem={
_init:function(){
this._events = {}; //hash of event handlers, name => 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<event_stack.length; i++)
/*
Call events one by one
If any event return false - result of whole event will be false
Handlers which are not returning anything - counted as positive
*/
if (event_stack[i].apply(this,(params||[]))===false) return_value=false;
if (this._map[type] && !this._map[type].callEvent(type,params))
return_value = false;
return return_value;
},
//assign handler for some named event
attachEvent:function(type,functor,id){
type=type.toLowerCase();
dhtmlx.assert_event_attach(this, type);
id=id||dhtmlx.uid(); //ID can be used for detachEvent
functor = dhtmlx.toFunctor(functor); //functor can be a name of method
var event_stack=this._events[type]||dhtmlx.toArray();
//save new event handler
event_stack.push(functor);
this._events[type]=event_stack;
this._handlers[id]={ f:functor,t:type };
return id;
},
//remove event handler
detachEvent:function(id){
var type=this._handlers[id].t;
var functor=this._handlers[id].f;
//remove from all collections
var event_stack=this._events[type];
event_stack.remove(functor);
delete this._handlers[id];
}
};
//array helper
//can be used by dhtmlx.toArray()
dhtmlx.PowerArray={
//remove element at specified position
removeAt:function(pos,len){
if (pos>=0) this.splice(pos,(len||1));
},
//find element in collection and remove it
remove:function(value){
this.removeAt(this.find(value));
},
//add element to collection at specific position
insertAt:function(data,pos){
if (!pos && pos!==0) //add to the end by default
this.push(data);
else {
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 (i=0; i<this.length; i++)
if (data==this[i]) return i;
return -1;
},
//execute some method for each element of array
each:function(functor,master){
for (var i=0; i < this.length; i++)
functor.call((master||this),this[i]);
},
//create new array from source, by using results of functor
map:function(functor,master){
for (var i=0; i < this.length; i++)
this[i]=functor.call((master||this),this[i]);
return this;
}
};
//environment detection
if (navigator.userAgent.indexOf('Opera') != -1)
dhtmlx._isOpera=true;
else{
//very rough detection, but it is enough for current goals
dhtmlx._isIE=!!document.all;
dhtmlx._isFF=!document.all;
dhtmlx._isWebKit=(navigator.userAgent.indexOf("KHTML")!=-1);
if (navigator.appVersion.indexOf("MSIE 8.0")!= -1 && document.compatMode != "BackCompat")
dhtmlx._isIE=8;
}
//store maximum used z-index
dhtmlx.zIndex={ drag : 10000 };
//html helpers
dhtmlx.html={
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 = dhtmlx.toNode(node);
if (!node) return "";
return dhtmlx.isNotDefined(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.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){
e=e||event;
var trg=e.target||e.srcElement;
while (trg && trg.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) };
} 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};
}
},
//returns position of event
pos:function(ev){
ev = ev || event;
if(ev.pageX || ev.pageY) //FF, KHTML
return {x:ev.pageX, y:ev.pageY};
//IE
var d = ((dhtmlx._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();
dhtmlx.html.stopEvent(e);
},
//stop event bubbling
stopEvent:function(e){
(e||event).cancelBubble=true;
return false;
},
//add css class to the node
addCss:function(node,name){
node.className+=" "+name;
},
//remove css class from the node
removeCss:function(node,name){
node.className=node.className.replace(RegExp(name,"g"),"");
}
};
//autodetect codebase folder
(function(){
var temp = document.getElementsByTagName("SCRIPT"); //current script, most probably
dhtmlx.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);
dhtmlx.codebase = temp.slice(0, temp.length).join("/")+"/";
}
})();
dhtmlx.ui={};
/* DHX DEPEND FROM FILE 'destructor.js'*/
/*
Behavior:Destruction
@export
destructor
*/
/*DHX:Depend dhtmlx.js*/
dhtmlx.Destruction = {
_init:function(){
//register self in global list of destructors
dhtmlx.destructors.push(this);
},
//will be called automatically on unload, can be called manually
//simplifies job of GC
destructor:function(){
this.destructor=function(){}; //destructor can be called only once
//html collection
this._htmlmap = null;
this._htmlrows = null;
//temp html element, used by toHTML
if (this._html)
document.body.appendChild(this._html); //need to attach, for IE's GC
this._html = null;
if (this._obj) {
this._obj.innerHTML="";
this._obj._htmlmap = null;
}
this._obj = this._dataobj=null;
this.data = null;
this._events = this._handlers = {};
}
};
//global list of destructors
dhtmlx.destructors = [];
dhtmlx.event(window,"unload",function(){
//call all registered destructors
for (var i=0; i<dhtmlx.destructors.length; i++)
dhtmlx.destructors[i].destructor();
dhtmlx.destructors = null;
//detach all known DOM events
for (var a in dhtmlx._events){
var ev = dhtmlx._events[a];
if (ev[0].removeEventListener)
ev[0].removeEventListener(ev[1],ev[2],false);
else if (ev[0].detachEvent)
ev[0].detachEvent("on"+ev[1],ev[2]);
delete dhtmlx._events[a];
}
});
/* DHX DEPEND FROM FILE 'math.js'*/
dhtmlx.math = {};
dhtmlx.math._toHex=["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"];
dhtmlx.math.toHex = function(number, length){
number=parseInt(number,10);
str = "";
while (number>0){
str=this._toHex[number%16]+str;
number=Math.floor(number/16);
}
while (str.length <length)
str = "0"+str;
return str;
};
/* DHX DEPEND FROM FILE 'map.js'*/
/*DHX:Depend dhtmlx.js*/
dhtmlx.ui.Map = function(key){
this.name = "Map";
this._id = "map_"+dhtmlx.uid();
this._key = key;
this._map = [];
};
dhtmlx.ui.Map.prototype = {
addRect: function(id,points) {
this._createMapArea(id,"RECT",points);
},
addPoly: function(id,points) {
this._createMapArea(id,"POLY",points);
},
_createMapArea:function(id,shape,coords){
this._map.push("<area "+this._key+"='"+id+"' shape='"+shape+"' coords='"+coords.join()+"'></area>");
},
addSector:function(id,alpha0,alpha1,x,y,R,ky){
var points = [];
points.push(x);
points.push(Math.floor(y*ky));
for(var i = alpha0; i < alpha1; i+=Math.PI/18){
points.push(Math.floor(x+R*Math.cos(i)));
points.push(Math.floor((y+R*Math.sin(i))*ky));
}
points.push(Math.floor(x+R*Math.cos(alpha1)));
points.push(Math.floor((y+R*Math.sin(alpha1))*ky));
points.push(x);
points.push(Math.floor(y*ky));
return this.addPoly(id,points);
},
render:function(obj){
var d = dhtmlx.html.create("DIV");
d.style.cssText="position:absolute; width:100%; height:100%; top:0px; left:0px;";
obj.appendChild(d);
var src = dhtmlx._isIE?"":"src='data:image/gif;base64,R0lGODlhEgASAIAAAP///////yH5BAUUAAEALAAAAAASABIAAAIPjI+py+0Po5y02ouz3pwXADs='";
d.innerHTML="<map id='"+this._id+"' name='"+this._id+"'>"+this._map.join("\n")+"</map><img "+src+" class='dhx_map_img' usemap='#"+this._id+"'>";
obj._htmlmap = d; //for clearing routine
this._map = [];
}
};
/* DHX DEPEND FROM FILE 'chart_graph.js'*/
/*DHX:Depend map.js*/
if (!dhtmlx.chart) dhtmlx.chart = {};
dhtmlx.chart.line = {
/**
* renders a graphic
* @param: ctx - canvas object
* @param: data - object those need to be displayed
* @param: width - the width of the container
* @param: height - the height of the container
*/
pvt_render_line:function(ctx, data, width, height){
var maxValue,minValue;
/*necessary if maxValue - minValue < 0*/
var valueFactor;
/*maxValue - minValue*/
var relValue;
/*necessary to add events and tooltips*/
var map = new dhtmlx.ui.Map(this._id);
/*scales*/
var yax = !!this._settings.yAxis;
var xax = !!this._settings.xAxis;
if(yax&&(typeof this._settings.yAxis.end!="undefied")&&(typeof this._settings.yAxis.start!="undefied")&&this._settings.yAxis.step){
maxValue = parseFloat(this._settings.yAxis.end);
minValue = parseFloat(this._settings.yAxis.start);
}
else{
maxValue = this.max(this._settings.value);
minValue = this.min(this._settings.value);
}
/*a vertical scale*/
this._drawYAxis(ctx,data,width,height,minValue,maxValue);
/*necessary for automatic scale*/
if(yax){
maxValue = parseFloat(this._settings.yAxis.end);
minValue = parseFloat(this._settings.yAxis.start);
}
var topPadding = parseInt(this._settings.padding.top,10);
var leftPadding = parseInt(this._settings.padding.left,10);
var bottomPadding = parseInt(this._settings.padding.bottom,10);
var rightPadding = parseInt(this._settings.padding.right,10);
/*available height*/
var total_height = height-topPadding-bottomPadding;
/*unit calculation (y_position = value*unit)*/
var relativeValues = this._getRelativeValue(minValue,maxValue);
relValue = relativeValues[0];
valueFactor = relativeValues[1];
var unit = total_height/relValue;
if(!yax){
/*defines start value for better representation of small values*/
var startValue = (unit>10?unit:10);
unit = (total_height-startValue)/relValue;
}
/*a space available for a single item*/
var cellWidth = Math.round((width - leftPadding - rightPadding)/data.length);
/*the value that defines the map area position*/
var areaPos = Math.floor(cellWidth/2);
/*drawing all items*/
if (data.length) {
/*gets the vertical coordinate of an item*/
var _graph_point = function(data){
/*the real value of an object*/
var value = this._settings.value(data);
/*a relative value*/
var v = (parseFloat(value) - minValue)*valueFactor;
if(!yax)
v += startValue/unit;
/*a vertical coordinate*/
var y = height - Math.floor(unit*v) - bottomPadding;
/*the limit of the minimum value is the minimum visible value*/
if(v<0)
y = height - topPadding;
/*the limit of the maximum value*/
if(value > maxValue)
y = topPadding;
/*the limit of the minimum value*/
if(value < minValue)
y = height - bottomPadding;
return y;
};
/*the vertical position of the first item*/
var y1 = _graph_point.call(this,data[0]);
/*drawing the previous item and the line between to items*/
for(var i=1; i <= data.length;i ++){
/*horizontal positions of the previous and current items (0.5 - the fix for line width)*/
var x1 = Math.floor(cellWidth*(i-0.5)) - 0.5 + leftPadding;
var x2 = Math.floor(cellWidth*(i+0.5)) - 0.5 + leftPadding;
/*sets label on the horizontal scale*/
this._drawXAxisLabel(x1,data[i-1]);
/*draws a vertical line for the horizontal scale*/
this._drawXAxisLine(ctx,x1,height-bottomPadding,topPadding);
/*a line between items*/
if (data.length!=i){
var y2 = _graph_point.call(this,data[i]);
this._drawLine(ctx,x1,y1,x2,y2,this._settings.line.color(data[i-1]),this._settings.line.width);
}
/*draws prevous item*/
this._drawGraphicItem(ctx,x1,y1,data[i-1]);
/*creates map area*/
map.addRect(data[i-1].id,[x1-areaPos,y1-areaPos,x1+areaPos,y1+areaPos]);
y1=y2;
}
_graph_point = null;
}
/*horizontal scale*/
this._drawXAxis(ctx,data,width,height);
map.render(this._obj);
},
/**
* draws an item and its label
* @param: ctx - canvas object
* @param: x0 - the x position of a circle
* @param: y0 - the y position of a circle
* @param: obj - data object
*/
_drawGraphicItem:function(ctx,x0,y0,obj){
var R = parseInt(this._settings.item.radius,10);
ctx.lineWidth = parseInt(this._settings.item.borderWidth,10);
ctx.fillStyle = this._settings.item.color(obj);
ctx.strokeStyle = this._settings.item.borderColor(obj);
ctx.beginPath();
ctx.arc(x0,y0,R,0,Math.PI*2,true);
ctx.fill();
ctx.stroke();
/*item label*/
this.renderTextAt(false, true, x0,y0-R-this._settings.labelOffset,this._settings.label(obj));
}
};
/* DHX DEPEND FROM FILE 'chart_bar.js'*/
/*DHX:Depend map.js*/
if (!dhtmlx.chart) dhtmlx.chart = {};
dhtmlx.chart.bar = {
/**
* renders a bar chart
* @param: ctx - canvas object
* @param: data - object those need to be displayed
* @param: x - the width of the container
* @param: y - the height of the container
*/
pvt_render_bar:function(ctx, data, x, y){
var maxValue,minValue;
/*necessary if maxValue - minValue < 0*/
var valueFactor;
/*maxValue - minValue*/
var relValue;
var map = new dhtmlx.ui.Map(this._id);
var yax = !!this._settings.yAxis;
var xax = !!this._settings.xAxis;
if(yax&&(typeof this._settings.yAxis.end!="undefied")&&(typeof this._settings.yAxis.start!="undefied")&&this._settings.yAxis.step){
maxValue = parseFloat(this._settings.yAxis.end);
minValue = parseFloat(this._settings.yAxis.start);
}
else{
maxValue = this.max(this._settings.value);
minValue = this.min(this._settings.value);
}
/*a vertical scale*/
this._drawYAxis(ctx,data,x,y,minValue,maxValue);
/*necessary for automatic scale*/
if(yax){
maxValue = parseFloat(this._settings.yAxis.end);
minValue = parseFloat(this._settings.yAxis.start);
}
var yPadding = parseInt(this._settings.padding.top,10);
var xPadding = parseInt(this._settings.padding.left,10);
var bottomPadding = parseInt(this._settings.padding.bottom,10);
var total_height = y-yPadding-bottomPadding;
/*unit calculation (bar_height = value*unit)*/
var relativeValues = this._getRelativeValue(minValue,maxValue);
relValue = relativeValues[0];
valueFactor = relativeValues[1];
var unit = total_height/relValue;
if(!yax){
/*defines start value for better representation of small values*/
var startValue = (unit>10?unit:10);
unit = (total_height-startValue)/relValue;
}
/*an available width for one bar*/
var cellWidth = Math.floor((x-xPadding-parseInt(this._settings.padding.right,10))/data.length);
/*a real bar width */
var barWidth = parseInt(this._settings.width,10);
if(barWidth>cellWidth) barWidth = cellWidth-2;
/*the half of distance between bars*/
var barOffset = Math.floor((cellWidth - barWidth)/2);
/*the radius of rounding in the top part of each bar*/
var radius = Math.round(barWidth/5);
var inner_gradient = false;
var gradient = this._settings.gradient;
if (gradient === true){
inner_gradient = true;
gradient = false;
} else if (gradient){
gradient = ctx.createLinearGradient(0,bottomPadding,0,y-yPadding);
this._settings.gradient(gradient);
}
var scaleY = 0;
/*draws a black line if the horizontal scale isn't defined*/
if(bottomPadding&&!xax){
scaleY = y-bottomPadding;
this._drawLine(ctx,0,scaleY+0.5,x,scaleY+0.5,"#000000",1); //hardcoded color!
}
for(var i=0; i < data.length;i ++){
var value = parseFloat(this._settings.value(data[i]));
if(value>maxValue) value = maxValue;
value -= minValue;
value *= valueFactor;
/*start point (bottom left)*/
var x0 = xPadding + barOffset + i*cellWidth;
var y0 = y-bottomPadding;
/*sets a label in the horizontal scale*/
this._drawXAxisLabel(x0+Math.floor(barWidth/2),data[i]);
if(value<0||(this._settings.yAxis&&value===0)){
this.renderTextAt(true, true, x0+Math.floor(barWidth/2),y0,this._settings.label(data[i]));
continue;
}
/*takes start value into consideration*/
if(!yax) value += startValue/unit;
var color = gradient||this._settings.color.call(this,data[i]);
/*drawing the gradient border of a bar*/
if(this._settings.border){
ctx.beginPath();
ctx.fillStyle = color;
this._setBarPoints(ctx,x0-1,y0,barWidth+2,radius,unit,value,0);
ctx.lineTo(x0,0);
ctx.fill();
ctx.fillStyle = "#000000";
ctx.globalAlpha = 0.37;
ctx.beginPath();
this._setBarPoints(ctx,x0-1,y0,barWidth+2,radius,unit,value,0);
ctx.fill();
}
/*drawing bar body*/
ctx.globalAlpha = this._settings.alpha.call(this,data[i]);
ctx.fillStyle = (gradient||this._settings.color.call(this,data[i]));
ctx.beginPath();
var points = this._setBarPoints(ctx,x0,y0,barWidth,radius,unit,value,(this._settings.border?1:0));
if (gradient) ctx.lineTo(x0,0); //fix gradient sphreading
ctx.fill();
ctx.globalAlpha = 1;
if (inner_gradient){
var i_gradient = ctx.createLinearGradient(0,y0-unit*value+2,0,y0);
i_gradient.addColorStop(0,color);
i_gradient.addColorStop(0.1,color);
i_gradient.addColorStop(1,"#FFFFFF");
ctx.fillStyle = i_gradient;
ctx.beginPath();
var points = this._setBarPoints(ctx,x0+2,y0,barWidth-4,radius,unit,value,1);
ctx.fill();
}
/*sets a bar label*/
this.renderTextAt(true, true, x0+Math.floor(barWidth/2),points[1],this._settings.label(data[i]));
/*defines a map area for a bar*/
map.addRect(data[i].id,[x0,points[1],points[0],y0]);
}
this._drawXAxis(ctx,data,x,y);
map.render(this._obj);
},
/**
* sets points for bar and returns the position of the bottom right point
* @param: ctx - canvas object
* @param: x0 - the x position of start point
* @param: y0 - the y position of start point
* @param: barWidth - bar width
* @param: radius - the rounding radius of the top
* @param: unit - the value defines the correspondence between item value and bar height
* @param: value - item value
* @param: offset - the offset from expected bar edge (necessary for drawing border)
*/
_setBarPoints:function(ctx,x0,y0,barWidth,radius,unit,value,offset){
/*correction for displaing small values (when rounding radius is bigger than bar height)*/
var angle_corr = 0;
if(radius>unit*value){
var cosA = (radius-unit*value)/radius;
angle_corr = -Math.acos(cosA)+Math.PI/2;
}
/*start*/
ctx.moveTo(x0,y0);
/*start of left rounding*/
var y1 = y0 - Math.floor(unit*value) + radius;
if(radius<unit*value)
ctx.lineTo(x0,y1);
/*left rounding*/
var x2 = x0 + radius-offset;
if (radius)
ctx.arc(x2,y1,radius-offset,-Math.PI+angle_corr,-Math.PI/2,false);
/*start of right rounding*/
var x3 = x0 + barWidth - radius+offset;
var y3 = y1 - radius+(radius?offset:0);
ctx.lineTo(x3,y3);
/*right rounding*/
var y4 = y1;
if (radius)
ctx.arc(x3,y4,radius-offset,-Math.PI/2,0-angle_corr,false);
/*bottom right point*/
var x5 = x0 + barWidth;
ctx.lineTo(x5,y0);
/*line to the start point*/
ctx.lineTo(x0,y0);
// ctx.lineTo(x0,0); //IE fix!
return [x5,y3];
}
};
/* DHX DEPEND FROM FILE 'chart_pie.js'*/
/*DHX:Depend map.js*/
if (!dhtmlx.chart) dhtmlx.chart = {};
dhtmlx.chart.pie = {
pvt_render_pie:function(ctx,data,x,y){
this._renderPie(ctx,data,x,y,1);
},
pvt_render_pie3D:function(ctx,data,x,y){
this._renderPie(ctx,data,x,y,this._settings.cant);
},
/**
* renders a pie chart
* @param: ctx - canvas object
* @param: data - object those need to be displayed
* @param: x - the width of the container
* @param: y - the height of the container
* @param: ky - value from 0 to 1 that defines an angle of inclination (0<ky<1 - 3D chart)
*/
_renderPie:function(ctx,data,x,y,ky){
var map = new dhtmlx.ui.Map(this._id);
var totalValue = 0;
var coord = this._getPieParameters(x,y);
/*pie radius*/
var radius = (this._settings.radius?this._settings.radius:coord.radius);
var maxValue = this.max(this._settings.value);
/*weighed values (the ratio of object value to total value)*/
var ratios = [];
/*real values*/
var values = [];
var prevSum = 0;
if(this._settings.legend)
this._settings.legend.total_height = this._settings.legend.height*data.length;
for(var i = 0; i < data.length;i++)
totalValue += parseFloat(this._settings.value(data[i]));
for(var i = 0; i < data.length;i++){
values[i] = parseFloat(this._settings.value(data[i]));
ratios[i] = Math.PI*2*((values[i]+prevSum)/totalValue);
prevSum += values[i];
}
/*pie center*/
var x0 = (this._settings.x?this._settings.x:coord.x);
var y0 = (this._settings.y?this._settings.y:coord.y);
/*adds shadow to the 2D pie*/
if(ky==1&&this._settings.shadow)
this._addShadow(ctx,x0,y0,radius);
/*changes vertical position of the center according to 3Dpie cant*/
y0 = y0/ky;
/*the angle defines the 1st edge of the sector*/
var alpha0 = -Math.PI/2;
/*changes Canvas vertical scale*/
ctx.scale(1,ky);
for(var i = 0; i < data.length;i++){
/*drawing sector*/
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(x0,y0);
/*the angle defines the 2nd edge of the sector*/
alpha1 = -Math.PI/2+ratios[i];
ctx.arc(x0,y0,radius,alpha0,alpha1,false);
ctx.lineTo(x0,y0);
var color = this._settings.color.call(this,data[i]);
ctx.fillStyle = color;
ctx.strokeStyle = this._settings.lineColor(data[i]);
ctx.stroke();
ctx.fill();
/*text that needs being displayed inside the sector*/
if(this._settings.pieInnerText)
this._drawSectorLabel(x0,y0,5*radius/6,alpha0,alpha1,ky,this._settings.pieInnerText(data[i],totalValue),true);
/*label outside the sector*/
if(this._settings.label)
this._drawSectorLabel(x0,y0,radius+this._settings.labelOffset,alpha0,alpha1,ky,this._settings.label(data[i]));
/*drawing lower part for 3D pie*/
if(ky!=1){
this._createLowerSector(ctx,x0,y0,alpha0,alpha1,radius,true);
ctx.fillStyle = "#000000";
ctx.globalAlpha = 0.2;
this._createLowerSector(ctx,x0,y0,alpha0,alpha1,radius,false);
ctx.globalAlpha = 1;
ctx.fillStyle = color;
}
/*creats map area (needed for events)*/
map.addSector(data[i].id,alpha0,alpha1,x0,y0,radius,ky);
alpha0 = alpha1;
/*render the details block*/
if(this._settings.legend){
ctx.scale(1,1/ky);
this._drawLegendItem(ctx,data[i],x,y,i);
ctx.scale(1,ky);
}
}
map.render(this._obj);
/*adds radial gradient to a pie*/
if (this._settings.gradient){
var x1 = (ky!=1?x0+radius/3:x0);
var y1 = (ky!=1?y0+radius/3:y0);
this._showRadialGradient(ctx,x0,y0,radius,x1,y1);
}
ctx.scale(1,1/ky);
},
/**
* draws the element of details block
* @param: ctx - canvas object
* @param: object - data object
* @param: x - the width of a container
* @param: y - the height of a container
* @param: i - the index of a data object
*/
_drawLegendItem:function(ctx,obj,x,y,i){
var details = this._settings.legend;
ctx.strokeStyle = ctx.fillStyle;
ctx.lineWidth = details.marker.height;
ctx.lineCap = details.marker.type;
ctx.beginPath();
/*start of item*/
var x0 = 5+ctx.lineWidth/2;
var y0 = 5+ctx.lineWidth/2;
/*the correction for start position (considers the align of details block)*/
if(details.align == "right")
x0 += this._obj.offsetWidth - details.width;
if(details.valign == "bottom"&&(y>details.total_height))
y0 += y-details.total_height-ctx.lineWidth/2;
/*considers item index*/
y0 += i*details.height;
ctx.moveTo(x0,y0);
var x1 = x0+details.marker.width-details.marker.height +1;
ctx.lineTo(x1,y0);
ctx.stroke();
/*item text*/
this.renderText(x1+details.marker.width/2+5,y0-details.marker.height/2,details.template(obj));
},
/**
* returns calculated pie parameters: center position and radius
* @param: x - the width of a container
* @param: y - the height of a container
*/
_getPieParameters:function(x,y){
var offset = 0;
if(this._settings.legend)
offset = this._settings.legend.width*(this._settings.legend.align=="right"?-1:1);
x = (x + offset)/2;
y = y/2;
var radius = Math.min(x,y)-this._settings.padding.top;
return {"x":x,"y":y,"radius":radius};
},
/**
* creates lower part of sector in 3Dpie
* @param: ctx - canvas object
* @param: x0 - the horizontal position of the pie center
* @param: y0 - the vertical position of the pie center
* @param: a0 - the angle that defines the first edge of a sector
* @param: a1 - the angle that defines the second edge of a sector
* @param: R - pie radius
* @param: line (boolean) - if the sector needs a border
*/
_createLowerSector:function(ctx,x0,y0,a1,a2,R,line){
ctx.lineWidth = 1;
/*checks if the lower sector needs being displayed*/
if(!((a1<=0 && a2>=0)||(a1>=0 && a2<=Math.PI)||(a1<=Math.PI && a2>=Math.PI))) return;
if(a1<=0 && a2>=0){
a1 = 0;
line = false;
this._drawSectorLine(ctx,x0,y0,R,a1,a2);
}
if(a1<=Math.PI && a2>=Math.PI){
a2 = Math.PI;
line = false;
this._drawSectorLine(ctx,x0,y0,R,a1,a2);
}
/*the height of 3D pie*/
var offset = (this._settings.height||Math.floor(R/4))/this._settings.cant;
ctx.beginPath();
ctx.arc(x0,y0,R,a1,a2,false);
ctx.lineTo(x0+R*Math.cos(a2),y0+R*Math.sin(a2)+offset);
ctx.arc(x0,y0+offset,R,a2,a1,true);
ctx.lineTo(x0+R*Math.cos(a1),y0+R*Math.sin(a1));
ctx.fill();
if(line)
ctx.stroke();
},
/**
* draws a serctor arc
*/
_drawSectorLine:function(ctx,x0,y0,R,a1,a2){
ctx.beginPath();
ctx.arc(x0,y0,R,a1,a2,false);
ctx.stroke();
},
/**
* adds a shadow to pie
* @param: ctx - canvas object
* @param: x - the horizontal position of the pie center
* @param: y - the vertical position of the pie center
* @param: R - pie radius
*/
_addShadow:function(ctx,x,y,R){
var shadows = ["#676767","#7b7b7b","#a0a0a0","#bcbcbc","#d1d1d1","#d6d6d6"];
for(var i = shadows.length-1;i>-1;i--){
ctx.beginPath();
ctx.fillStyle = shadows[i];
ctx.arc(x+2,y+2,R+i,0,Math.PI*2,true);
ctx.fill();
}
},
/**
* returns a gray gradient
* @param: gradient - gradient object
*/
_getGrayGradient:function(gradient){
gradient.addColorStop(0.0,"#ffffff");
gradient.addColorStop(0.7,"#7a7a7a");
gradient.addColorStop(1.0,"#000000");
return gradient;
},
/**
* adds gray radial gradient
* @param: ctx - canvas object
* @param: x - the horizontal position of the pie center
* @param: y - the vertical position of the pie center
* @param: radius - pie radius
* @param: x0 - the horizontal position of a gradient center
* @param: y0 - the vertical position of a gradient center
*/
_showRadialGradient:function(ctx,x,y,radius,x0,y0){
ctx.globalAlpha = 0.3;
ctx.beginPath();
var gradient;
if(typeof this._settings.gradient!= "function"){
gradient = ctx.createRadialGradient(x0,y0,radius/4,x,y,radius);
gradient = this._getGrayGradient(gradient);
}
else gradient = this._settings.gradient(gradient);
ctx.fillStyle = gradient;
ctx.arc(x,y,radius,0,Math.PI*2,true);
ctx.fill();
ctx.globalAlpha = 1;
},
/**
* returns the calculates pie parameters: center position and radius
* @param: ctx - canvas object
* @param: x0 - the horizontal position of the pie center
* @param: y0 - the vertical position of the pie center
* @param: R - pie radius
* @param: alpha1 - the angle that defines the 1st edge of a sector
* @param: alpha2 - the angle that defines the 2nd edge of a sector
* @param: ky - the value that defines an angle of inclination
* @param: text - label text
* @param: in_width (boolean) - if label needs being displayed inside a pie
*/
_drawSectorLabel:function(x0,y0,R,alpha1,alpha2,ky,text,in_width){
var t = this.renderText(0,0,text,0,1);
if (!t) return;
//get existing width of text
var labelWidth = t.scrollWidth;
t.style.width = labelWidth+"px"; //adjust text label to fit all text
if (labelWidth>x0) labelWidth = x0; //the text can't be greater than half of view
//calculate expected correction based on default font metrics
var width = 8;
if (in_width) width = labelWidth/1.8;
var alpha = alpha1+(alpha2-alpha1)/2;
//calcualteion position and correction
R = R-(width-8)/2;
var corr_x = - width;
var corr_y = -8;
var align = "left";
//for items in right upper sector
if(alpha>=Math.PI/2 && alpha<Math.PI){
//correction need to be applied because of righ align
//we need to count right upper angle instead of left upper angle
corr_x = -labelWidth-corr_x+1;/*correction for label width*/
align = "right";
}
//for items in right lower sector
if(alpha<=3*Math.PI/2 && alpha>=Math.PI){
corr_x = -labelWidth-corr_x+1;
align = "right";
}
//calculate position of text
//basically get point at center of pie sector
var y = (y0+Math.floor(R*Math.sin(alpha)))*ky+corr_y;
var x = x0+Math.floor((R+width/2)*Math.cos(alpha))+corr_x;
//if pie sector starts in left of right part pie, related text
//must be placed to the left of to the right of pie as well
var left_end = (alpha2 < Math.PI/2+0.01)
var left_start = (alpha < Math.PI/2);
if (left_start && left_end)
x = Math.max(x,x0+3); //right part of pie
else if (!left_start && !left_end)
x = Math.min(x,x0-labelWidth); //left part of pie
/*correction for the lower sector of the 3D pie*/
if (!in_width && ky<1 && y > y0*ky){
y+= (this._settings.height||Math.floor(R/4));
}
//we need to set position of text manually, based on above calculations
t.style.top = y+"px";
t.style.left = x+"px";
t.style.width = labelWidth+"px";
t.style.textAlign = align;
t.style.whiteSpace = "nowrap";
}
};
/* DHX DEPEND FROM FILE 'template.js'*/
/*
Template - handles html templates
*/
/*DHX:Depend dhtmlx.js*/
dhtmlx.Template={
_cache:{
},
empty:function(){
return "";
},
setter:function(name, value){
return dhtmlx.Template.fromHTML(value);
},
obj_setter:function(name,value){
var f = dhtmlx.Template.setter(name,value);
var obj = this;
return function(){
return f.apply(obj, arguments);
};
},
fromHTML:function(str){
if (typeof str == "function") return str;
if (this._cache[str])
return this._cache[str];
//supported idioms
// {obj} => 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";
this.version = "3.0";
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){
//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";
},
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(mode,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 'datastore.js'*/
/*DHX:Depend dhtmlx.js*/
/*
DataStore is not a behavior, it standalone object, which represents collection of data.
Call provideAPI to map data API
@export
exists
idByIndex
indexById
get
set
refresh
dataCount
sort
filter
next
previous
clearAll
first
last
*/
dhtmlx.DataStore = function(){
this.name = "DataStore";
dhtmlx.extend(this, dhtmlx.EventSystem);
this.setDriver("xml"); //default data source is an XML
this.pull = {}; //hash of IDs
this.order = dhtmlx.toArray(); //order of IDs
};
dhtmlx.DataStore.prototype={
//defines type of used data driver
//data driver is an abstraction other different data formats - xml, json, csv, etc.
setDriver:function(type){
dhtmlx.assert(dhtmlx.DataDriver[type],"incorrect DataDriver");
this.driver = dhtmlx.DataDriver[type];
},
//process incoming raw data
_parse:function(data){
//get size and position of data
var info = this.driver.getInfo(data);
//get array of records
var recs = this.driver.getRecords(data);
var from = (info._from||0)*1;
var j=0;
for (var i=0; i<recs.length; i++){
//get has of details for each record
var temp = this.driver.getDetails(recs[i]);
var id = this.id(temp); //generate ID for the record
if (!this.pull[id]){ //if such ID already exists - update instead of insert
this.order[j+from]=id;
j++;
}
this.pull[id]=temp;
}
//for all not loaded data
for (var i=0; i < info._size; i++)
if (!this.order[i]){
var id = dhtmlx.uid();
var temp = {id:id, $template:"loading"}; //create fake records
this.pull[id]=temp;
this.order[i]=id;
}
this.callEvent("onStoreLoad",[this.driver, data]);
//repaint self after data loading
this.refresh();
},
//generate id for data object
id:function(data){
return data.id||(data.id=dhtmlx.uid());
},
//get data from hash by id
get:function(id){
return this.pull[id];
},
//assigns data by id
set:function(id,data){
this.pull[id]=data;
this.refresh();
},
//sends repainting signal
refresh:function(id){
if (id)
this.callEvent("onStoreUpdated",[id, this.pull[id], "update"]);
else
this.callEvent("onStoreUpdated",[null,null,null]);
},
//converts range IDs to array of all IDs between them
getRange:function(from,to){
if (!arguments.length){
//if indexes not provided - return all visible rows
from = this.min||0; to = Math.min((this.max||Infinity),(this.dataCount()-1));
} else{
from = this.indexById(from);
to = this.indexById(to);
if (from>to){ //can be in case of backward shift-selection
var a=to; to=from; from=a;
}
}
return this.getIndexRange(from,to);
},
//converts range of indexes to array of all IDs between them
getIndexRange:function(from,to){
to=Math.min(to,this.dataCount()-1);
var ret=dhtmlx.toArray(); //result of method is rich-array
for (var i=from; i <= to; i++)
ret.push(this.get(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 || tindex >= this.order.length){
dhtmlx.error("DataStore::move","Incorrect indexes");
return;
}
var id = this.idByIndex(sindex);
var obj = this.get(id);
this.order.removeAt(sindex); //remove at old position
//if (sindex>tindex) tindex--; //correct shift, caused by element removing
this.order.insertAt(id,tindex); //insert at new position
//repaint signal
this.callEvent("onStoreUpdated",[id,obj,"move"]);
},
//adds item to the store
add:function(obj,index){
//generate id for the item
var id = this.id(obj);
//by default item is added to the end of the list
var data_size = this.dataCount();
if (dhtmlx.isNotDefined(index))
index = data_size;
//check to prevent too big indexes
if (index > data_size){
dhtmlx.log("Warning","DataStore:add","Index of out of bounds");
index = Math.min(this.order.length,index);
}
if (!this.callEvent("onbeforeAdd",[id,index])) return;
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])) return;
if (!this.exists(id)) return dhtmlx.error("Not existing ID",id);
var obj = this.get(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._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 (!res && res!==0)
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;
var sorter = dhtmlx.sort.create(sort);
//get array of IDs
var neworder = this.getRange();
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){
//remove previous filtering , if any
if (this._filter_order){
this.order = this._filter_order;
delete this._filter_order;
}
//if text not define -just unfilter previous state and exit
if (text){
var filter = text;
if (typeof text == "string"){
text = dhtmlx.Template.setter(0,text);
filter = function(obj,value){ //default filter - string start from, case in-sensitive
return text(obj).toLowerCase().indexOf(value)!=-1;
};
}
value = (value||"").toString().toLowerCase();
var neworder = dhtmlx.toArray();
this.order.each(function(id){
if (filter(this.get(id),value))
neworder.push(id);
},this);
//set new order of items, store original
this._filter_order = this.order;
this.order = neworder;
}
//repaint self
this.refresh();
},
/*
Iterate through collection
*/
each:function(method,master){
for (var i=0; i<this.order.length; i++)
method.call((master||this), this.get(this.order[i]));
},
/*
map inner methods to some distant object
*/
provideApi:function(target,eventable){
if (eventable){
this.mapEvent({
onbeforesort: target,
onaftersort: target,
onbeforeadd: target,
onafteradd: target,
onbeforedelete: target,
onafterdelete: target
});
}
var list = ["sort","add","remove","exists","idByIndex","indexById","get","set","refresh","dataCount","filter","next","previous","clearAll","first","last"];
for (var i=0; i < list.length; i++)
target[list[i]]=dhtmlx.methodPush(this,list[i]);
if (dhtmlx.assert_enabled())
this.assert_event(target);
}
};
dhtmlx.sort = {
create:function(config){
return dhtmlx.sort.dir(config.dir, dhtmlx.sort.by(config.by, config.as));
},
as:{
"int":function(a,b){
a = a*1; b=b*1;
return a>b?1:(a<b?-1:0);
},
"string_strict":function(a,b){
a = a.toString(); b=b.toString();
return a>b?1:(a<b?-1:0);
},
"string":function(a,b){
a = a.toString().toLowerCase(); b=b.toString().toLowerCase();
return a>b?1:(a<b?-1:0);
}
},
by:function(prop, method){
if (typeof method != "function")
method = dhtmlx.sort.as[method||"string"];
prop = dhtmlx.Template.setter(0,prop);
return function(a,b){
return method(prop(a),prop(b));
};
},
dir:function(prop, method){
if (prop == "asc")
return method;
return function(a,b){
return method(a,b)*-1;
};
}
};
/* DHX DEPEND FROM FILE 'group.js'*/
/*DHX:Depend datastore.js*/
/*DHX:Depend dhtmlx.js*/
dhtmlx.Group = {
_init:function(){
dhtmlx.assert(this.data,"DataStore required for grouping");
this.data.attachEvent("onStoreLoad",dhtmlx.bind(function(){
if (this._settings.group)
this.group(this._settings.group,false);
},this));
this.attachEvent("onBeforeRender",dhtmlx.bind(function(data){
if (this._settings.sort){
data.block();
data.sort(this._settings.sort);
data.unblock();
}
},this));
this.attachEvent("onBeforeSort",dhtmlx.bind(function(){
this._settings.sort = null;
},this));
},
_init_group_data_event:function(data,master){
data.attachEvent("onClearAll",dhtmlx.bind(function(){
this.ungroup(false);
},master));
},
sum:function(property, data){
property = dhtmlx.Template.setter(0,property);
data = data || this.data;
var summ = 0;
data.each(function(obj){
summ+=property(obj)*1;
});
return summ;
},
min:function(property, data){
property = dhtmlx.Template.setter(0,property);
data = data || this.data;
var min = Infinity;
data.each(function(obj){
if (property(obj)*1 < min) min = property(obj)*1;
});
return min*1;
},
max:function(property, data){
property = dhtmlx.Template.setter(0,property);
data = data || this.data;
var max = -Infinity;
data.each(function(obj){
if (property(obj)*1 > max) max = property(obj)*1;
});
return max;
},
_split_data_by:function(stats){
var any=function(property, data){
property = dhtmlx.Template.setter(0,property);
return property(data[0]);
};
var key = dhtmlx.Template.setter(0,stats.by);
if (!stats.map[key])
stats.map[key] = [key, any];
var groups = {};
var labels = [];
this.data.each(function(data){
var current = key(data);
if (!groups[current]){
labels.push({id:current});
groups[current] = dhtmlx.toArray();
}
groups[current].push(data);
});
for (var prop in stats.map){
var functor = (stats.map[prop][1]||any);
if (typeof functor != "function")
functor = this[functor];
for (var i=0; i < labels.length; i++) {
labels[i][prop]=functor.call(this, stats.map[prop][0], groups[labels[i].id]);
}
}
// if (this._settings.sort)
// labels.sortBy(stats.sort);
this._not_grouped_data = this.data;
this.data = new dhtmlx.DataStore();
this.data.provideApi(this,true);
this._init_group_data_event(this.data, this);
this.parse(labels,"json");
},
group:function(config,mode){
this.ungroup(false);
this._split_data_by(config);
if (mode!==false)
this.render();
},
ungroup:function(mode){
if (this._not_grouped_data){
this.data = this._not_grouped_data;
this.data.provideApi(this, true);
}
if (mode!==false)
this.render();
},
group_setter:function(name, config){
dhtmlx.assert(typeof config == "object", "Incorrect group value");
dhtmlx.assert(config.by,"group.by is mandatory");
dhtmlx.assert(config.map,"group.map is mandatory");
return config;
},
//need to be moved to more appropriate object
sort_setter:function(name, config){
if (typeof config != "object")
config = { by:config };
this._mergeSettings(config,{
as:"string",
dir:"asc"
});
return config;
}
};
/* DHX DEPEND FROM FILE 'key.js'*/
/*
Behavior:KeyEvents - hears keyboard
*/
dhtmlx.KeyEvents = {
_init:function(){
//attach handler to the main container
dhtmlx.event(this._obj,"keypress",this._onKeyPress,this);
},
//called on each key press , when focus is inside of related component
_onKeyPress:function(e){
e=e||event;
var code = e.keyCode; //FIXME better solution is required
this.callEvent((this._edit_id?"onEditKeyPress":"onKeyPress"),[code,e.ctrlKey,e.shiftKey,e]);
}
};
/* DHX DEPEND FROM FILE 'mouse.js'*/
/*
Behavior:MouseEvents - provides inner evnets for mouse actions
*/
dhtmlx.MouseEvents={
_init: function(){
//attach dom events if related collection is defined
if (this._click){
dhtmlx.event(this._obj,"click",this._onClick,this);
dhtmlx.event(this._obj,"contextmenu",this._onContext,this);
}
if (this._dblclick)
dhtmlx.event(this._obj,"dblclick",this._onDblClick,this);
if (this._mouse_move){
dhtmlx.event(this._obj,"mousemove",this._onMouse,this);
dhtmlx.event(this._obj,(dhtmlx._isIE?"mouseleave":"mouseout"),this._onMouse,this);
}
},
//inner onclick object handler
_onClick: function(e) {
return this._mouseEvent(e,this._click,"ItemClick");
},
//inner ondblclick object handler
_onDblClick: function(e) {
return this._mouseEvent(e,this._dblclick,"ItemDblClick");
},
//process oncontextmenu events
_onContext: function(e) {
var id = dhtmlx.html.locate(e, this._id);
if (id && !this.callEvent("onBeforeContextMenu", [id,e]))
return dhtmlx.html.preventEvent(e);
},
/*
event throttler - ignore events which occurs too fast
during mouse moving there are a lot of event firing - we need no so much
also, mouseout can fire when moving inside the same html container - we need to ignore such fake calls
*/
_onMouse:function(e){
if (dhtmlx._isIE) //make a copy of event, will be used in timed call
e = document.createEventObject(event);
if (this._mouse_move_timer) //clear old event timer
window.clearTimeout(this._mouse_move_timer);
//this event just inform about moving operation, we don't care about details
this.callEvent("onMouseMoving",[e]);
//set new event timer
this._mouse_move_timer = window.setTimeout(dhtmlx.bind(function(){
//called only when we have at least 100ms after previous event
if (e.type == "mousemove")
this._onMouseMove(e);
else
this._onMouseOut(e);
},this),500);
},
//inner mousemove object handler
_onMouseMove: function(e) {
if (!this._mouseEvent(e,this._mouse_move,"MouseMove"))
this.callEvent("onMouseOut",[e||event]);
},
//inner mouseout object handler
_onMouseOut: function(e) {
this.callEvent("onMouseOut",[e||event]);
},
//common logic for click and dbl-click processing
_mouseEvent:function(e,hash,name){
e=e||event;
var trg=e.target||e.srcElement;
var css = "";
var id = null;
var found = false;
//loop through all parents
while (trg && trg.parentNode){
if (!found){ //if element with ID mark is not detected yet
id = trg.getAttribute(this._id); //check id of current one
if (id){
if (!this.callEvent("on"+name,[id,e,trg])) return; //it will be triggered only for first detected ID, in case of nested elements
found = true; //set found flag
}
}
css=trg.className;
if (css){ //check if pre-defined reaction for element's css name exists
css = css.split(" ");
css = css[0]||css[1]; //FIXME:bad solution, workaround css classes which are starting from whitespace
if (hash[css])
return hash[css].call(this,e,id,trg);
}
trg=trg.parentNode;
}
return found; //returns true if item was located and event was triggered
}
};
/* DHX DEPEND FROM FILE 'config.js'*/
/*
Behavior:Settings
@export
customize
config
*/
/*DHX:Depend template.js*/
/*DHX:Depend dhtmlx.js*/
dhtmlx.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=[];
this._settings.sort = null; //mask default method
},
define:function(property, value){
if (typeof property == "object")
return this._parseSeetingColl(property);
return this._define(property, value);
},
_define:function(property,value){
dhtmlx.assert_settings.call(this,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,property,value):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 = dhtmlx.extend({},initial);
//code below will copy all properties over default one
if (typeof obj == "object" && !obj.tagName)
dhtmlx.extend(settings,obj);
//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;
},
//helper for html container init
_parseContainer:function(obj,name,fallback){
/*
parameter can be a config object, in such case real container will be obj.container
or it can be html object or ID of html object
*/
if (typeof obj == "object" && !obj.tagName)
obj=obj.container;
this._obj = dhtmlx.toNode(obj);
if (!this._obj && fallback)
this._obj = fallback(obj);
dhtmlx.assert(this._obj, "Incorrect html container");
this._obj.className+=" "+name;
this._obj.onselectstart=function(){return false;}; //block selection by default
this._dataobj = this._obj;//separate reference for rendering modules
},
//apply template-type
_set_type:function(name){
//parameter can be a hash of settings
if (typeof name == "object")
return this.type_setter("type",name);
dhtmlx.assert(this.types, "RenderStack :: Types are not defined");
dhtmlx.assert(this.types[name],"RenderStack :: Inccorect type name",name);
//or parameter can be a name of existing template-type
this.type=dhtmlx.extend({},this.types[name]);
this.customize(); //init configs
},
customize:function(obj){
//apply new properties
if (obj) dhtmlx.extend(this.type,obj);
//init tempaltes for item start and item end
this.type._item_start = dhtmlx.Template.fromHTML(this.template_item_start(this.type));
this.type._item_end = this.template_item_end(this.type);
//repaint self
this.render();
},
//config.type - creates new template-type, based on configuration object
type_setter:function(mode,value){
this._set_type(typeof value == "object"?dhtmlx.Type.add(this,value):value);
return value;
},
//config.template - creates new template-type with defined template string
template_setter:function(mode,value){
return this.type_setter("type",{template:value});
},
//config.css - css name for top level container
css_setter:function(mode,value){
this._obj.className += " "+value;
return value;
}
};
/* DHX DEPEND FROM FILE 'compatibility.js'*/
/*
Collection of compatibility hacks
*/
/*DHX:Depend dhtmlx.js*/
dhtmlx.compat=function(name, obj){
//check if name hach present, and applies it when necessary
if (dhtmlx.compat[name])
dhtmlx.compat[name](obj);
};
(function(){
if (!window.dhtmlxError){
//dhtmlxcommon is not included
//create fake error tracker for connectors
var dummy = function(){};
window.dhtmlxError={ catchError:dummy, throwError:dummy };
//helpers instead of ones from dhtmlxcommon
window.convertStringToBoolean=function(value){
return !!value;
};
window.dhtmlxEventable = function(node){
dhtmlx.extend(node,dhtmlx.EventSystem);
};
//imitate ajax layer of dhtmlxcommon
var loader = {
getXMLTopNode:function(name){
},
doXPath:function(path){
return dhtmlx.DataDriver.xml.xpath(this.xml,path);
},
xmlDoc:{
responseXML:true
}
};
//wrap ajax methods of dataprocessor
dhtmlx.compat.dataProcessor=function(obj){
//FIXME
//this is pretty ugly solution - we replace whole method , so changes in dataprocessor need to be reflected here
var sendData = "_sendData";
var in_progress = "_in_progress";
var tMode = "_tMode";
var waitMode = "_waitMode";
obj[sendData]=function(a1,rowId){
if (!a1) return; //nothing to send
if (rowId)
this[in_progress][rowId]=(new Date()).valueOf();
if (!this.callEvent("onBeforeDataSending",rowId?[rowId,this.getState(rowId)]:[])) return false;
var a2 = this;
var a3=this.serverProcessor;
if (this[tMode]!="POST")
//use dhtmlx.ajax instead of old ajax layer
dhtmlx.ajax().get(a3+((a3.indexOf("?")!=-1)?"&":"?")+a1,"",function(t,x,xml){
loader.xml = dhtmlx.DataDriver.xml.checkResponse(t,x);
a2.afterUpdate(a2, null, null, null, loader);
});
else
dhtmlx.ajax().post(a3,a1,function(t,x,xml){
loader.xml = dhtmlx.DataDriver.xml.checkResponse(t,x);
a2.afterUpdate(a2, null, null, null, loader);
});
this[waitMode]++;
};
};
}
})();
/* 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;
this.grid = new window[name](conf);
this.gridId = obj.id;
this.gridObj = obj;
return this.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(){
if (window.dhtmlXChart)
dhtmlXChart.prototype.setSizes = function(){
this.resize();
};
if (window.dhtmlXDataView)
dhtmlXDataView.prototype.setSizes = function(){
this.render();
};
};
/* DHX DEPEND FROM FILE 'load.js'*/
/*
ajax operations
can be used for direct loading as
dhtmlx.ajax(ulr, callback)
or
dhtmlx.ajax().get(url)
dhtmlx.ajax().post(url)
*/
/*DHX:Depend datastore.js*/
/*DHX:Depend dhtmlx.js*/
dhtmlx.ajax = function(url,call,master){
//if parameters was provided - made fast call
if (arguments.length!==0){
var http_request = new dhtmlx.ajax();
if (master) http_request.master=master;
http_request.get(url,null,call);
}
if (!this.getXHR) return new dhtmlx.ajax(); //allow to create new instance without direct new declaration
return this;
};
dhtmlx.ajax.prototype={
//creates xmlHTTP object
getXHR:function(){
if (dhtmlx._isIE)
return new ActiveXObject("Microsoft.xmlHTTP");
else
return new XMLHttpRequest();
},
/*
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){
var x=this.getXHR();
if (typeof call == "function")
call = [call];
//add extra params to the url
if (typeof params == "object"){
var t=[];
for (var a in params)
t.push(a+"="+encodeURIComponent(params[a]));// utf-8 escaping
params=t.join("&");
}
if (params && !this.post){
url=url+(url.indexOf("?")!=-1 ? "&" : "?")+params;
params=null;
}
x.open(this.post?"POST":"GET",url,!this._sync);
if (this.post)
x.setRequestHeader('Content-type','application/x-www-form-urlencoded');
//async mode, define loading callback
if (!this._sync){
var self=this;
x.onreadystatechange= function(){
if (!x.readyState || x.readyState == 4){
dhtmlx.log_full_time("data_loading"); //log rendering time
if (call && self)
for (var i=0; i < call.length; i++) //there can be multiple callbacks
if (call[i])
call[i].call((self.master||self),x.responseText,x.responseXML,x);
self.master=null;
call=x=self=null; //anti-leak
}
};
}
x.send(params||null);
return x; //return XHR, which can be used in case of sync. mode
},
//GET request
get:function(url,params,call){
this.post=false;
return this.send(url,params,call);
},
//POST request
post:function(url,params,call){
this.post=true;
return this.send(url,params,call);
},
sync:function(){
this._sync = true;
return this;
}
};
/*
Behavior:DataLoader - load data in the component
@export
load
parse
*/
dhtmlx.DataLoader={
_init:function(){
//prepare data store
this.data = new dhtmlx.DataStore();
},
//loads data from external URL
load:function(url,call){
this.callEvent("onXLS",[]);
if (typeof call == "string"){ //second parameter can be a loading type or callback
this.data.setDriver(call);
call = arguments[2];
}
//prepare data feed for dyn. loading
if (!this.data.feed)
this.data.feed = function(from,count){
//allow only single request at same time
if (this._load_count)
return this._load_count=[from,count]; //save last ignored request
else
this._load_count=true;
this.load(url+((url.indexOf("?")==-1)?"?":"&")+"posStart="+from+"&count="+count,function(){
//after loading check if we have some ignored requests
var temp = this._load_count;
this._load_count = false;
if (typeof temp =="object")
this.data.feed.apply(this, temp); //load last ignored request
});
};
//load data by async ajax call
dhtmlx.ajax(url,[this._onLoad,call],this);
},
//loads data from object
parse:function(data,type){
this.callEvent("onXLS",[]);
if (type)
this.data.setDriver(type);
this._onLoad(data,null);
},
//default after loading callback
_onLoad:function(text,xml,loader){
this.data._parse(this.data.driver.toObject(text,xml));
this.callEvent("onXLE",[]);
}
};
/*
Abstraction layer for different data types
*/
dhtmlx.DataDriver={};
dhtmlx.DataDriver.json={
//convert json string to json object if necessary
toObject:function(data){
if (typeof data == "string"){
eval ("dhtmlx.temp="+data);
return dhtmlx.temp;
}
return data;
},
//get array of records
getRecords:function(data){
if (data && !(data instanceof Array))
return [data];
return data;
},
//get hash of properties for single record
getDetails:function(data){
return data;
},
//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)
};
}
};
dhtmlx.DataDriver.html={
/*
incoming data can be
- collection of nodes
- 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 = dhtmlx.toNode(data);
if (!t){
t=document.createElement("DIV");
t.innerHTML = data;
}
return t.getElementsByTagName(this.tag);
}
return data;
},
//get array of records
getRecords:function(data){
if (data.tagName)
return data.childNodes;
return data;
},
//get hash of properties for single record
getDetails:function(data){
return dhtmlx.DataDriver.xml.tagToObject(data);
},
//dyn loading is not supported by HTML data source
getInfo:function(data){
return {
_size:0,
_from:0
};
},
tag: "LI"
};
dhtmlx.DataDriver.jsarray={
//eval jsarray string to jsarray object if necessary
toObject:function(data){
if (typeof data == "string"){
eval ("dhtmlx.temp="+data);
return dhtmlx.temp;
}
return data;
},
//get array of records
getRecords:function(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];
return result;
},
//dyn loading is not supported by js-array data source
getInfo:function(data){
return {
_size:0,
_from:0
};
}
};
dhtmlx.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];
return result;
},
//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;
},
row:"\n", //default row separator
cell:"," //default cell separator
};
dhtmlx.DataDriver.xml={
//convert xml string to xml object if necessary
toObject:function(text,xml){
if (xml && (xml=this.checkResponse(text,xml))) //checkResponse - fix incorrect content type and extra whitespaces errors
return xml;
if (typeof text == "string"){
return this.fromString(text);
}
return text;
},
//get array of records
getRecords:function(data){
return this.xpath(data,this.records);
},
records:"/*/item",
userdata:"/*/userdata",
//get hash of properties for single record
getDetails:function(data){
return this.tagToObject(data,{});
},
getUserData:function(data,col){
col = col || {};
var ud = this.xpath(data,this.userdata);
for (var i=0; i < ud.length; i++) {
var udx = this.tagToObject(ud[i]);
col[udx.name] = udx.value;
}
return col;
},
//get count of data and position at which new data_loading need to be inserted
getInfo:function(data){
return {
_size:(data.documentElement.getAttribute("total_count")||0),
_from:(data.documentElement.getAttribute("pos")||0)
};
},
//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;
} //IE
return xml.selectNodes(path);
},
//convert xml tag to js object, all subtags and attributes are mapped to the properties of result object
tagToObject:function(tag,z){
z=z||{};
//map attributes
var a=tag.attributes;
for (var i=0; i<a.length; i++)
z[a[i].name]=a[i].value;
//map subtags
var flag=false;
var b=tag.childNodes;
for (var i=0; i<b.length; i++){
if (b[i].nodeType==1){
z[b[i].tagName]=this.tagToObject(b[i],{}); //sub-object for complex subtags
flag=true;
}
}
if (!a.length && !flag)
return this.nodeValue(tag);
//each object will have its text content as "value" property
z.value = this.nodeValue(tag);
return z;
},
//get value of xml node
nodeValue:function(node){
if (node.firstChild)
return node.firstChild.data; //FIXME - long text nodes in FF not supported for now
return "";
},
//convert XML string to XML object
fromString:function(xmlString){
if (window.DOMParser) // FF, KHTML, Opera
return (new DOMParser()).parseFromString(xmlString,"text/xml");
if (window.ActiveXObject){ // IE, utf-8 only
temp=new ActiveXObject("Microsoft.xmlDOM");
temp.loadXML(xmlString);
return temp;
}
dhtmlx.error("Load from xml string is not supported");
},
//check is XML correct and try to reparse it if its invalid
checkResponse:function(text,xml){
if (xml && ( xml.firstChild && xml.firstChild.tagName != "parsererror") )
return xml;
//parsing as string resolves incorrect content type
//regexp removes whitespaces before xml declaration, which is vital for FF
var a=this.from_string(text.responseText.replace(/^[\s]+/,""));
if (a) return a;
dhtmlx.error("xml can't be parsed",text.responseText);
}
};
/* DHX DEPEND FROM FILE 'compatibility_grid.js'*/
/*
Compatibility hack for loading data from the grid.
Provides new type of datasource - dhtmlxgrid
*/
/*DHX:Depend load.js*/
dhtmlx.DataDriver.dhtmlxgrid={
_grid_getter:"_get_cell_value",
toObject:function(data){
this._grid = data;
return data;
},
getRecords:function(data){
return data.rowsBuffer;
},
getDetails:function(data){
var result = {};
for (var i=0; i < this._grid.getColumnsNum(); i++)
result["data"+i]=this._grid[this._grid_getter](data,i);
return result;
},
getInfo:function(data){
return {
_size:0,
_from:0
};
}
};
/* DHX DEPEND FROM FILE 'canvas.js'*/
/*DHX:Depend dhtmlx.js*/
dhtmlx.Canvas = {
_init:function(){
this._canvas_labels = [];
},
_prepareCanvas:function(container){
//canvas has the same size as master object
this._canvas = dhtmlx.html.create("canvas",{ width:container.offsetWidth, height:container.offsetHeight });
container.appendChild(this._canvas);
//use excanvas in IE
if (!this._canvas.getContext){
if (dhtmlx._isIE){
G_vmlCanvasManager.init_(document);
G_vmlCanvasManager.initElement(this._canvas);
} else //some other not supported browser
dhtmlx.error("Canvas is not supported in the current browser");
}
return this._canvas;
},
getCanvas:function(context){
return (this._canvas||this._prepareCanvas(this._obj)).getContext(context||"2d");
},
_resizeCanvas:function(){
if (this._canvas){
this._canvas.setAttribute("width", this._canvas.parentNode.offsetWidth);
this._canvas.setAttribute("height", this._canvas.parentNode.offsetHeight);
}
},
renderText:function(x,y,text,css,w){
if (!text) return; //ignore empty text
var t = dhtmlx.html.create("DIV",{
"class":"dhx_canvas_text"+(css?(" "+css):""),
"style":"left:"+x+"px; top:"+y+"px;"
},text);
this._obj.appendChild(t);
this._canvas_labels.push(t); //destructor?
if (w)
t.style.width = w+"px";
return t;
},
renderTextAt:function(top, center, x,y,t,c,w){
var text=this.renderText.call(this,x,y,t,c,w);
if (text){
if (top){
//TODO: Check again, when new version of Chrome will be released
//fixes incorrect text sizing in Chrome
if (dhtmlx._isWebKit) text.style.fontSize = "13px";
text.style.top = y-text.offsetHeight + "px";
}
if (center){
text.style.left = parseInt(x-text.offsetWidth/2,10) + "px";
//TODO: Check again, when new version of Chrome will be released
//fixes incorrect text sizing in Chrome
if (dhtmlx._isWebKit) text.style.width = text.offsetWidth+"px";
}
}
},
clearCanvas:function(){
for(var i=0; i < this._canvas_labels.length;i++)
this._obj.removeChild(this._canvas_labels[i]);
this._canvas_labels = [];
if (this._obj._htmlmap){
this._obj._htmlmap.parentNode.removeChild(this._obj._htmlmap);
this._obj._htmlmap = null;
}
//FF breaks, when we are using clear canvas and call clearRect without parameters
this.getCanvas().clearRect(0,0,this._canvas.offsetWidth, this._canvas.offsetHeight);
}
};
/* DHX INITIAL FILE 'D:\dev\xampp\htdocs\dhtmlx\main\dhtmlxCore/sources//chart.js'*/
/*DHX:Depend thirdparty\excanvas*/
/*DHX:Depend chart.css*/
/*DHX:Depend canvas.js*/
/*DHX:Depend load.js*/
/*DHX:Depend compatibility_grid.js*/
/*DHX:Depend compatibility_layout.js*/
/*DHX:Depend config.js*/
/*DHX:Depend destructor.js*/
/*DHX:Depend mouse.js*/
/*DHX:Depend key.js*/
/*DHX:Depend group.js*/
/*DHX:Depend autotooltip.js*/
/*DHX:Depend chart_pie.js*/
/*DHX:Depend chart_bar.js*/
/*DHX:Depend chart_graph.js*/
/*DHX:Depend math.js*/
/*DHX:Depend destructor.js*/
/*DHX:Depend dhtmlx.js*/
dhtmlXChart = function(container){
this.name = "Chart";
this.version = "3.0";
if (dhtmlx.assert_enabled()) this._assert();
dhtmlx.extend(this, dhtmlx.Settings);
this._parseContainer(container,"dhx_chart");
dhtmlx.extend(this, dhtmlx.DataLoader);
this.data.provideApi(this,true);
dhtmlx.extend(this, dhtmlx.EventSystem);
dhtmlx.extend(this, dhtmlx.MouseEvents);
dhtmlx.extend(this, dhtmlx.Destruction);
dhtmlx.extend(this, dhtmlx.Canvas);
dhtmlx.extend(this, dhtmlx.Group);
dhtmlx.extend(this, dhtmlx.AutoTooltip);
dhtmlx.extend(this, dhtmlx.chart.pie);
dhtmlx.extend(this, dhtmlx.chart.bar);
dhtmlx.extend(this, dhtmlx.chart.line);
this._parseSettings(container,{
color:"RAINBOW",
alpha:"1",
label:false,
value:"{obj.value}",
padding:{},
view:"pie",
lineColor:"#ffffff",
cant:0.5,
width: 15,
labelWidth:100,
line:{},
item:{},
shadow:true,
gradient:false,
border:true,
labelOffset: 20
});
this.data.attachEvent("onStoreUpdated",dhtmlx.bind(function(){
this.render();
},this));
};
dhtmlXChart.prototype={
_id:"dhx_area_id",
_click:{
},
_dblclick:{
},
_mouse_move:{
},
resize:function(){
this._resizeCanvas();
this.render();
},
render:function(){
if (!this.callEvent("onBeforeRender",[this.data]))
return;
this.clearCanvas();
this["pvt_render_"+this._settings.view](
this.getCanvas(),
this.data.getRange(),
this._obj.offsetWidth,
this._obj.offsetHeight
);
},
value_setter:dhtmlx.Template.obj_setter,
alpha_setter:dhtmlx.Template.obj_setter,
label_setter:dhtmlx.Template.obj_setter,
lineColor_setter:dhtmlx.Template.obj_setter,
pieInnerText_setter:dhtmlx.Template.obj_setter,
//available coloring presets
colormap:{
"RAINBOW":function(obj){
var pos = Math.floor(this.indexById(obj.id)/this.dataCount()*1536);
if (pos==1536) pos-=1;
return this._rainbow[Math.floor(pos/256)](pos%256);
}
},
color_setter:function(name, value){
return this.colormap[value]||dhtmlx.Template.obj_setter(name, value);
},
legend_setter:function(name, config){
if(typeof(config)!="object") //allow to use template string instead of object
config={template:config};
this._mergeSettings(config,{
width:150,
height:18,
align:"left",
valign:"bottom",
template:"",
marker:{
type:"square",
width:25,
height:15
}
});
config.template = dhtmlx.Template.setter(0,config.template);
return config;
},
item_setter:function(name, config){
if(typeof(config)!="object")
config={color:config, borderColor:config};
this._mergeSettings(config,{
radius:4,
color:"#000000",
borderColor:"#000000",
borderWidth:2
});
config.color = dhtmlx.Template.setter(0,config.color);
config.borderColor = dhtmlx.Template.setter(0,config.borderColor);
return config;
},
line_setter:function(name, config){
if(typeof(config)!="object")
config={color:config};
this._mergeSettings(config,{
width:3,
color:"#d4d4d4"
});
config.color = dhtmlx.Template.setter(0,config.color);
return config;
},
padding_setter:function(name, config){
if(typeof(config)!="object")
config={left:config, right:config, top:config, bottom:config};
this._mergeSettings(config,{
left:50,
right:20,
top:35,
bottom:40
});
return config;
},
xAxis_setter:function(name, config){
if(!config) return false;
if(typeof(config)!="object")
config={ template:config };
this._mergeSettings(config,{
title:"",
color:"#000000",
template:"",
lines:false
});
if(config.template)
config.template = dhtmlx.Template.setter(0,config.template);
return config;
},
yAxis_setter:function(name, config){
this._mergeSettings(config,{
title:"",
color:"#000000",
template:"{obj}",
lines:true
});
if(config.template)
config.template = dhtmlx.Template.setter(0,config.template);
return config;
},
_drawXAxis:function(ctx,data,x,y){
if (!this._settings.xAxis) return;
var x0 = parseInt(this._settings.padding.left,10)-0.5;
var y0 = y-this._settings.padding.bottom+0.5;
var x1 = x-this._settings.padding.right;
this._drawLine(ctx,x0,y0,x1,y0,this._settings.xAxis.color,1);
this.renderTextAt(true, false, x0,y-2,
this._settings.xAxis.title,
"dhx_axis_title_x",
x-x0-parseInt(this._settings.padding.right,10)-2
);
/*the right border in lines in scale are enabled*/
if (!this._settings.xAxis||!this._settings.xAxis.lines) return;
this._drawLine(ctx,x1,y0,x1+0.5,this._settings.padding.top+0.5,this._settings.xAxis.color,0.2);
},
_drawYAxis:function(ctx,data,x,y,start,end){
var step;
var scaleParam= {};
if (!this._settings.yAxis) return;
var x0 = parseInt(this._settings.padding.left,10)-0.5;
var y0 = y- parseInt(this._settings.padding.bottom,10);
var y1 = parseInt(this._settings.padding.top,10);
this._drawLine(ctx,x0,y0,x0,y1,this._settings.yAxis.color,1);
if(this._settings.yAxis.step)
step = parseFloat(this._settings.yAxis.step);
if(typeof this._settings.yAxis.step =="undefined"||typeof this._settings.yAxis.start=="undefined"||typeof this._settings.yAxis.end =="undefined"){
scaleParam = this._calculateScale(start,end);
start = scaleParam.start;
end = scaleParam.end;
step = scaleParam.step;
this._settings.yAxis.end = end;
this._settings.yAxis.start = start;
this._settings.yAxis.step = step;
}
if(step===0) return;
var stepHeight = (y0-y1)*step/(end-start);
var c = 0;
for(var i = start; i<=end; i += step){
if(scaleParam.fixNum) i = parseFloat((new Number(i)).toFixed(scaleParam.fixNum));
var yi = Math.floor(y0-c*stepHeight)+ 0.5;/*canvas line fix*/
if(i!=start &&this._settings.yAxis.lines)
this._drawLine(ctx,x0,yi,x-this._settings.padding.right,yi,this._settings.yAxis.color,0.2);
this.renderText(0,yi-5,
this._settings.yAxis.template(i.toString()),
"dhx_axis_item_y",
parseInt(this._settings.padding.left,10)-5
);
c++;
}
this.renderTextAt(true, false, 0,y1-4,this._settings.yAxis.title,"dhx_axis_title_y");
},
_calculateScale:function(nmin,nmax){
var step,start,end;
step = (nmax-nmin)/8;
var power = Math.floor(this.log10(step));
var calculStep = Math.pow(10,power);
var stepVal = step/calculStep;
stepVal = (stepVal>5?10:5);
step = parseInt(stepVal,10)*calculStep;
if(step>Math.abs(nmin)) start = (nmin<0?-step:0);
else{
var absNmin = Math.abs(nmin);
var powerStart = Math.floor(this.log10(absNmin));
var nminVal = absNmin/Math.pow(10,powerStart);
start = Math.ceil(nminVal*10)/10*Math.pow(10,powerStart)-step;
if(nmin<0) start =-start-2*step;
}
var end = start;
while(end<=nmax){
end += step;
end = parseFloat((new Number(end)).toFixed(Math.abs(power)));
}
return {start:start,end:end,step:step,fixNum:Math.abs(power)};
},
log10:function(n){
return Math.floor((Math.log(n)/Math.LN10));
},
_drawXAxisLabel:function(x,obj){
if (!this._settings.xAxis) return;
var elem = this.renderTextAt(false, true, x,
this._obj.offsetHeight-parseInt(this._settings.padding.bottom,10),
this._settings.xAxis.template(obj));
},
_drawXAxisLine:function(ctx,x,y1,y2){
if (!this._settings.xAxis||!this._settings.xAxis.lines) return;
this._drawLine(ctx,x,y1,x,y2,this._settings.xAxis.color,0.2);
},
_drawLine:function(ctx,x1,y1,x2,y2,color,width){
ctx.strokeStyle = color;
ctx.lineWidth = width;
ctx.beginPath();
ctx.moveTo(x1,y1);
ctx.lineTo(x2,y2);
ctx.stroke();
},
_getRelativeValue:function(minValue,maxValue){
var relValue;
var valueFactor = 1;
if(maxValue != minValue){
relValue = maxValue - minValue;
if(Math.abs(relValue) < 1){
while(Math.abs(relValue)<1){
valueFactor *= 10;
relValue *= valueFactor;
}
}
}
else relValue = minValue;
return [relValue,valueFactor];
},
_rainbow : [
function(pos){ return "#FF"+dhtmlx.math.toHex(pos/2,2)+"00";},
function(pos){ return "#FF"+dhtmlx.math.toHex(pos/2+128,2)+"00";},
function(pos){ return "#"+dhtmlx.math.toHex(255-pos,2)+"FF00";},
function(pos){ return "#00FF"+dhtmlx.math.toHex(pos,2);},
function(pos){ return "#00"+dhtmlx.math.toHex(255-pos,2)+"FF";},
function(pos){ return "#"+dhtmlx.math.toHex(pos,2)+"00FF";}
]
};
dhtmlx.compat("layout");