/* 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 */ /* 2012 July 31 */ /* 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.version="3.0"; 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 (i=0; i0){ str=this._toHex[number%16]+str; number=Math.floor(number/16); } while (str.length 255) r = 0; if (g < 0 || g > 255) g = 0; if (b < 0 || b > 255) b = 0; return [r,g,b]; } dhtmlx.math.hsvToRgb = function(h, s, v){ var hi,f,p,q,t,r,g,b; hi = Math.floor((h/60))%6; f = h/60-hi; p = v*(1-s); q = v*(1-f*s); t = v*(1-(1-f)*s); r = 0; g = 0; b = 0; switch(hi) { case 0: r = v; g = t; b = p; break; case 1: r = q; g = v; b = p; break; case 2: r = p; g = v; b = t; break; case 3: r = p; g = q; b = v; break; case 4: r = t; g = p; b = v; break; case 5: r = v; g = p; b = q; break; } r = Math.floor(r*255); g = Math.floor(g*255); b = Math.floor(b*255); return [r, g, b]; }; dhtmlx.math.rgbToHsv = function(r, g, b){ var r0,g0,b0,min0,max0,s,h,v; r0 = r/255; g0 = g/255; b0 = b/255; var min0 = Math.min(r0, g0, b0); var max0 = Math.max(r0, g0, b0); h = 0; s = max0==0?0:(1-min0/max0); v = max0; if (max0 == min0) { h = 0; } else if (max0 == r0 && g0>=b0) { h = 60*(g0 - b0)/(max0 - min0)+0; } else if (max0 == r0 && g0 < b0) { h = 60*(g0 - b0)/(max0 - min0)+360; } else if (max0 == g0) { h = 60*(b0 - r0)/(max0-min0)+120; } else if (max0 == b0) { h = 60*(r0 - g0)/(max0 - min0)+240; } return [h, s, v]; } /* DHX DEPEND FROM FILE 'ext/chart/presets.js'*/ /*chart presents*/ if(!dhtmlx.presets) dhtmlx.presets = {}; dhtmlx.presets.chart = { "simple":{ item:{ borderColor: "#ffffff", color: "#2b7100", shadow: false, borderWidth:2 }, line:{ color:"#8ecf03", width:2 } }, "plot":{ color:"#1293f8", item:{ borderColor:"#636363", borderWidth:1, color: "#ffffff", type:"r", shadow: false }, line:{ color:"#1293f8", width:2 } }, "diamond":{ color:"#b64040", item:{ borderColor:"#b64040", color: "#b64040", type:"d", radius:3, shadow:true }, line:{ color:"#ff9000", width:2 } }, "point":{ color:"#fe5916", disableLines:true, fill:false, disableItems:false, item:{ color:"#feb916", borderColor:"#fe5916", radius:2, borderWidth:1, type:"r" }, alpha:1 }, "line":{ line:{ color:"#3399ff", width:2 }, item:{ color:"#ffffff", borderColor:"#3399ff", radius:2, borderWidth:2, type:"d" }, fill:false, disableItems:false, disableLines:false, alpha:1 }, "area":{ fill:"#3399ff", line:{ color:"#3399ff", width:1 }, disableItems:true, alpha: 0.2, disableLines:false }, "round":{ item:{ radius:3, borderColor:"#3f83ff", borderWidth:1, color:"#3f83ff", type:"r", shadow:false, alpha:0.6 } }, "square":{ item:{ radius:3, borderColor:"#447900", borderWidth:2, color:"#69ba00", type:"s", shadow:false, alpha:1 } }, /*bar*/ "column":{ color:"RAINBOW", gradient:false, width:45, radius:0, alpha:1, border:true }, "stick":{ width:5, gradient:false, color:"#67b5c9", radius:2, alpha:1, border:false }, "alpha":{ color:"#b9a8f9", width:70, gradient:"falling", radius:0, alpha:0.5, border:true } }; /* 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,userdata) { this._createMapArea(id,"RECT",points,userdata); }, addPoly: function(id,points) { this._createMapArea(id,"POLY",points); }, _createMapArea:function(id,shape,coords,userdata){ var extra_data = ""; if(arguments.length==4) extra_data = "userdata='"+userdata+"'"; this._map.push(""); }, 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=""+this._map.join("\n")+""; obj._htmlmap = d; //for clearing routine this._map = []; } }; /* DHX DEPEND FROM FILE 'ext/chart/chart_base.js'*/ /*DHX:Depend map.js*/ dhtmlx.chart = {}; /* DHX DEPEND FROM FILE 'ext/chart/chart_scatter.js'*/ /*DHX:Depend ext/chart/chart_base.js*/ dhtmlx.chart.scatter = { /** * renders a graphic * @param: ctx - canvas object * @param: data - object those need to be displayed * @param: point0 - top left point of a chart * @param: point1 - right bottom point of a chart * @param: sIndex - index of drawing chart * @param: map - map object */ pvt_render_scatter:function(ctx, data, point0, point1, sIndex, map){ if(!this._settings.xValue) return dhtmlx.log("warning","Undefined propery: xValue"); /*max in min values*/ var limitsY = this._getLimits(); var limitsX = this._getLimits("h","xValue"); /*render scale*/ if(!sIndex){ this._drawYAxis(ctx,data,point0,point1,limitsY.min,limitsY.max); this._drawHXAxis(ctx,data,point0,point1,limitsX.min,limitsX.max); } limitsY = {min:this._settings.yAxis.start,max:this._settings.yAxis.end}; limitsX = {min:this._settings.xAxis.start,max:this._settings.xAxis.end}; var params = this._getScatterParams(ctx,data,point0,point1,limitsX,limitsY); for(var i=0;i limits.max) pos = point0[axis.toLowerCase()]; /*the limit of the minimum value*/ if(value < limits.min) pos = point1[axis.toLowerCase()]; return pos; }, _calcScatterUnit:function(p,min,max,size,axis){ var relativeValues = this._getRelativeValue(min,max); axis = (axis||""); p["relValue"+axis] = relativeValues[0]; p["valueFactor"+axis] = relativeValues[1]; p["unit"+axis] = (p["relValue"+axis]?size/p["relValue"+axis]:10); } }; /* DHX DEPEND FROM FILE 'ext/chart/chart_radar.js'*/ /*DHX:Depend ext/chart/chart_base.js*/ dhtmlx.chart.radar = { pvt_render_radar:function(ctx,data,x,y,sIndex,map){ this._renderRadarChart(ctx,data,x,y,sIndex,map); }, /** * renders a pie chart * @param: ctx - canvas object * @param: data - object those need to be displayed * @param: x - the width of the container * @param: y - the height of the container * @param: ky - value from 0 to 1 that defines an angle of inclination (0=start; i -=step){ if(scaleParam.fixNum) i = parseFloat((new Number(i)).toFixed(scaleParam.fixNum)); units.push(Math.floor(c*stepHeight)+ 0.5); if(corr){ i = Math.round(i*corr)/corr; } var unitY = y-radius+units[units.length-1]; this.renderTextAt("middle","left",x,unitY, configY.template(i.toString()), "dhx_axis_item_y dhx_radar" ); if(ratios.length<2){ this._drawScaleSector(ctx,"arc",x,y,radius-units[units.length-1],-Math.PI/2,3*Math.PI/2,i); return; } var startAlpha = -Math.PI/2;/*possibly need to moved in config*/ var alpha0 = startAlpha; var alpha1; for(j=0;j< ratios.length;j++){ if(i==end) angles.push(alpha0); alpha1 = startAlpha+ratios[j]-0.0001; this._drawScaleSector(ctx,(config.lineShape||"line"),x,y,radius-units[units.length-1],alpha0,alpha1,i,j,data[i]); alpha0 = alpha1; } c++; } /*renders radius lines and labels*/ for(i=0;i< angles.length;i++){ p = this._getPositionByAngle(angles[i],x,y,radius); this._drawLine(ctx,x,y,p.x,p.y,(configX?configX.lineColor.call(this,data[i]):"#cfcfcf"),((configX&&configX.lineWidth)?configX.lineWidth.call(this,data[i]):1)); this._drawRadarScaleLabel(ctx,x,y,radius,angles[i],(configX?configX.template.call(this,data[i]):" ")); } }, _drawScaleSector:function(ctx,shape,x,y,radius,a1,a2,i,j){ var pos1, pos2; if(radius<0) return false; pos1 = this._getPositionByAngle(a1,x,y,radius); pos2 = this._getPositionByAngle(a2,x,y,radius); var configY = this._settings.yAxis; if(configY.bg){ ctx.beginPath(); ctx.moveTo(x,y); if(shape=="arc") ctx.arc(x,y,radius,a1,a2,false); else{ ctx.lineTo(pos1.x,pos1.y); ctx.lineTo(pos2.x,pos2.y); } ctx.fillStyle = configY.bg(i,j); ctx.moveTo(x,y); ctx.fill(); ctx.closePath(); } if(configY.lines(i,j)){ ctx.lineWidth = 1; ctx.beginPath(); if(shape=="arc") ctx.arc(x,y,radius,a1,a2,false); else{ ctx.moveTo(pos1.x,pos1.y); ctx.lineTo(pos2.x,pos2.y); } ctx.strokeStyle = configY.lineColor(i,j); ctx.stroke(); } }, _drawRadarScaleLabel:function(ctx,x,y,r,a,text){ var t = this.renderText(0,0,text,"dhx_axis_radar_title",1); var width = t.scrollWidth; var height = t.offsetHeight; var delta = 0.001; var pos = this._getPositionByAngle(a,x,y,r+5); var corr_x=0,corr_y=0; if(a<0||a>Math.PI){ corr_y = -height; } if(a>Math.PI/2){ corr_x = -width; } if(Math.abs(a+Math.PI/2)=0 ;i--){ var xi = x0+ Math.floor(params.cellWidth*i) - 0.5; var yi1 = data[i].$startY; ctx.lineTo(xi,yi1); } } else ctx.lineTo(x0+ Math.floor(params.cellWidth*(length-1)) - 0.5,y01); ctx.lineTo(x0,y01); ctx.fill(); for(var i=0; i < data.length;i ++){ data[i].$startY = y1[i]; } } } }; /* DHX DEPEND FROM FILE 'ext/chart/chart_spline.js'*/ /*DHX:Depend ext/chart/chart_base.js*/ dhtmlx.chart.spline = { /** * renders a spline chart * @param: ctx - canvas object * @param: data - object those need to be displayed * @param: width - the width of the container * @param: height - the height of the container * @param: sIndex - index of drawing chart */ pvt_render_spline:function(ctx, data, point0, point1, sIndex, map){ var areaPos,config,i,items,j,params,radius,sparam,x,x0,x1,x2,y,y1,y2; params = this._calculateParametersOfLineChart(ctx,data,point0,point1,sIndex); config = this._settings; /*array of all points*/ items = []; /*drawing all items*/ if (data.length) { /*getting all points*/ x0 = (config.offset?point0.x+params.cellWidth*0.5:point0.x); for(i=0; i < data.length;i ++){ x = ((!i)?x0:Math.floor(params.cellWidth*i) - 0.5 + x0); y = this._getYPointOfLineChart(data[i],point0,point1,params); items.push({x:x,y:y}); } sparam = this._getSplineParameters(items); for(i =0; i< items.length; i++){ x1 = items[i].x; y1 = items[i].y; if(i=1; i--) s[i] = (v[i] - h[i]*s[i+1])/u[i]; a = []; b = []; c = []; d = []; for(i =0; icellWidth) barWidth = cellWidth/this._series.length-4; /*the half of distance between bars*/ var barOffset = Math.floor((cellWidth - barWidth*this._series.length)/2); /*the radius of rounding in the top part of each bar*/ var radius = (typeof this._settings.radius!="undefined"?parseInt(this._settings.radius,10):Math.round(barWidth/5)); var inner_gradient = false; var gradient = this._settings.gradient; if (gradient&&typeof(gradient) != "function"){ inner_gradient = gradient; gradient = false; } else if (gradient){ gradient = ctx.createLinearGradient(point0.x,point0.y,point1.x,point0.y); this._settings.gradient(gradient); } var scaleY = 0; /*draws a black line if the horizontal scale isn't defined*/ if(!yax){ this._drawLine(ctx,point0.x-0.5,point0.y,point0.x-0.5,point1.y,"#000000",1); //hardcoded color! } for(var i=0; i < data.length;i ++){ var value = parseFloat(this._settings.value(data[i]||0)); if(value>maxValue) value = maxValue; value -= minValue; value *= valueFactor; /*start point (bottom left)*/ var x0 = point0.x; var y0 = point0.y+ barOffset + i*cellWidth+(barWidth+1)*sIndex; if((value<0&&this._settings.origin=="auto")||(this._settings.xAxis&&value===0&&!(this._settings.origin!="auto"&&this._settings.origin>minValue))){ this.renderTextAt("middle", "right", x0+10,y0+barWidth/2+barOffset,this._settings.label(data[i])); continue; } if(value<0&&this._settings.origin!="auto"&&this._settings.origin>minValue){ value = 0; } /*takes start value into consideration*/ if(!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){ this._drawBarHBorder(ctx,x0,y0,barWidth,minValue,radius,unit,value,color); } /*drawing bar body*/ ctx.globalAlpha = this._settings.alpha.call(this,data[i]); var points = this._drawBarH(ctx,point0,x0,y0,barWidth,minValue,radius,unit,value,color,gradient,inner_gradient); if (inner_gradient!=false){ this._drawBarHGradient(ctx,x0,y0,barWidth,minValue,radius,unit,value,color,inner_gradient); } ctx.globalAlpha = 1; /*sets a bar label and map area*/ if(points[3]==y0){ this.renderTextAt("middle", "left", points[0]-5,points[3]+Math.floor(barWidth/2),this._settings.label(data[i])); map.addRect(data[i].id,[points[0],points[3],points[2],points[3]+barWidth],sIndex); }else{ this.renderTextAt("middle", false, points[2]+5,points[1]+Math.floor(barWidth/2),this._settings.label(data[i])); map.addRect(data[i].id,[points[0],y0,points[2],points[3]],sIndex); } } }, /** * sets points for bar and returns the position of the bottom right point * @param: ctx - canvas object * @param: x0 - the x position of start point * @param: y0 - the y position of start point * @param: barWidth - bar width * @param: radius - the rounding radius of the top * @param: unit - the value defines the correspondence between item value and bar height * @param: value - item value * @param: offset - the offset from expected bar edge (necessary for drawing border) */ _setBarHPoints:function(ctx,x0,y0,barWidth,radius,unit,value,offset,skipLeft){ /*correction for displaing small values (when rounding radius is bigger than bar height)*/ var angle_corr = 0; if(radius>unit*value){ var sinA = (radius-unit*value)/radius; angle_corr = -Math.asin(sinA)+Math.PI/2; } /*start*/ ctx.moveTo(x0,y0+offset); /*start of left rounding*/ var x1 = x0 + unit*value - radius - (radius?0:offset); if(radius0) ctx.arc(x1,y2,radius-offset,-Math.PI/2+angle_corr,0,false); /*start of right rounding*/ var y3 = y0 + barWidth - radius - (radius?0:offset); var x3 = x1 + radius - (radius?offset:0); ctx.lineTo(x3,y3); /*right rounding*/ var x4 = x1; if (radius&&radius>0) ctx.arc(x4,y3,radius-offset,0,Math.PI/2-angle_corr,false); /*bottom right point*/ var y5 = y0 + barWidth-offset; ctx.lineTo(x0,y5); /*line to the start point*/ if(!skipLeft){ ctx.lineTo(x0,y0+offset); } // ctx.lineTo(x0,0); //IE fix! return [x3,y5]; }, _drawHScales:function(ctx,data,point0,point1,start,end,cellWidth){ var x = this._drawHXAxis(ctx,data,point0,point1,start,end); this._drawHYAxis(ctx,data,point0,point1,cellWidth,x); }, _drawHYAxis:function(ctx,data,point0,point1,cellWidth,yAxisX){ if (!this._settings.yAxis) return; var unitPos; var x0 = parseInt((yAxisX?yAxisX:point0.x),10)-0.5; var y0 = point1.y+0.5; var y1 = point0.y; this._drawLine(ctx,x0,y0,x0,y1,this._settings.yAxis.color,1); for(var i=0; i < data.length;i ++){ /*scale labels*/ var right = ((this._settings.origin!="auto")&&(this._settings.view=="barH")&&(parseFloat(this._settings.value(data[i]))minValue)){ x += (this._settings.origin-minValue)*unit; axisStart = x; value = value-(this._settings.origin-minValue); if(value < 0){ value *= (-1); ctx.translate(x,y+barWidth); ctx.rotate(Math.PI); x = 0.5; y = 0; } x += 0.5; } return {value:value,x0:x,y0:y,start:axisStart} }, _drawBarH:function(ctx,point0,x0,y0,barWidth,minValue,radius,unit,value,color,gradient,inner_gradient){ ctx.save(); var p = this._correctBarHParams(ctx,x0,y0,value,unit,barWidth,minValue); ctx.fillStyle = color; ctx.beginPath(); var points = this._setBarHPoints(ctx,p.x0,p.y0,barWidth,radius,unit,p.value,(this._settings.border?1:0)); if (gradient&&!inner_gradient) ctx.lineTo(point0.x+total_width,p.y0+(this._settings.border?1:0)); //fix gradient sphreading ctx.fill(); ctx.restore(); var y1 = p.y0; var y2 = (p.y0!=y0?y0:points[1]); var x1 = (p.y0!=y0?(p.start-points[0]):p.start); var x2 = (p.y0!=y0?p.start:points[0]); return [x1,y1,x2,y2]; }, _drawBarHBorder:function(ctx,x0,y0,barWidth,minValue,radius,unit,value,color){ ctx.save(); var p = this._correctBarHParams(ctx,x0,y0,value,unit,barWidth,minValue); ctx.beginPath(); this._setBorderStyles(ctx,color); ctx.globalAlpha =0.9; this._setBarHPoints(ctx,p.x0,p.y0,barWidth,radius,unit,p.value,ctx.lineWidth/2,1); ctx.stroke(); ctx.restore(); }, _drawBarHGradient:function(ctx,x0,y0,barWidth,minValue,radius,unit,value,color,inner_gradient){ ctx.save(); //y0 -= (dhx.env.isIE?0:0.5); var p = this._correctBarHParams(ctx,x0,y0,value,unit,barWidth,minValue); var gradParam = this._setBarGradient(ctx,p.x0,p.y0+barWidth,p.x0+unit*p.value,p.y0,inner_gradient,color,"x"); ctx.fillStyle = gradParam.gradient; ctx.beginPath(); var points = this._setBarHPoints(ctx,p.x0,p.y0+gradParam.offset,barWidth-gradParam.offset*2,radius,unit,p.value,gradParam.offset); ctx.fill(); ctx.globalAlpha = 1; ctx.restore(); } }; /* DHX DEPEND FROM FILE 'ext/chart/chart_stackedbarh.js'*/ /*DHX:Depend ext/chart/chart_base.js*/ /*DHX:Depend ext/chart/chart_barh.js*/ dhtmlx.assert(dhtmlx.chart.barH); dhtmlx.chart.stackedBarH = { /** * renders a bar chart * @param: ctx - canvas object * @param: data - object those need to be displayed * @param: x - the width of the container * @param: y - the height of the container * @param: sIndex - index of drawing chart * @param: map - map object */ pvt_render_stackedBarH:function(ctx, data, point0, point1, sIndex, map){ var maxValue,minValue; /*necessary if maxValue - minValue < 0*/ var valueFactor; /*maxValue - minValue*/ var relValue; var total_width = point1.x-point0.x; var yax = !!this._settings.yAxis; var limits = this._getStackedLimits(data); maxValue = limits.max; minValue = limits.min; /*an available width for one bar*/ var cellWidth = Math.floor((point1.y-point0.y)/data.length); /*draws x and y scales*/ if(!sIndex) this._drawHScales(ctx,data,point0, point1,minValue,maxValue,cellWidth); /*necessary for automatic scale*/ if(yax){ maxValue = parseFloat(this._settings.xAxis.end); minValue = parseFloat(this._settings.xAxis.start); } /*unit calculation (bar_height = value*unit)*/ var relativeValues = this._getRelativeValue(minValue,maxValue); relValue = relativeValues[0]; valueFactor = relativeValues[1]; var unit = (relValue?total_width/relValue:10); if(!yax){ /*defines start value for better representation of small values*/ var startValue = 10; unit = (relValue?(total_width-startValue)/relValue:10); } /*a real bar width */ var barWidth = parseInt(this._settings.width,10); if((barWidth+4)>cellWidth) barWidth = cellWidth-4; /*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 = 0; var inner_gradient = false; var gradient = this._settings.gradient; if (gradient){ inner_gradient = true; } /*draws a black line if the horizontal scale isn't defined*/ if(!yax){ this._drawLine(ctx,point0.x-0.5,point0.y,point0.x-0.5,point1.y,"#000000",1); //hardcoded color! } for(var i=0; i < data.length;i ++){ if(!sIndex) data[i].$startX = point0.x; var value = parseFloat(this._settings.value(data[i]||0)); if(value>maxValue) value = maxValue; value -= minValue; value *= valueFactor; /*start point (bottom left)*/ var x0 = point0.x; var y0 = point0.y+ barOffset + i*cellWidth; if(!sIndex) data[i].$startX = x0; else x0 = data[i].$startX; if(value<0||(this._settings.yAxis&&value===0)){ this.renderTextAt("middle", true, x0+10,y0+barWidth/2,this._settings.label(data[i])); continue; } /*takes start value into consideration*/ if(!yax) value += startValue/unit; var color = this._settings.color.call(this,data[i]); /*drawing bar body*/ ctx.globalAlpha = this._settings.alpha.call(this,data[i]); ctx.fillStyle = this._settings.color.call(this,data[i]); ctx.beginPath(); var points = this._setBarHPoints(ctx,x0,y0,barWidth,radius,unit,value,(this._settings.border?1:0)); if (gradient&&!inner_gradient) ctx.lineTo(point0.x+total_width,y0+(this._settings.border?1:0)); //fix gradient sphreading ctx.fill(); if (inner_gradient!=false){ var gradParam = this._setBarGradient(ctx,x0,y0+barWidth,x0,y0,inner_gradient,color,"x"); ctx.fillStyle = gradParam.gradient; ctx.beginPath(); points = this._setBarHPoints(ctx,x0,y0, barWidth,radius,unit,value,0); ctx.fill(); } /*drawing the gradient border of a bar*/ if(this._settings.border){ this._drawBarHBorder(ctx,x0,y0,barWidth,minValue,radius,unit,value,color); } ctx.globalAlpha = 1; /*sets a bar label*/ this.renderTextAt("middle",true,data[i].$startX+(points[0]-data[i].$startX)/2-1, y0+(points[1]-y0)/2, this._settings.label(data[i])); /*defines a map area for a bar*/ map.addRect(data[i].id,[data[i].$startX,y0,points[0],points[1]],sIndex); /*the start position for the next series*/ data[i].$startX = points[0]; } } }; /* DHX DEPEND FROM FILE 'ext/chart/chart_stackedbar.js'*/ /*DHX:Depend ext/chart/chart_base.js*/ dhtmlx.chart.stackedBar = { /** * renders a bar chart * @param: ctx - canvas object * @param: data - object those need to be displayed * @param: x - the width of the container * @param: y - the height of the container * @param: sIndex - index of drawing chart */ pvt_render_stackedBar:function(ctx, data, point0, point1, sIndex, map){ var maxValue,minValue; /*necessary if maxValue - minValue < 0*/ var valueFactor; /*maxValue - minValue*/ var relValue; var total_height = point1.y-point0.y; var yax = !!this._settings.yAxis; var xax = !!this._settings.xAxis; var limits = this._getStackedLimits(data); maxValue = limits.max; minValue = limits.min; /*an available width for one bar*/ var cellWidth = Math.floor((point1.x-point0.x)/data.length); /*draws x and y scales*/ if(!sIndex) this._drawScales(ctx,data,point0, point1,minValue,maxValue,cellWidth); /*necessary for automatic scale*/ if(yax){ maxValue = parseFloat(this._settings.yAxis.end); minValue = parseFloat(this._settings.yAxis.start); } /*unit calculation (bar_height = value*unit)*/ var relativeValues = this._getRelativeValue(minValue,maxValue); relValue = relativeValues[0]; valueFactor = relativeValues[1]; var unit = (relValue?total_height/relValue:10); /*a real bar width */ var barWidth = parseInt(this._settings.width,10); if(barWidth+4 > cellWidth) barWidth = cellWidth-4; /*the half of distance between bars*/ var barOffset = Math.floor((cellWidth - barWidth)/2); var inner_gradient = (this._settings.gradient?this._settings.gradient:false); /*draws a black line if the horizontal scale isn't defined*/ if(!xax){ //scaleY = y-bottomPadding; this._drawLine(ctx,point0.x,point1.y+0.5,point1.x,point1.y+0.5,"#000000",1); //hardcoded color! } for(var i=0; i < data.length;i ++){ var value = parseFloat(this._settings.value(data[i]||0)); if(!value){ if(!sIndex||!data[i].$startY) data[i].$startY = point1.y; continue; } /*adjusts the first tab to the scale*/ if(!sIndex) value -= minValue; value *= valueFactor; /*start point (bottom left)*/ var x0 = point0.x + barOffset + i*cellWidth; var y0 = point1.y; if(!sIndex) data[i].$startY = y0; else y0 = data[i].$startY; /*the max height limit*/ if(y0 < (point0.y+1)) continue; if(value<0||(this._settings.yAxis&&value===0)){ this.renderTextAt(true, true, x0+Math.floor(barWidth/2),y0,this._settings.label(data[i])); continue; } var color = this._settings.color.call(this,data[i]); /*drawing bar body*/ ctx.globalAlpha = this._settings.alpha.call(this,data[i]); ctx.fillStyle = this._settings.color.call(this,data[i]); ctx.beginPath(); var points = this._setStakedBarPoints(ctx,x0-(this._settings.border?0.5:0),y0,barWidth+(this._settings.border?0.5:0),unit,value,0,point0.y); ctx.fill(); /*gradient*/ if (inner_gradient){ ctx.save(); var gradParam = this._setBarGradient(ctx,x0,y0,x0+barWidth,points[1],inner_gradient,color,"y"); ctx.fillStyle = gradParam.gradient; ctx.beginPath(); points = this._setStakedBarPoints(ctx,x0+gradParam.offset,y0,barWidth-gradParam.offset*2,unit,value,(this._settings.border?1:0),point0.y); ctx.fill(); ctx.restore() } /*drawing the gradient border of a bar*/ if(this._settings.border){ ctx.save(); this._setBorderStyles(ctx,color); ctx.beginPath(); this._setStakedBarPoints(ctx,x0-0.5,y0,barWidth+1,unit,value,0,point0.y,1); ctx.stroke(); ctx.restore(); } ctx.globalAlpha = 1; /*sets a bar label*/ this.renderTextAt(false, true, x0+Math.floor(barWidth/2),(points[1]+(y0-points[1])/2)-7,this._settings.label(data[i])); /*defines a map area for a bar*/ map.addRect(data[i].id,[x0,points[1],points[0],(data[i].$startY||y0)],sIndex); /*the start position for the next series*/ data[i].$startY = (this._settings.border?(points[1]+1):points[1]); } }, /** * sets points for bar and returns the position of the bottom right point * @param: ctx - canvas object * @param: x0 - the x position of start point * @param: y0 - the y position of start point * @param: barWidth - bar width * @param: radius - the rounding radius of the top * @param: unit - the value defines the correspondence between item value and bar height * @param: value - item value * @param: offset - the offset from expected bar edge (necessary for drawing border) * @param: minY - the minimum y position for the bars () */ _setStakedBarPoints:function(ctx,x0,y0,barWidth,unit,value,offset,minY,skipBottom){ /*start*/ ctx.moveTo(x0,y0); /*start of left rounding*/ var y1 = y0 - unit*value+offset; /*maximum height limit*/ if(y1=0;i--){ ctx.globalAlpha = alphas[i]; ctx.strokeStyle = "#d0d0d0"; ctx.beginPath(); this._strokeChartItem(ctx,x0,y0+2*R/3,R+i+1,config.type); ctx.stroke(); } ctx.beginPath(); ctx.globalAlpha = 0.3; ctx.fillStyle = "#bdbdbd"; this._strokeChartItem(ctx,x0,y0+2*R/3,R+1,config.type); ctx.fill(); } ctx.restore(); ctx.lineWidth = config.borderWidth; ctx.fillStyle = config.color.call(this,obj); ctx.strokeStyle = config.borderColor.call(this,obj); ctx.globalAlpha = config.alpha.call(this,obj); ctx.beginPath(); this._strokeChartItem(ctx,x0,y0,R+1,config.type); ctx.fill(); ctx.stroke(); ctx.globalAlpha = 1; /*item label*/ if(label) this.renderTextAt(false, true, x0,y0-R-this._settings.labelOffset,this._settings.label.call(this,obj)); }, _strokeChartItem:function(ctx,x0,y0,R,type){ if(type && (type=="square" || type=="s")){ R *= Math.sqrt(2)/2; ctx.moveTo(x0-R-ctx.lineWidth/2,y0-R); ctx.lineTo(x0+R,y0-R); ctx.lineTo(x0+R,y0+R); ctx.lineTo(x0-R,y0+R); ctx.lineTo(x0-R,y0-R); } else if(type && (type=="diamond" || type=="d")){ var corr = (ctx.lineWidth>1?ctx.lineWidth*Math.sqrt(2)/4:0); ctx.moveTo(x0,y0-R); ctx.lineTo(x0+R,y0); ctx.lineTo(x0,y0+R); ctx.lineTo(x0-R,y0); ctx.lineTo(x0+corr,y0-R-corr); } else if(type && (type=="triangle" || type=="t")){ ctx.moveTo(x0,y0-R); ctx.lineTo(x0+Math.sqrt(3)*R/2,y0+R/2); ctx.lineTo(x0-Math.sqrt(3)*R/2,y0+R/2); ctx.lineTo(x0,y0-R); } else ctx.arc(x0,y0,R,0,Math.PI*2,true); }, /** * gets the vertical position of the item * @param: data - data object * @param: y0 - the y position of chart start * @param: y1 - the y position of chart end * @param: params - the object with elements: minValue, maxValue, unit, valueFactor (the value multiple of 10) */ _getYPointOfLineChart: function(data,point0,point1,params){ var minValue = params.minValue; var maxValue = params.maxValue; var unit = params.unit; var valueFactor = params.valueFactor; /*the real value of an object*/ var value = this._settings.value(data); /*a relative value*/ var v = (parseFloat(value||0) - minValue)*valueFactor; if(!this._settings.yAxis) v += params.startValue/unit; /*a vertical coordinate*/ var y = point1.y - Math.floor(unit*v); /*the limit of the minimum value is the minimum visible value*/ if(v<0) y = point1.y; /*the limit of the maximum value*/ if(value > maxValue) y = point0.y; /*the limit of the minimum value*/ if(value < minValue) y = point1.y; return y; }, _calculateParametersOfLineChart: function(ctx,data,point0,point1,sIndex){ var params = {}; /*maxValue - minValue*/ var relValue; /*available height*/ params.totalHeight = point1.y-point0.y; /*a space available for a single item*/ //params.cellWidth = Math.round((point1.x-point0.x)/((!this._settings.offset&&this._settings.yAxis)?(data.length-1):data.length)); params.cellWidth = Math.round((point1.x-point0.x)/((!this._settings.offset)?(data.length-1):data.length)); /*scales*/ var yax = !!this._settings.yAxis; var limits = (this._settings.view.indexOf("stacked")!=-1?this._getStackedLimits(data):this._getLimits()); params.maxValue = limits.max; params.minValue = limits.min; /*draws x and y scales*/ if(!sIndex) this._drawScales(ctx,data, point0, point1,params.minValue,params.maxValue,params.cellWidth); /*necessary for automatic scale*/ if(yax){ params.maxValue = parseFloat(this._settings.yAxis.end); params.minValue = parseFloat(this._settings.yAxis.start); } /*unit calculation (y_position = value*unit)*/ var relativeValues = this._getRelativeValue(params.minValue,params.maxValue); relValue = relativeValues[0]; params.valueFactor = relativeValues[1]; params.unit = (relValue?params.totalHeight/relValue:10); params.startValue = 0; if(!yax){ /*defines start value for better representation of small values*/ params.startValue = 10; if(params.unit!=params.totalHeight) params.unit = (relValue?(params.totalHeight - params.startValue)/relValue:10); } return params; } }; /* DHX DEPEND FROM FILE 'ext/chart/chart_bar.js'*/ /*DHX:Depend ext/chart/chart_base.js*/ 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 * @param: sIndex - index of drawing chart */ pvt_render_bar:function(ctx, data, point0, point1, sIndex, map){ var maxValue,minValue; /*necessary if maxValue - minValue < 0*/ var valueFactor; /*maxValue - minValue*/ var relValue; var total_height = point1.y-point0.y; var yax = !!this._settings.yAxis; var xax = !!this._settings.xAxis; var limits = this._getLimits(); maxValue = limits.max; minValue = limits.min; /*an available width for one bar*/ var cellWidth = Math.floor((point1.x-point0.x)/data.length); /*draws x and y scales*/ if(!sIndex&&!(this._settings.origin!="auto"&&!yax)){ this._drawScales(ctx,data,point0, point1,minValue,maxValue,cellWidth); } /*necessary for automatic scale*/ if(yax){ maxValue = parseFloat(this._settings.yAxis.end); minValue = parseFloat(this._settings.yAxis.start); } /*unit calculation (bar_height = value*unit)*/ var relativeValues = this._getRelativeValue(minValue,maxValue); relValue = relativeValues[0]; valueFactor = relativeValues[1]; var unit = (relValue?total_height/relValue:relValue); if(!yax&&!(this._settings.origin!="auto"&&xax)){ /*defines start value for better representation of small values*/ var startValue = 10; unit = (relValue?(total_height-startValue)/relValue:startValue); } /*if yAxis isn't set, but with custom origin */ if(!sIndex&&(this._settings.origin!="auto"&&!yax)&&this._settings.origin>minValue){ this._drawXAxis(ctx,data,point0,point1,cellWidth,point1.y-unit*(this._settings.origin-minValue)); } /*a real bar width */ var barWidth = parseInt(this._settings.width,10); if(this._series&&(barWidth*this._series.length+4)>cellWidth) barWidth = parseInt(cellWidth/this._series.length-4,10); /*the half of distance between bars*/ var barOffset = Math.floor((cellWidth - barWidth*this._series.length)/2); /*the radius of rounding in the top part of each bar*/ var radius = (typeof this._settings.radius!="undefined"?parseInt(this._settings.radius,10):Math.round(barWidth/5)); var inner_gradient = false; var gradient = this._settings.gradient; if(gradient && typeof(gradient) != "function"){ inner_gradient = gradient; gradient = false; } else if (gradient){ gradient = ctx.createLinearGradient(0,point1.y,0,point0.y); this._settings.gradient(gradient); } /*draws a black line if the horizontal scale isn't defined*/ if(!xax){ this._drawLine(ctx,point0.x,point1.y+0.5,point1.x,point1.y+0.5,"#000000",1); //hardcoded color! } for(var i=0; i < data.length;i ++){ var value = parseFloat(this._settings.value(data[i]||0)); if(value>maxValue) value = maxValue; value -= minValue; value *= valueFactor; /*start point (bottom left)*/ var x0 = point0.x + barOffset + i*cellWidth+(barWidth+1)*sIndex; var y0 = point1.y+0.5; if(value<0||(this._settings.yAxis&&value===0&&!(this._settings.origin!="auto"&&this._settings.origin>minValue))){ this.renderTextAt(true, true, x0+Math.floor(barWidth/2),y0,this._settings.label(data[i])); continue; } /*takes start value into consideration*/ if(!yax&&!(this._settings.origin!="auto"&&xax)) value += startValue/unit; var color = gradient||this._settings.color.call(this,data[i]); /*drawing bar body*/ ctx.globalAlpha = this._settings.alpha.call(this,data[i]); var points = this._drawBar(ctx,point0,x0,y0,barWidth,minValue,radius,unit,value,color,gradient,inner_gradient); if (inner_gradient){ this._drawBarGradient(ctx,x0,y0,barWidth,minValue,radius,unit,value,color,inner_gradient); } /*drawing the gradient border of a bar*/ if(this._settings.border) this._drawBarBorder(ctx,x0,y0,barWidth,minValue,radius,unit,value,color); ctx.globalAlpha = 1; /*sets a bar label*/ if(points[0]!=x0) this.renderTextAt(false, true, x0+Math.floor(barWidth/2),points[1],this._settings.label(data[i])); else this.renderTextAt(true, true, x0+Math.floor(barWidth/2),points[3],this._settings.label(data[i])); /*defines a map area for a bar*/ map.addRect(data[i].id,[x0,points[3],points[2],points[1]],sIndex); } }, _correctBarParams:function(ctx,x,y,value,unit,barWidth,minValue){ var xax = this._settings.xAxis; var axisStart = y; if(!!xax&&this._settings.origin!="auto" && (this._settings.origin>minValue)){ y -= (this._settings.origin-minValue)*unit; axisStart = y; value = value-(this._settings.origin-minValue); if(value < 0){ value *= (-1); ctx.translate(x+barWidth,y); ctx.rotate(Math.PI); x = 0; y = 0; } y -= 0.5; } return {value:value,x0:parseInt(x,10),y0:parseInt(y,10),start:axisStart} }, _drawBar:function(ctx,point0,x0,y0,barWidth,minValue,radius,unit,value,color,gradient,inner_gradient){ ctx.save(); ctx.fillStyle = color; var p = this._correctBarParams(ctx,x0,y0,value,unit,barWidth,minValue); var points = this._setBarPoints(ctx,p.x0,p.y0,barWidth,radius,unit,p.value,(this._settings.border?1:0)); if (gradient&&!inner_gradient) ctx.lineTo(p.x0+(this._settings.border?1:0),point0.y); //fix gradient sphreading ctx.fill(); ctx.restore(); var x1 = p.x0; var x2 = (p.x0!=x0?x0+points[0]:points[0]); var y1 = (p.x0!=x0?(p.start-points[1]):y0); var y2 = (p.x0!=x0?p.start:points[1]); return [x1,y1,x2,y2]; }, _setBorderStyles:function(ctx,color){ var hsv,rgb; rgb = dhtmlx.math.toRgb(color); hsv = dhtmlx.math.rgbToHsv(rgb[0],rgb[1],rgb[2]); hsv[2] /= 2; color = "rgb("+dhtmlx.math.hsvToRgb(hsv[0],hsv[1],hsv[2])+")"; ctx.strokeStyle = color; if(ctx.globalAlpha==1) ctx.globalAlpha = 0.9; }, _drawBarBorder:function(ctx,x0,y0,barWidth,minValue,radius,unit,value,color){ var p; ctx.save(); p = this._correctBarParams(ctx,x0,y0,value,unit,barWidth,minValue); this._setBorderStyles(ctx,color); this._setBarPoints(ctx,p.x0,p.y0,barWidth,radius,unit,p.value,ctx.lineWidth/2,1); ctx.stroke(); /*ctx.fillStyle = color; this._setBarPoints(ctx,p.x0,p.y0,barWidth,radius,unit,p.value,0); ctx.lineTo(p.x0,0); ctx.fill() ctx.fillStyle = "#000000"; ctx.globalAlpha = 0.37; this._setBarPoints(ctx,p.x0,p.y0,barWidth,radius,unit,p.value,0); ctx.fill() */ ctx.restore(); }, _drawBarGradient:function(ctx,x0,y0,barWidth,minValue,radius,unit,value,color,inner_gradient){ ctx.save(); //y0 -= (dhtmlx._isIE?0:0.5); var p = this._correctBarParams(ctx,x0,y0,value,unit,barWidth,minValue); var gradParam = this._setBarGradient(ctx,p.x0,p.y0,p.x0+barWidth,p.y0-unit*p.value+2,inner_gradient,color,"y"); var borderOffset = this._settings.border?1:0; ctx.fillStyle = gradParam.gradient; this._setBarPoints(ctx,p.x0+gradParam.offset,p.y0,barWidth-gradParam.offset*2,radius,unit,p.value,gradParam.offset+borderOffset); ctx.fill(); ctx.restore(); }, /** * sets points for bar and returns the position of the bottom right point * @param: ctx - canvas object * @param: x0 - the x position of start point * @param: y0 - the y position of start point * @param: barWidth - bar width * @param: radius - the rounding radius of the top * @param: unit - the value defines the correspondence between item value and bar height * @param: value - item value * @param: offset - the offset from expected bar edge (necessary for drawing border) */ _setBarPoints:function(ctx,x0,y0,barWidth,radius,unit,value,offset,skipBottom){ /*correction for displaing small values (when rounding radius is bigger than bar height)*/ ctx.beginPath(); //y0 = 0.5; var angle_corr = 0; if(radius>unit*value){ var cosA = (radius-unit*value)/radius; if(cosA<=1&&cosA>=-1) angle_corr = -Math.acos(cosA)+Math.PI/2; } /*start*/ ctx.moveTo(x0+offset,y0); /*start of left rounding*/ var y1 = y0 - Math.floor(unit*value) + radius + (radius?0:offset); if(radius0) ctx.arc(x2,y1,radius-offset,-Math.PI+angle_corr,-Math.PI/2,false); /*start of right rounding*/ var x3 = x0 + barWidth - radius - (radius?0:offset); var y3 = y1 - radius+(radius?offset:0); ctx.lineTo(x3,y3); /*right rounding*/ if (radius&&radius>0) ctx.arc(x3,y1,radius-offset,-Math.PI/2,0-angle_corr,false); /*bottom right point*/ var x5 = x0 + barWidth-offset; ctx.lineTo(x5,y0); /*line to the start point*/ if(!skipBottom){ ctx.lineTo(x0+offset,y0); } // ctx.lineTo(x0,0); //IE fix! return [x5,y3]; } }; /* DHX DEPEND FROM FILE 'ext/chart/chart_pie.js'*/ /*DHX:Depend ext/chart/chart_base.js*/ dhtmlx.chart.pie = { pvt_render_pie:function(ctx,data,x,y,sIndex,map){ this._renderPie(ctx,data,x,y,1,map); }, /** * renders a pie chart * @param: ctx - canvas object * @param: data - object those need to be displayed * @param: x - the width of the container * @param: y - the height of the container * @param: ky - value from 0 to 1 that defines an angle of inclination (0=0)||(a1>=0 && a2<=Math.PI)||(Math.abs(a1-Math.PI)>0.003&&a1<=Math.PI && a2>=Math.PI))) return; if(a1<=0 && a2>=0){ a1 = 0; line = false; this._drawSectorLine(ctx,x0,y0,R,a1,a2); } if(a1<=Math.PI && a2>=Math.PI){ a2 = Math.PI; line = false; this._drawSectorLine(ctx,x0,y0,R,a1,a2); } /*the height of 3D pie*/ var offset = (this._settings.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){ ctx.globalAlpha = 0.5; var shadows = ["#c4c4c4","#c6c6c6","#cacaca","#dcdcdc","#dddddd","#e0e0e0","#eeeeee","#f5f5f5","#f8f8f8"]; for(var i = shadows.length-1;i>-1;i--){ ctx.beginPath(); ctx.fillStyle = shadows[i]; ctx.arc(x+1,y+1,R+i,0,Math.PI*2,true); ctx.fill(); } ctx.globalAlpha = 1 }, /** * returns a gray gradient * @param: gradient - gradient object */ _getGrayGradient:function(gradient){ gradient.addColorStop(0.0,"#ffffff"); gradient.addColorStop(0.7,"#7a7a7a"); gradient.addColorStop(1.0,"#000000"); return gradient; }, /** * adds gray radial gradient * @param: ctx - canvas object * @param: x - the horizontal position of the pie center * @param: y - the vertical position of the pie center * @param: radius - pie radius * @param: x0 - the horizontal position of a gradient center * @param: y0 - the vertical position of a gradient center */ _showRadialGradient:function(ctx,x,y,radius,x0,y0){ //ctx.globalAlpha = 0.3; ctx.beginPath(); var gradient; if(typeof this._settings.gradient!= "function"){ gradient = ctx.createRadialGradient(x0,y0,radius/4,x,y,radius); gradient = this._getGrayGradient(gradient); } else gradient = this._settings.gradient(gradient); ctx.fillStyle = gradient; ctx.arc(x,y,radius,0,Math.PI*2,true); ctx.fill(); //ctx.globalAlpha = 1; ctx.globalAlpha = 0.7; }, /** * returns the calculates pie parameters: center position and radius * @param: ctx - canvas object * @param: x0 - the horizontal position of the pie center * @param: y0 - the vertical position of the pie center * @param: R - pie radius * @param: alpha1 - the angle that defines the 1st edge of a sector * @param: alpha2 - the angle that defines the 2nd edge of a sector * @param: ky - the value that defines an angle of inclination * @param: text - label text * @param: in_width (boolean) - if label needs being displayed inside a pie */ _drawSectorLabel:function(x0,y0,R,alpha1,alpha2,ky,text,in_width){ var t = this.renderText(0,0,text,0,1); if (!t) return; //get existing width of text var labelWidth = t.scrollWidth; t.style.width = labelWidth+"px"; //adjust text label to fit all text if (labelWidth>x0) labelWidth = x0; //the text can't be greater than half of view //calculate expected correction based on default font metrics var width = (alpha2-alpha1<0.2?4:8); if (in_width) width = labelWidth/1.8; var alpha = alpha1+(alpha2-alpha1)/2; //position and its correction R = R-(width-8)/2; var corr_x = - width; var corr_y = -8; var align = "right"; //for items in left upper and lower sector if(alpha>=Math.PI/2 && alpha=Math.PI){ corr_x = -labelWidth-corr_x+1;/*correction for label width*/ align = "left"; } //calculate position of text //basically get point at center of pie sector var offset = 0; if(!in_width&&ky<1&&(alpha>0&&alpha=Math.PI/2 && alpha=Math.PI)){ x += labelWidth/3; } //we need to set position of text manually, based on above calculations t.style.top = y+"px"; t.style.left = x+"px"; t.style.width = labelWidth+"px"; t.style.textAlign = align; t.style.whiteSpace = "nowrap"; } }; dhtmlx.chart.pie3D = { pvt_render_pie3D:function(ctx,data,x,y,sIndex,map){ this._renderPie(ctx,data,x,y,this._settings.cant,map); } }; dhtmlx.chart.donut = { pvt_render_donut:function(ctx,data,point0,point1,sIndex,map){ if(!data.length) return; this._renderPie(ctx,data,point0,point1,1,map); var config = this._settings; var coord = this._getPieParameters(point0,point1); var pieRadius = (config.radius?config.radius:coord.radius); var innerRadius = ((config.innerRadius&&(config.innerRadius 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){ 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 'load.js'*/ /* ajax operations can be used for direct loading as dhtmlx.ajax(ulr, callback) or dhtmlx.ajax().item(url) dhtmlx.ajax().post(url) */ /*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){ var value = params[a]; if (value === null || value === dhtmlx.undefined) value = ""; t.push(a+"="+encodeURIComponent(value));// 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=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; } }; dhtmlx.AtomDataLoader={ _init:function(config){ //prepare data store this.data = {}; if (config){ this._settings.datatype = config.datatype||"json"; this._after_init.push(this._load_when_ready); } }, _load_when_ready:function(){ this._ready_for_data = true; if (this._settings.url) this.url_setter(this._settings.url); if (this._settings.data) this.data_setter(this._settings.data); }, url_setter:function(value){ if (!this._ready_for_data) return value; this.load(value, this._settings.datatype); return value; }, data_setter:function(value){ if (!this._ready_for_data) return value; this.parse(value, this._settings.datatype); return true; }, //loads data from external URL load:function(url,call){ this.callEvent("onXLS",[]); if (typeof call == "string"){ //second parameter can be a loading type or callback this.data.driver = dhtmlx.DataDriver[call]; call = arguments[2]; } else this.data.driver = dhtmlx.DataDriver["xml"]; //load data by async ajax call dhtmlx.ajax(url,[this._onLoad,call],this); }, //loads data from object parse:function(data,type){ this.callEvent("onXLS",[]); this.data.driver = dhtmlx.DataDriver[type||"xml"]; this._onLoad(data,null); }, //default after loading callback _onLoad:function(text,xml,loader){ var driver = this.data.driver; var top = driver.getRecords(driver.toObject(text,xml))[0]; this.data=(driver?driver.getDetails(top):text); this.callEvent("onXLE",[]); }, _check_data_feed:function(data){ if (!this._settings.dataFeed || this._ignore_feed || !data) return true; var url = this._settings.dataFeed; if (typeof url == "function") return url.call(this, (data.id||data), data); url = url+(url.indexOf("?")==-1?"?":"&")+"action=get&id="+encodeURIComponent(data.id||data); this.callEvent("onXLS",[]); dhtmlx.ajax(url, function(text,xml){ this._ignore_feed=true; this.setValues(dhtmlx.DataDriver.json.toObject(text)[0]); this._ignore_feed=false; this.callEvent("onXLE",[]); }, this); return false; } }; /* Abstraction layer for different data types */ dhtmlx.DataDriver={}; dhtmlx.DataDriver.json={ //convert json string to json object if necessary toObject:function(data){ if (!data) 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), _key:(data.dhx_security) }; } }; dhtmlx.DataDriver.json_ext={ //convert json string to json object if necessary toObject:function(data){ if (!data) data="[]"; if (typeof data == "string"){ var temp; eval ("temp="+data); dhtmlx.temp = []; var header = temp.header; for (var i = 0; i < temp.data.length; i++) { var item = {}; for (var j = 0; j < header.length; j++) { if (typeof(temp.data[i][j]) != "undefined") item[header[j]] = temp.data[i][j]; } dhtmlx.temp.push(item); } 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", //get hash of properties for single record getDetails:function(data){ return this.tagToObject(data,{}); }, //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), _key:(data.documentElement.getAttribute("dhx_security")) }; }, //xpath helper xpath:function(xml,path){ if (window.XPathResult){ //FF, KHTML, Opera var node=xml; if(xml.nodeName.indexOf("document")==-1) xml=xml.ownerDocument; var res = []; var col = xml.evaluate(path, node, null, XPathResult.ANY_TYPE, null); var temp = col.iterateNext(); while (temp){ res.push(temp); temp = col.iterateNext(); } return res; } else { var test = true; try { if (typeof(xml.selectNodes)=="undefined") test = false; } catch(e){ /*IE7 and below can't operate with xml object*/ } //IE if (test) return xml.selectNodes(path); else { //Google hate us, there is no interface to do XPath //use naive approach var name = path.split("/").pop(); return xml.getElementsByTagName(name); } } }, //convert xml tag to js object, all subtags and attributes are mapped to the properties of result object tagToObject:function(tag,z){ z=z||{}; var flag=false; //map attributes var a=tag.attributes; if(a && a.length){ 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 max) max = property(obj)*1; }); return max; }, _split_data_by:function(stats){ var any=function(property, data){ property = dhtmlx.Template.setter(property); return property(data[0]); }; var key = dhtmlx.Template.setter(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(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(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.which||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.on_click){ dhtmlx.event(this._obj,"click",this._onClick,this); dhtmlx.event(this._obj,"contextmenu",this._onContext,this); } if (this.on_dblclick) dhtmlx.event(this._obj,"dblclick",this._onDblClick,this); if (this.on_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.on_click,"ItemClick"); }, //inner ondblclick object handler _onDblClick: function(e) { return this._mouseEvent(e,this.on_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.on_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 && trg.getAttribute){ //if element with ID mark is not detected yet id = trg.getAttribute(this._id); //check id of current one if (id){ if (trg.getAttribute("userdata")) this.callEvent("onLocateData",[id,trg]); 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= {}; }, 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,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(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(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(value){ return this.type_setter({template:value}); }, //config.css - css name for top level container css_setter:function(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 hash 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)?"&":"?")+this.serialize(a1,rowId),"",function(t,x,xml){ loader.xml = dhtmlx.DataDriver.xml.checkResponse(t,x); a2.afterUpdate(a2, null, null, null, loader); }); else dhtmlx.ajax().post(a3,this.serialize(a1,rowId),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; 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(){}; /* 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 thirdparty\excanvas*/ /*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){ dhtmlx.require("thirdparty/excanvas/excanvas.js"); //sync loading 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(valign,align, x,y,t,c,w){ var text=this.renderText.call(this,x,y,t,c,w); if (text){ if (valign){ if(valign == "middle") text.style.top = parseInt(y-text.offsetHeight/2,10) + "px"; else text.style.top = y-text.offsetHeight + "px"; } if (align){ if(align == "left") text.style.left = x-text.offsetWidth + "px"; else text.style.left = parseInt(x-text.offsetWidth/2,10) + "px"; } } return text; }, clearCanvas:function(){ 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 'G:\dhtmlx.out\Standard\dhtmlxCore/sources//chart.js'*/ /*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 ext/chart/chart_base.js*/ /*DHX:Depend ext/chart/chart_pie.js*/ //+pie3d /*DHX:Depend ext/chart/chart_bar.js*/ /*DHX:Depend ext/chart/chart_line.js*/ /*DHX:Depend ext/chart/chart_barh.js*/ /*DHX:Depend ext/chart/chart_stackedbar.js*/ /*DHX:Depend ext/chart/chart_stackedbarh.js*/ /*DHX:Depend ext/chart/chart_spline.js*/ /*DHX:Depend ext/chart/chart_area.js*/ //+stackedArea /*DHX:Depend ext/chart/chart_radar.js*/ /*DHX:Depend ext/chart/chart_scatter.js*/ /*DHX:Depend ext/chart/presets.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.AtomDataLoader); 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); for (var key in dhtmlx.chart) dhtmlx.extend(this, dhtmlx.chart[key]); if(container.preset){ this.definePreset(container); } this._parseSettings(container,this.defaults); this._series = [this._settings]; this.data.attachEvent("onStoreUpdated",dhtmlx.bind(function(){ this.render(); },this)); this.attachEvent("onLocateData", this._switchSerie); }; dhtmlXChart.prototype={ _id:"dhx_area_id", on_click:{ }, on_dblclick:{ }, on_mouse_move:{ }, bind:function(){ dhx.BaseBind.legacyBind.apply(this, arguments); }, sync:function(){ dhx.BaseBind.legacySync.apply(this, arguments); }, resize:function(){ this._resizeCanvas(); this.render(); }, view_setter:function( val){ if (!dhtmlx.chart[val]) dhtmlx.error("Chart type extension is not loaded: "+val); //if you will need to add more such settings - move them ( and this one ) in a separate methods if (typeof this._settings.offset == "undefined"){ this._settings.offset = !(val == "area" || val == "stackedArea"); } if(val=="radar"&&!this._settings.yAxis) this.define("yAxis",{}); if(val=="scatter"){ if(!this._settings.yAxis) this.define("yAxis",{}); if(!this._settings.xAxis) this.define("xAxis",{}); } return val; }, render:function(){ if (!this.callEvent("onBeforeRender",[this.data])) return; this.clearCanvas(); if(this._settings.legend){ this._drawLegend(this.getCanvas(), this.data.getRange(), this._obj.offsetWidth ); } var bounds = this._getChartBounds(this._obj.offsetWidth,this._obj.offsetHeight); var map = new dhtmlx.ui.Map(this._id); var temp = this._settings; for(var i=0; i < this._series.length;i++){ this._settings = this._series[i]; this["pvt_render_"+this._settings.view]( this.getCanvas(), this.data.getRange(), bounds.start, bounds.end, i, map ); } map.render(this._obj); this._settings = temp; }, value_setter:dhtmlx.Template.obj_setter, xValue_setter:dhtmlx.Template.obj_setter, yValue_setter:function(config){ this.define("value",config); }, alpha_setter:dhtmlx.Template.obj_setter, label_setter:dhtmlx.Template.obj_setter, lineColor_setter:dhtmlx.Template.obj_setter, pieInnerText_setter:dhtmlx.Template.obj_setter, gradient_setter:function(config){ if((typeof(config)!="function")&&config&&(config === true)) config = "light"; return config; }, 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(value){ return this.colormap[value]||dhtmlx.Template.obj_setter( value); }, fill_setter:function(value){ return ((!value||value==0)?false:dhtmlx.Template.obj_setter( value)); }, definePreset:function(obj){ this.define("preset",obj.preset); delete obj.preset; }, preset_setter:function(value){ var a, b, preset; this.defaults = dhtmlx.extend({},this.defaults); if(typeof dhtmlx.presets.chart[value]=="object"){ preset = dhtmlx.presets.chart[value]; for(a in preset){ if(typeof preset[a]=="object"){ if(!this.defaults[a]||typeof this.defaults[a]!="object"){ this.defaults[a] = dhtmlx.extend({},preset[a]); } else{ this.defaults[a] = dhtmlx.extend({},this.defaults[a]); for(b in preset[a]){ this.defaults[a][b] = preset[a][b]; } } }else{ this.defaults[a] = preset[a]; } } return value; } return false; }, legend_setter:function( config){ if(!config){ if(this.legendObj){ this.legendObj.innerHTML = ""; this.legendObj = null; } return false; } if(typeof(config)!="object") //allow to use template string instead of object config={template:config}; this._mergeSettings(config,{ width:150, height:18, layout:"y", align:"left", valign:"bottom", template:"", marker:{ type:"square", width:15, height:15, radius:3 }, margin: 4, padding: 3 }); config.template = dhtmlx.Template.setter(config.template); return config; }, defaults:{ color:"RAINBOW", alpha:"1", label:false, value:"{obj.value}", padding:{}, view:"pie", lineColor:"#ffffff", cant:0.5, width: 30, labelWidth:100, line:{ width:2, color:"#1293f8" }, item:{ radius:3, borderColor:"#636363", borderWidth:1, color: "#ffffff", alpha:1, type:"r", shadow:false }, shadow:true, gradient:false, border:true, labelOffset: 20, origin:"auto" }, item_setter:function( config){ if(typeof(config)!="object") config={color:config, borderColor:config}; this._mergeSettings(config,dhtmlx.extend({},this.defaults.item)); config.alpha = dhtmlx.Template.setter(config.alpha); config.borderColor = dhtmlx.Template.setter(config.borderColor); config.color = dhtmlx.Template.setter(config.color); config.radius = dhtmlx.Template.setter(config.radius); return config; }, line_setter:function( config){ if(typeof(config)!="object") config={color:config}; dhtmlx.extend(this.defaults.line,config); config = dhtmlx.extend({},this.defaults.line); config.color = dhtmlx.Template.setter(config.color); return config; }, padding_setter:function( config){ if(typeof(config)!="object") config={left:config, right:config, top:config, bottom:config}; this._mergeSettings(config,{ left:50, right:20, top:35, bottom:40 }); return config; }, xAxis_setter:function( config){ if(!config) return false; if(typeof(config)!="object") config={ template:config }; this._mergeSettings(config,{ title:"", color:"#000000", lineColor:"#cfcfcf", template:"{obj}", lines:true }); var templates = ["lineColor","template","lines"]; this._converToTemplate(templates,config); this._settings.configXAxis = dhtmlx.extend({},config); return config; }, yAxis_setter:function( config){ this._mergeSettings(config,{ title:"", color:"#000000", lineColor:"#cfcfcf", template:"{obj}", lines:true, bg:"#ffffff" }); var templates = ["lineColor","template","lines","bg"]; this._converToTemplate(templates,config); this._settings.configYAxis = dhtmlx.extend({},config); return config; }, _converToTemplate:function(arr,config){ for(var i=0;i< arr.length;i++){ config[arr[i]] = dhtmlx.Template.setter(config[arr[i]]); } }, _drawScales:function(ctx,data,point0,point1,start,end,cellWidth){ var y = this._drawYAxis(ctx,data,point0,point1,start,end); this._drawXAxis(ctx,data,point0,point1,cellWidth,y); return y; }, _drawXAxis:function(ctx,data,point0,point1,cellWidth,y){ if (!this._settings.xAxis) return; var x0 = point0.x-0.5; var y0 = parseInt((y?y:point1.y),10)+0.5; var x1 = point1.x; var unit_pos; var center = true; this._drawLine(ctx,x0,y0,x1,y0,this._settings.xAxis.color,1); for(var i=0; i < data.length;i ++){ if(this._settings.offset === true) unit_pos = x0+cellWidth/2+i*cellWidth; else{ unit_pos = (i==data.length-1)?point1.x:x0+i*cellWidth; center = !!i; } unit_pos = parseInt(unit_pos,10)-0.5; /*scale labels*/ var top = ((this._settings.origin!="auto")&&(this._settings.view=="bar")&&(parseFloat(this._settings.value(data[i]))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(absNmin>1&&step>0.1){ start = Math.ceil(start); } while(nmin<0?start<=nmin:start>=nmin) start -= step; if(nmin<0) start =-start-2*step; } end = start; while(end1) for(var i=1; i < this._series.length;i++){ var maxI = this.max(this._series[i][value||"value"]); var minI = this.min(this._series[i][value||"value"]); if (maxI > maxValue) maxValue = maxI; if (minI < minValue) minValue = minI; } } return {max:maxValue,min:minValue}; }, _log10:function(n){ var method_name="log"; return Math.floor((Math[method_name](n)/Math.LN10)); }, _drawXAxisLabel:function(x,y,obj,center,top){ if (!this._settings.xAxis) return; var elem = this.renderTextAt(top, center, x,y-(top?2:0),this._settings.xAxis.template(obj)); if (elem) elem.className += " dhx_axis_item_x"; }, _drawXAxisLine:function(ctx,x,y1,y2,obj){ if (!this._settings.xAxis||!this._settings.xAxis.lines) return; this._drawLine(ctx,x,y1,x,y2,this._settings.xAxis.lineColor.call(this,obj),1); }, _drawLine:function(ctx,x1,y1,x2,y2,color,width){ ctx.strokeStyle = color; ctx.lineWidth = width; ctx.beginPath(); ctx.moveTo(x1,y1); ctx.lineTo(x2,y2); ctx.stroke(); ctx.lineWidth = 1; }, _getRelativeValue:function(minValue,maxValue){ var relValue, origRelValue; var valueFactor = 1; if(maxValue != minValue){ origRelValue = maxValue - minValue; if(Math.abs(relValue) < 1){ while(Math.abs(relValue)<1){ valueFactor *= 10; origRelValue = relValue* valueFactor; } } relValue = origRelValue; } 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";} ], /** * adds series to the chart (value and color properties) * @param: obj - obj with configuration properties */ addSeries:function(obj){ var temp = this._settings; this._settings = dhtmlx.extend({},temp); this._parseSettings(obj,{}); this._series.push(this._settings); this._settings = temp; }, /*switch global settings to serit in question*/ _switchSerie:function(id, tag){ var tip; this._active_serie = tag.getAttribute("userdata"); if (!this._series[this._active_serie]) return; for (var i=0; i < this._series.length; i++) { tip = this._series[i].tooltip; if (tip) tip.disable(); } tip = this._series[this._active_serie].tooltip; if (tip) tip.enable(); }, /** * renders legend block * @param: ctx - canvas object * @param: data - object those need to be displayed * @param: width - the width of the container * @param: height - the height of the container */ _drawLegend:function(ctx,data,width){ var i,legend,legendContainer,legendHeight,legendItems,legendWidth, style, x=0,y=0; /*legend config*/ legend = this._settings.legend; style = (this._settings.legend.layout!="x"?"width:"+legend.width+"px":""); /*creation of legend container*/ if(this.legendObj) this.legendObj.innerHTML = ""; legendContainer = dhtmlx.html.create("DIV",{ "class":"dhx_chart_legend", "style":"left:"+x+"px; top:"+y+"px;"+style },""); if(legend.padding){ legendContainer.style.padding = legend.padding+"px"; } this.legendObj = legendContainer; this._obj.appendChild(legendContainer); /*rendering legend text items*/ legendItems = []; if(!legend.values) for(i = 0; i < data.length; i++){ legendItems.push(this._drawLegendText(legendContainer,legend.template(data[i]))); } else for(i = 0; i < legend.values.length; i++){ legendItems.push(this._drawLegendText(legendContainer,legend.values[i].text)); } legendWidth = legendContainer.offsetWidth; legendHeight = legendContainer.offsetHeight; /*this._settings.legend.width = legendWidth; this._settings.legend.height = legendHeight;*/ /*setting legend position*/ if(legendWidth maxValue) maxValue = data[i].$sum ; if (data[i].$min < minValue) minValue = data[i].$min ; } if(minValue>0) minValue =0; } return {max:maxValue,min:minValue}; }, /*adds colors to the gradient object*/ _setBarGradient:function(ctx,x1,y1,x2,y2,type,color,axis){ var gradient,offset,rgb,hsv,color0; if(type == "light"){ if(axis == "x") gradient = ctx.createLinearGradient(x1,y1,x2,y1); else gradient = ctx.createLinearGradient(x1,y1,x1,y2); gradient.addColorStop(0,"#FFFFFF"); gradient.addColorStop(0.9,color); gradient.addColorStop(1,color); offset = 2; } else if(type == "falling"||type == "rising"){ if(axis == "x") gradient = ctx.createLinearGradient(x1,y1,x2,y1); else gradient = ctx.createLinearGradient(x1,y1,x1,y2); rgb = dhtmlx.math.toRgb(color); hsv = dhtmlx.math.rgbToHsv(rgb[0],rgb[1],rgb[2]); hsv[1] *= 1/2; color0 = "rgb("+dhtmlx.math.hsvToRgb(hsv[0],hsv[1],hsv[2])+")"; if(type == "falling"){ gradient.addColorStop(0,color0); gradient.addColorStop(0.7,color); gradient.addColorStop(1,color); } else if(type == "rising"){ gradient.addColorStop(0,color); gradient.addColorStop(0.3,color); gradient.addColorStop(1,color0); } offset = 0; } else{ ctx.globalAlpha = 0.37; offset = 0; if(axis == "x") gradient = ctx.createLinearGradient(x1,y2,x1,y1); else gradient = ctx.createLinearGradient(x1,y1,x2,y1); /*gradient.addColorStop(0,"#9d9d9d"); gradient.addColorStop(0.4,"#FFFFFF"); gradient.addColorStop(0.6,"#FFFFFF"); gradient.addColorStop(1,"#9d9d9d");*/ gradient.addColorStop(0,"#9d9d9d"); gradient.addColorStop(0.3,"#e8e8e8"); gradient.addColorStop(0.45,"#ffffff"); gradient.addColorStop(0.55,"#ffffff"); gradient.addColorStop(0.7,"#e8e8e8"); gradient.addColorStop(1,"#9d9d9d"); } return {gradient:gradient,offset:offset}; }, /** * returns the x and y position * @param: a - angle * @param: x - start x position * @param: y - start y position * @param: r - destination to the point */ _getPositionByAngle:function(a,x,y,r){ a *= (-1); x = x+Math.cos(a)*r; y = y-Math.sin(a)*r; return {x:x,y:y}; } }; dhtmlx.compat("layout");