From 20c75c45d8888e44b9b58b8ba67c6b5b2e8e7876 Mon Sep 17 00:00:00 2001 From: "Kunal Chavda (OpenERP)" Date: Fri, 8 Apr 2011 14:20:05 +0530 Subject: [IMP]Initialy show bar chart with data. bzr revid: kch@tinyerp.com-20110408085005-hv9jv2wcfopnxrgm --- .../lib/dhtmlxGraph/codebase/dhtmlxchart_debug.js | 4737 ++++++++++++++++++++ 1 file changed, 4737 insertions(+) create mode 100644 addons/base_graph/static/lib/dhtmlxGraph/codebase/dhtmlxchart_debug.js (limited to 'addons/base_graph/static/lib/dhtmlxGraph/codebase/dhtmlxchart_debug.js') diff --git a/addons/base_graph/static/lib/dhtmlxGraph/codebase/dhtmlxchart_debug.js b/addons/base_graph/static/lib/dhtmlxGraph/codebase/dhtmlxchart_debug.js new file mode 100644 index 00000000000..57e5b5d5b0d --- /dev/null +++ b/addons/base_graph/static/lib/dhtmlxGraph/codebase/dhtmlxchart_debug.js @@ -0,0 +1,4737 @@ +/* +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 +*/ +/* +2010 September 30 +*/ + + + +/* 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 + +//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 "); + }, + 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_area.js'*/ + + +/*DHX:Depend ext/chart/chart_base.js*/ +dhtmlx.chart.area = { + /** + * renders an area 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_area:function(ctx, data, point0, point1, sIndex, map){ + + var params = this._calculateParametersOfLineChart(ctx,data,point0,point1,sIndex); + + /*the value that defines the map area position*/ + var areaPos = Math.floor(params.cellWidth/2); + + /*drawing all items*/ + if (data.length) { + + ctx.globalAlpha = this._settings.alpha.call(this,data[0]); + ctx.fillStyle = this._settings.color.call(this,data[0]); + + /*the position of the first item*/ + var y0 = this._getYPointOfLineChart(data[0],point0,point1,params); + var x0 = (this._settings.offset?point0.x+params.cellWidth*0.5:point0.x); + ctx.beginPath(); + ctx.moveTo(x0,point1.y); + ctx.lineTo(x0,y0); + + /*creates map area*/ + map.addRect(data[0].id,[x0-areaPos,y0-areaPos,x0+areaPos,y0+areaPos]); + /*item label*/ + if(!this._settings.yAxis) + this.renderTextAt(false, (!this._settings.offset?false:true), x0, y0-this._settings.labelOffset, this._settings.label(data[0])); + + /*drawing the previous item and the line between to items*/ + for(var i=1; i < data.length;i ++){ + /*horizontal positions of the previous and current items (0.5 - the fix for line width)*/ + var xi = x0+ Math.floor(params.cellWidth*i) - 0.5; + var yi = this._getYPointOfLineChart(data[i],point0,point1,params); + ctx.lineTo(xi,yi); + /*creates map area*/ + map.addRect(data[i].id,[xi-areaPos,yi-areaPos,xi+areaPos,yi+areaPos]); + /*item label*/ + if(!this._settings.yAxis) + this.renderTextAt(false, (!this._settings.offset&&i==(data.length-1)?"left":"center"), xi, yi-this._settings.labelOffset, this._settings.label(data[i])); + } + ctx.lineTo(x0+Math.floor(params.cellWidth*[data.length-1]),point1.y); + ctx.lineTo(x0,point1.y); + ctx.fill(); + } + } +}; +dhtmlx.chart.stackedArea ={ + /** + * renders an area 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_stackedArea:function(ctx, data, point0, point1, sIndex, map){ + + var params = this._calculateParametersOfLineChart(ctx,data,point0,point1,sIndex); + + /*the value that defines the map area position*/ + var areaPos = Math.floor(params.cellWidth/2); + + var y1 = []; + + /*drawing all items*/ + if (data.length) { + + ctx.globalAlpha = this._settings.alpha.call(this,data[0]); + ctx.fillStyle = this._settings.color.call(this,data[0]); + + /*for the 2nd, 3rd, etc. series*/ + var y01 = (sIndex?data[0].$startY:point1.y); + + /*the position of the first item*/ + var x0 = (this._settings.offset?point0.x+params.cellWidth*0.5:point0.x); + var y02 = this._getYPointOfLineChart(data[0],point0,point1,params)-(sIndex?(point1.y-y01):0); + + y1[0] = y02; + + ctx.beginPath(); + ctx.moveTo(x0,y01); + ctx.lineTo(x0,y02); + + /*creates map area*/ + map.addRect(data[0].id,[x0-areaPos,y02-areaPos,x0+areaPos,y02+areaPos]); + /*item label*/ + if(!this._settings.yAxis) + this.renderTextAt(false, true, x0, y02-this._settings.labelOffset, this._settings.label(data[0])); + + /*drawing the previous item and the line between to items*/ + for(var i=1; i < data.length;i ++){ + /*horizontal positions of the previous and current items (0.5 - the fix for line width)*/ + var xi = x0+ Math.floor(params.cellWidth*i) - 0.5; + var yi2 = this._getYPointOfLineChart(data[i],point0,point1,params)-(sIndex?(point1.y-data[i].$startY):0); + + y1[i] = yi2; + + ctx.lineTo(xi,yi2); + /*creates map area*/ + map.addRect(data[i].id,[xi-areaPos,yi2-areaPos,xi+areaPos,yi2+areaPos]); + /*item label*/ + if(!this._settings.yAxis) + this.renderTextAt(false, true, xi, yi2-this._settings.labelOffset, this._settings.label(data[i])); + } + ctx.lineTo(x0+Math.floor(params.cellWidth*[data.length-1]),y01); + /*drawing lines of the lower part*/ + if(sIndex){ + for(var i=data.length-1; i >=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 params = this._calculateParametersOfLineChart(ctx,data,point0,point1,sIndex); + + /*the value that defines the map area position*/ + var areaPos = Math.floor(params.cellWidth/2); + + /*array of all points*/ + var items = []; + + /*drawing all items*/ + if (data.length) { + + /*getting all points*/ + var x0 = (this._settings.offset?point0.x+params.cellWidth*0.5:point0.x); + for(var i=0; i < data.length;i ++){ + var x = ((!i)?x0:Math.floor(params.cellWidth*i) - 0.5 + x0); + var y = this._getYPointOfLineChart(data[i],point0,point1,params); + items.push({x:x,y:y}); + } + var sparam = this._getSplineParameters(items); + + for(var i =0; i< items.length-1; i++){ + var x1 = items[i].x; + var y1 = items[i].y; + var x2 = items[i+1].x; + var y2 = items[i+1].y; + + + for(var j = x1; j < x2; j++) + this._drawLine(ctx,j,this._getSplineYPoint(j,x1,i,sparam.a,sparam.b,sparam.c,sparam.d),j+1,this._getSplineYPoint(j+1,x1,i,sparam.a,sparam.b,sparam.c,sparam.d),this._settings.line.color(data[i]),this._settings.line.width); + this._drawLine(ctx,x2-1,this._getSplineYPoint(j,x1,i,sparam.a,sparam.b,sparam.c,sparam.d),x2,y2,this._settings.line.color(data[i]),this._settings.line.width); + this._drawItemOfLineChart(ctx,x1,y1,data[i],this._settings.label(data[i])); + } + this._drawItemOfLineChart(ctx,x2,y2,data[i],this._settings.label(data[i])); + } + }, + /*gets spline parameter*/ + _getSplineParameters:function(points){ + var h,u,v,s,a,b,c,d,n; + h = []; m = []; + n = points.length; + + for(var i =0; i=1; i--) + s[i] = (v[i] - h[i]*s[i+1])/u[i]; + + a = []; b = []; c = []; d = []; + + for(var 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])); + 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.yAxis&&value===0)){ + this.renderTextAt(true, true, x0+Math.floor(barWidth/2),y0,this._settings.label(data[i])); + continue; + } + + /*takes start value into consideration*/ + if(!yax) value += startValue/unit; + var color = gradient||this._settings.color.call(this,data[i]); + + /*drawing the gradient border of a bar*/ + if(this._settings.border){ + ctx.beginPath(); + ctx.fillStyle = color; + this._setBarHPoints(ctx,x0,y0,barWidth,radius,unit,value,0); + ctx.lineTo(x0,0); + ctx.fill(); + + ctx.fillStyle = "#000000"; + ctx.globalAlpha = 0.37; + ctx.beginPath(); + this._setBarHPoints(ctx,x0,y0,barWidth,radius,unit,value,0); + ctx.fill(); + } + + /*drawing bar body*/ + ctx.globalAlpha = this._settings.alpha.call(this,data[i]); + ctx.fillStyle = (gradient||this._settings.color.call(this,data[i])); + ctx.beginPath(); + var points = this._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(); + ctx.globalAlpha = 1; + + if (inner_gradient!=false){ + var gradParam = this._setBarGradient(ctx,point0.x,y0+barWidth,point0.x+unit*value+2,y0,inner_gradient,color,"x"); + ctx.fillStyle = gradParam.gradient; + ctx.beginPath(); + var points = this._setBarHPoints(ctx,x0,y0+gradParam.offset,barWidth-gradParam.offset*2,radius,unit,value,gradParam.offset); + ctx.fill(); + ctx.globalAlpha = 1; + } + + + /*sets a bar label*/ + this.renderTextAt("middle",false,points[0]+3, parseInt(y0+(points[1]-y0)/2,10), this._settings.label(data[i])); + /*defines a map area for a bar*/ + map.addRect(data[i].id,[x0,y0,points[0],points[1]],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){ + /*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(radiuscellWidth) 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; + + var inner_gradient = false; + var gradient = this._settings.gradient; + if (gradient){ + inner_gradient = true; + } + 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 ++){ + + if(!sIndex) + data[i].$startX = point0.x; + + var value = parseFloat(this._settings.value(data[i])); + if(value>maxValue) value = maxValue; + value -= minValue; + value *= valueFactor; + + /*start point (bottom left)*/ + var x0 = point0.x; + var y0 = point0.y+ barOffset + i*cellWidth; + + /*for the 2nd, 3rd, etc. series*/ + if(sIndex) + x0 = data[i].$startX; + + if(value<0||(this._settings.yAxis&&value===0)){ + this.renderTextAt(true, true, x0+Math.floor(barWidth/2),y0,this._settings.label(data[i])); + continue; + } + + /*takes start value into consideration*/ + if(!yax) value += startValue/unit; + var color = this._settings.color.call(this,data[i]); + + /*drawing the gradient border of a bar*/ + if(this._settings.border){ + ctx.beginPath(); + ctx.fillStyle = color; + this._setBarHPoints(ctx,x0,y0,barWidth,radius,unit,value,0); + ctx.lineTo(x0,0); + ctx.fill(); + + ctx.fillStyle = "#000000"; + ctx.globalAlpha = 0.37; + ctx.beginPath(); + this._setBarHPoints(ctx,x0,y0,barWidth,radius,unit,value,0); + ctx.fill(); + } + ctx.globalAlpha = 1; + /*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(); + var points = this._setBarHPoints(ctx,x0,y0, barWidth,radius,unit,value,0); + ctx.fill(); + 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); + + var scaleY = 0; + /*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])); + if(!value) 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; + + /*for the 2nd, 3rd, etc. series*/ + if(sIndex) + 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 the gradient border of a bar*/ + if(this._settings.border){ + ctx.beginPath(); + ctx.fillStyle = color; + this._setStakedBarPoints(ctx,x0-1,y0,barWidth+2,unit,value,0,point0.y); + ctx.lineTo(x0,y0); + ctx.fill(); + + ctx.fillStyle = "#000000"; + ctx.globalAlpha = 0.37; + ctx.beginPath(); + this._setStakedBarPoints(ctx,x0-1,y0,barWidth+2,unit,value,0,point0.y); + ctx.fill(); + } + + /*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,y0,barWidth,unit,value,(this._settings.border?1:0),point0.y); + ctx.fill(); + ctx.globalAlpha = 1; + + /*gradient*/ + if (inner_gradient){ + var gradParam = this._setBarGradient(ctx,x0,y0,x0+barWidth,points[1],inner_gradient,color,"y"); + ctx.fillStyle = gradParam.gradient; + ctx.beginPath(); + var points = this._setStakedBarPoints(ctx,x0+gradParam.offset,y0,barWidth-gradParam.offset*2,unit,value,(this._settings.border?1:0),point0.y); + ctx.fill(); + 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){ + /*start*/ + ctx.moveTo(x0,y0); + /*start of left rounding*/ + var y1 = y0 - unit*value+offset; + /*maximum height limit*/ + if(y1 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 xax = !!this._settings.xAxis; + + 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 = (params.unit>10?params.unit:10); + 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 = 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(0,point1.y,0,point0.y); + this._settings.gradient(gradient); + } + var scaleY = 0; + /*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])); + 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; + + 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 the gradient border of a bar*/ + if(this._settings.border) + this._drawBarBorder(ctx,x0,y0,barWidth,minValue,radius,unit,value,color); + + /*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); + ctx.globalAlpha = 1; + + if (inner_gradient){ + this._drawBarGradient(ctx,x0,y0,barWidth,minValue,radius,unit,value,color,inner_gradient); + } + /*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:x,y0:y,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]; + }, + _drawBarBorder:function(ctx,x0,y0,barWidth,minValue,radius,unit,value,color){ + ctx.save(); + var p = this._correctBarParams(ctx,x0,y0,value,unit,barWidth,minValue); + + 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"); + ctx.fillStyle = gradParam.gradient; + this._setBarPoints(ctx,p.x0+gradParam.offset,p.y0,barWidth-gradParam.offset*2,radius,unit,p.value,gradParam.offset); + 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){ + /*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; + 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(radius=0)||(a1>=0 && a2<=Math.PI)||(a1<=Math.PI && a2>=Math.PI))) return; + + if(a1<=0 && a2>=0){ + a1 = 0; + line = false; + this._drawSectorLine(ctx,x0,y0,R,a1,a2); + } + if(a1<=Math.PI && a2>=Math.PI){ + a2 = Math.PI; + line = false; + this._drawSectorLine(ctx,x0,y0,R,a1,a2); + } + /*the height of 3D pie*/ + var offset = (this._settings.height||Math.floor(R/4))/this._settings.cant; + ctx.beginPath(); + ctx.arc(x0,y0,R,a1,a2,false); + ctx.lineTo(x0+R*Math.cos(a2),y0+R*Math.sin(a2)+offset); + ctx.arc(x0,y0+offset,R,a2,a1,true); + ctx.lineTo(x0+R*Math.cos(a1),y0+R*Math.sin(a1)); + ctx.fill(); + if(line) + ctx.stroke(); + }, + /** + * draws a serctor arc + */ + _drawSectorLine:function(ctx,x0,y0,R,a1,a2){ + ctx.beginPath(); + ctx.arc(x0,y0,R,a1,a2,false); + ctx.stroke(); + }, + /** + * adds a shadow to pie + * @param: ctx - canvas object + * @param: x - the horizontal position of the pie center + * @param: y - the vertical position of the pie center + * @param: R - pie radius + */ + _addShadow:function(ctx,x,y,R){ + var shadows = ["#676767","#7b7b7b","#a0a0a0","#bcbcbc","#d1d1d1","#d6d6d6"]; + for(var i = shadows.length-1;i>-1;i--){ + ctx.beginPath(); + ctx.fillStyle = shadows[i]; + ctx.arc(x+2,y+2,R+i,0,Math.PI*2,true); + ctx.fill(); + } + }, + /** + * returns a gray gradient + * @param: gradient - gradient object + */ + _getGrayGradient:function(gradient){ + gradient.addColorStop(0.0,"#ffffff"); + gradient.addColorStop(0.7,"#7a7a7a"); + gradient.addColorStop(1.0,"#000000"); + return gradient; + }, + /** + * adds gray radial gradient + * @param: ctx - canvas object + * @param: x - the horizontal position of the pie center + * @param: y - the vertical position of the pie center + * @param: radius - pie radius + * @param: x0 - the horizontal position of a gradient center + * @param: y0 - the vertical position of a gradient center + */ + _showRadialGradient:function(ctx,x,y,radius,x0,y0){ + ctx.globalAlpha = 0.3; + ctx.beginPath(); + var gradient; + if(typeof this._settings.gradient!= "function"){ + gradient = ctx.createRadialGradient(x0,y0,radius/4,x,y,radius); + gradient = this._getGrayGradient(gradient); + } + else gradient = this._settings.gradient(gradient); + ctx.fillStyle = gradient; + ctx.arc(x,y,radius,0,Math.PI*2,true); + ctx.fill(); + ctx.globalAlpha = 1; + }, + /** + * returns the calculates pie parameters: center position and radius + * @param: ctx - canvas object + * @param: x0 - the horizontal position of the pie center + * @param: y0 - the vertical position of the pie center + * @param: R - pie radius + * @param: alpha1 - the angle that defines the 1st edge of a sector + * @param: alpha2 - the angle that defines the 2nd edge of a sector + * @param: ky - the value that defines an angle of inclination + * @param: text - label text + * @param: in_width (boolean) - if label needs being displayed inside a pie + */ + _drawSectorLabel:function(x0,y0,R,alpha1,alpha2,ky,text,in_width){ + var t = this.renderText(0,0,text,0,1); + if (!t) return; + + //get existing width of text + var labelWidth = t.scrollWidth; + t.style.width = labelWidth+"px"; //adjust text label to fit all text + if (labelWidth>x0) labelWidth = x0; //the text can't be greater than half of view + + //calculate expected correction based on default font metrics + var width = 8; + if (in_width) width = labelWidth/1.8; + var alpha = alpha1+(alpha2-alpha1)/2; + + //calcualteion position and correction + R = R-(width-8)/2; + var corr_x = - width; + var corr_y = -8; + var align = "left"; + + //for items in right upper sector + if(alpha>=Math.PI/2 && alpha=Math.PI){ + corr_x = -labelWidth-corr_x+1; + align = "right"; + } + + //calculate position of text + //basically get point at center of pie sector + var y = (y0+Math.floor(R*Math.sin(alpha)))*ky+corr_y; + var x = x0+Math.floor((R+width/2)*Math.cos(alpha))+corr_x; + + //if pie sector starts in left of right part pie, related text + //must be placed to the left of to the right of pie as well + var left_end = (alpha2 < Math.PI/2+0.01) + var left_start = (alpha < Math.PI/2); + if (left_start && left_end) + x = Math.max(x,x0+3); //right part of pie + else if (!left_start && !left_end) + x = Math.min(x,x0-labelWidth); //left part of pie + + /*correction for the lower sector of the 3D pie*/ + if (!in_width && ky<1 && y > y0*ky){ + y+= (this._settings.height||Math.floor(R/4)); + } + + //we need to set position of text manually, based on above calculations + t.style.top = y+"px"; + t.style.left = x+"px"; + t.style.width = labelWidth+"px"; + t.style.textAlign = align; + t.style.whiteSpace = "nowrap"; + } +}; + +dhtmlx.chart.pie3D = { + pvt_render_pie3D:function(ctx,data,x,y,sIndex,map){ + this._renderPie(ctx,data,x,y,this._settings.cant,map); + } +} + + +/* DHX DEPEND FROM FILE 'template.js'*/ + + +/* + Template - handles html templates +*/ + +/*DHX:Depend dhtmlx.js*/ + +dhtmlx.Template={ + _cache:{ + }, + empty:function(){ + return ""; + }, + setter:function(name, value){ + return dhtmlx.Template.fromHTML(value); + }, + obj_setter:function(name,value){ + var f = dhtmlx.Template.setter(name,value); + var obj = this; + return function(){ + return f.apply(obj, arguments); + }; + }, + fromHTML:function(str){ + if (typeof str == "function") return str; + if (this._cache[str]) + return this._cache[str]; + + //supported idioms + // {obj} => value + // {obj.attr} => named attribute or value of sub-tag in case of xml + // {obj.attr?some:other} conditional output + // {-obj => sub-template + str=(str||"").toString(); + str=str.replace(/[\r\n]+/g,"\\n"); + str=str.replace(/\{obj\.([^}?]+)\?([^:]*):([^}]*)\}/g,"\"+(obj.$1?\"$2\":\"$3\")+\""); + str=str.replace(/\{common\.([^}\(]*)\}/g,"\"+common.$1+\""); + str=str.replace(/\{common\.([^\}\(]*)\(\)\}/g,"\"+(common.$1?common.$1(obj):\"\")+\""); + str=str.replace(/\{obj\.([^}]*)\}/g,"\"+obj.$1+\""); + str=str.replace(/#([a-z0-9_]+)#/gi,"\"+obj.$1+\""); + str=str.replace(/\{obj\}/g,"\"+obj+\""); + str=str.replace(/\{-obj/g,"{obj"); + str=str.replace(/\{-common/g,"{common"); + str="return \""+str+"\";"; + return this._cache[str]= Function("obj","common",str); + } +}; + +dhtmlx.Type={ + /* + adds new template-type + obj - object to which template will be added + data - properties of template + */ + add:function(obj, data){ + //auto switch to prototype, if name of class was provided + if (!obj.types && obj.prototype.types) + obj = obj.prototype; + //if (typeof data == "string") + // data = { template:data }; + + if (dhtmlx.assert_enabled()) + this.assert_event(data); + + var name = data.name||"default"; + + //predefined templates - autoprocessing + this._template(data); + this._template(data,"edit"); + this._template(data,"loading"); + + obj.types[name]=dhtmlx.extend(dhtmlx.extend({},(obj.types[name]||this._default)),data); + return name; + }, + //default template value - basically empty box with 5px margin + _default:{ + css:"default", + template:function(){ return ""; }, + template_edit:function(){ return ""; }, + template_loading:function(){ return "..."; }, + width:150, + height:80, + margin:5, + padding:0 + }, + //template creation helper + _template:function(obj,name){ + name = "template"+(name?("_"+name):""); + var data = obj[name]; + //if template is a string - check is it plain string or reference to external content + if (data && (typeof data == "string")){ + if (data.indexOf("->")!=-1){ + data = data.split("->"); + switch(data[0]){ + case "html": //load from some container on the page + data = dhtmlx.html.getValue(data[1]).replace(/\"/g,"\\\""); + break; + case "http": //load from external file + data = new dhtmlx.ajax().sync().get(data[1],{uid:(new Date()).valueOf()}).responseText; + break; + default: + //do nothing, will use template as is + break; + } + } + obj[name] = dhtmlx.Template.fromHTML(data); + } + } +}; + + + +/* DHX DEPEND FROM FILE 'single_render.js'*/ + + +/* + REnders single item. + Can be used for elements without datastore, or with complex custom rendering logic + + @export + render +*/ + +/*DHX:Depend template.js*/ + +dhtmlx.SingleRender={ + _init:function(){ + }, + //convert item to the HTML text + _toHTML:function(obj){ + /* + this one doesn't support per-item-$template + it has not sense, because we have only single item per object + */ + return this.type._item_start(obj,this.type)+this.type.template(obj,this.type)+this.type._item_end; + }, + //render self, by templating data object + render:function(){ + if (!this.callEvent || this.callEvent("onBeforeRender",[this.data])){ + if (this.data) + this._dataobj.innerHTML = this._toHTML(this.data); + if (this.callEvent) this.callEvent("onAfterRender",[]); + } + } +}; + + +/* DHX DEPEND FROM FILE 'tooltip.js'*/ + + +/* + UI: Tooltip + + @export + show + hide +*/ + +/*DHX:Depend tooltip.css*/ +/*DHX:Depend template.js*/ +/*DHX:Depend single_render.js*/ + +dhtmlx.ui.Tooltip=function(container){ + this.name = "Tooltip"; + this.version = "3.0"; + + if (dhtmlx.assert_enabled()) this._assert(); + + if (typeof container == "string"){ + container = { template:container }; + } + + dhtmlx.extend(this, dhtmlx.Settings); + dhtmlx.extend(this, dhtmlx.SingleRender); + this._parseSettings(container,{ + type:"default", + dy:0, + dx:20 + }); + + //create container for future tooltip + this._dataobj = this._obj = document.createElement("DIV"); + this._obj.className="dhx_tooltip"; + dhtmlx.html.insertBefore(this._obj,document.body.firstChild); +}; +dhtmlx.ui.Tooltip.prototype = { + //show tooptip + //pos - object, pos.x - left, pox.y - top + show:function(data,pos){ + 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(mode,value){ + var t = new dhtmlx.ui.Tooltip(value); + this.attachEvent("onMouseMove",function(id,e){ //show tooltip on mousemove + t.show(this.get(id),dhtmlx.html.pos(e)); + }); + this.attachEvent("onMouseOut",function(id,e){ //hide tooltip on mouseout + t.hide(); + }); + this.attachEvent("onMouseMoving",function(id,e){ //hide tooltip just after moving start + t.hide(); + }); + return t; + } +}; + + +/* DHX DEPEND FROM FILE 'datastore.js'*/ + + +/*DHX:Depend dhtmlx.js*/ + +/* + DataStore is not a behavior, it standalone object, which represents collection of data. + Call provideAPI to map data API + + @export + exists + idByIndex + indexById + get + set + refresh + dataCount + sort + filter + next + previous + clearAll + first + last +*/ +dhtmlx.DataStore = function(){ + this.name = "DataStore"; + + dhtmlx.extend(this, dhtmlx.EventSystem); + + this.setDriver("xml"); //default data source is an XML + this.pull = {}; //hash of IDs + this.order = dhtmlx.toArray(); //order of IDs + this._tree_store = false; +}; + +dhtmlx.DataStore.prototype={ + //defines type of used data driver + //data driver is an abstraction other different data formats - xml, json, csv, etc. + setDriver:function(type){ + dhtmlx.assert(dhtmlx.DataDriver[type],"incorrect DataDriver"); + this.driver = dhtmlx.DataDriver[type]; + }, + _tree_parse:function(data){ + if (data.item){ //FIXME + if (!(data.item instanceof Array)) + data.item=[data.item]; + for (var i=0; i < data.item.length; i++) { + var item = data.item[i]; + var id = this.id(item); + + data.item[i]=id; + this.pull[id]=item; + item.parent = data.id; + item.level = data.level+1; + this._tree_parse(item); + }; + } + }, + //process incoming raw data + _parse:function(data){ + //get size and position of data + var info = this.driver.getInfo(data); + //get array of records + + var recs = this.driver.getRecords(data); + var from = (info._from||0)*1; + var j=0; + for (var i=0; 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,this.dataCount()-1); + + var ret=dhtmlx.toArray(); //result of method is rich-array + for (var i=from; i <= to; i++) + ret.push(this.get(this.order[i])); + return ret; + }, + //returns total count of elements + dataCount:function(){ + return this.order.length; + }, + //returns truy if item with such ID exists + exists:function(id){ + return !!(this.pull[id]); + }, + //nextmethod is not visible on component level, check DataMove.move + //moves item from source index to the target index + move:function(sindex,tindex){ + if (sindex<0 || tindex<0){ + dhtmlx.error("DataStore::move","Incorrect indexes"); + return; + } + + var id = this.idByIndex(sindex); + var obj = this.get(id); + + this.order.removeAt(sindex); //remove at old position + //if (sindex data_size){ + dhtmlx.log("Warning","DataStore:add","Index of out of bounds"); + index = Math.min(this.order.length,index); + } + + if (!this.callEvent("onbeforeAdd",[id,index])) return; + + if (this.exists(id)) return dhtmlx.error("Not unique ID"); + + this.pull[id]=obj; + this.order.insertAt(id,index); + if (this._filter_order){ //adding during filtering + //we can't know the location of new item in full dataset, making suggestion + //put at end by default + var original_index = this._filter_order.length; + //put at start only if adding to the start and some data exists + if (!index && this.order.length) + original_index = 0; + + this._filter_order.insertAt(id,original_index); + } + + this.callEvent("onafterAdd",[id,index]); + //repaint signal + this.callEvent("onStoreUpdated",[id,obj,"add"]); + return id; + }, + + //removes element from datastore + remove:function(id){ + //id can be an array of IDs - result of getSelect, for example + if (id instanceof Array){ + for (var i=0; i < id.length; i++) + this.remove(id[i]); + return; + } + if (!this.callEvent("onbeforedelete",[id])) return; + if (!this.exists(id)) return dhtmlx.error("Not existing ID",id); + var obj = this.get(id); //save for later event + //clear from collections + this.order.remove(id); + if (this._filter_order) + this._filter_order.remove(id); + + delete this.pull[id]; + this.callEvent("onafterdelete",[id]); + //repaint signal + this.callEvent("onStoreUpdated",[id,obj,"delete"]); + }, + //deletes all records in datastore + clearAll:function(){ + //instead of deleting one by one - just reset inner collections + this.pull = {}; + this.order = dhtmlx.toArray(); + this._filter_order = null; + this.callEvent("onClearAll",[]); + this.refresh(); + }, + //converts id to index + idByIndex:function(index){ + if (index>=this.order.length || index<0) + dhtmlx.log("Warning","DataStore::idByIndex Incorrect index"); + + return this.order[index]; + }, + //converts index to id + indexById:function(id){ + var res = this.order.find(id); //slower than idByIndex + + if (res == -1) + 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){ + //remove previous filtering , if any + if (this._filter_order){ + this.order = this._filter_order; + delete this._filter_order; + } + //if text not define -just unfilter previous state and exit + if (text){ + var filter = text; + if (typeof text == "string"){ + text = dhtmlx.Template.setter(0,text); + filter = function(obj,value){ //default filter - string start from, case in-sensitive + return text(obj).toLowerCase().indexOf(value)!=-1; + }; + } + + value = (value||"").toString().toLowerCase(); + var neworder = dhtmlx.toArray(); + this.order.each(function(id){ + if (filter(this.get(id),value)) + neworder.push(id); + },this); + //set new order of items, store original + this._filter_order = this.order; + this.order = neworder; + } + //repaint self + this.refresh(); + }, + /* + Iterate through collection + */ + each:function(method,master){ + for (var i=0; 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(0,property); + return property(data[0]); + }; + var key = dhtmlx.Template.setter(0,stats.by); + if (!stats.map[key]) + stats.map[key] = [key, any]; + + var groups = {}; + var labels = []; + this.data.each(function(data){ + var current = key(data); + if (!groups[current]){ + labels.push({id:current}); + groups[current] = dhtmlx.toArray(); + } + groups[current].push(data); + }); + for (var prop in stats.map){ + var functor = (stats.map[prop][1]||any); + if (typeof functor != "function") + functor = this[functor]; + + for (var i=0; i < labels.length; i++) { + labels[i][prop]=functor.call(this, stats.map[prop][0], groups[labels[i].id]); + } + } +// if (this._settings.sort) +// labels.sortBy(stats.sort); + + this._not_grouped_data = this.data; + this.data = new dhtmlx.DataStore(); + this.data.provideApi(this,true); + this._init_group_data_event(this.data, this); + this.parse(labels,"json"); + }, + group:function(config,mode){ + this.ungroup(false); + this._split_data_by(config); + if (mode!==false) + this.render(); + }, + ungroup:function(mode){ + if (this._not_grouped_data){ + this.data = this._not_grouped_data; + this.data.provideApi(this, true); + } + if (mode!==false) + this.render(); + }, + group_setter:function(name, config){ + dhtmlx.assert(typeof config == "object", "Incorrect group value"); + dhtmlx.assert(config.by,"group.by is mandatory"); + dhtmlx.assert(config.map,"group.map is mandatory"); + return config; + }, + //need to be moved to more appropriate object + sort_setter:function(name, config){ + if (typeof config != "object") + config = { by:config }; + + this._mergeSettings(config,{ + as:"string", + dir:"asc" + }); + return config; + } +}; + + +/* DHX DEPEND FROM FILE 'key.js'*/ + + +/* + Behavior:KeyEvents - hears keyboard +*/ +dhtmlx.KeyEvents = { + _init:function(){ + //attach handler to the main container + dhtmlx.event(this._obj,"keypress",this._onKeyPress,this); + }, + //called on each key press , when focus is inside of related component + _onKeyPress:function(e){ + e=e||event; + var code = e.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,property,value):value; + }, + //process configuration object + _parseSeetingColl:function(coll){ + if (coll){ + for (var a in coll) //for each setting + this._define(a,coll[a]); //set value through config + } + }, + //helper for object initialization + _parseSettings:function(obj,initial){ + //initial - set of default values + var settings = dhtmlx.extend({},initial); + //code below will copy all properties over default one + if (typeof obj == "object" && !obj.tagName) + dhtmlx.extend(settings,obj); + //call config for each setting + this._parseSeetingColl(settings); + }, + _mergeSettings:function(config, defaults){ + for (var key in defaults) + switch(typeof config[key]){ + case "object": + config[key] = this._mergeSettings((config[key]||{}), defaults[key]); + break; + case "undefined": + config[key] = defaults[key]; + break; + default: //do nothing + break; + } + return config; + }, + //helper for html container init + _parseContainer:function(obj,name,fallback){ + /* + parameter can be a config object, in such case real container will be obj.container + or it can be html object or ID of html object + */ + if (typeof obj == "object" && !obj.tagName) + obj=obj.container; + this._obj = dhtmlx.toNode(obj); + if (!this._obj && fallback) + this._obj = fallback(obj); + + dhtmlx.assert(this._obj, "Incorrect html container"); + + this._obj.className+=" "+name; + this._obj.onselectstart=function(){return false;}; //block selection by default + this._dataobj = this._obj;//separate reference for rendering modules + }, + //apply template-type + _set_type:function(name){ + //parameter can be a hash of settings + if (typeof name == "object") + return this.type_setter("type",name); + + dhtmlx.assert(this.types, "RenderStack :: Types are not defined"); + dhtmlx.assert(this.types[name],"RenderStack :: Inccorect type name",name); + //or parameter can be a name of existing template-type + this.type=dhtmlx.extend({},this.types[name]); + this.customize(); //init configs + }, + customize:function(obj){ + //apply new properties + if (obj) dhtmlx.extend(this.type,obj); + + //init tempaltes for item start and item end + this.type._item_start = dhtmlx.Template.fromHTML(this.template_item_start(this.type)); + this.type._item_end = this.template_item_end(this.type); + + //repaint self + this.render(); + }, + //config.type - creates new template-type, based on configuration object + type_setter:function(mode,value){ + this._set_type(typeof value == "object"?dhtmlx.Type.add(this,value):value); + return value; + }, + //config.template - creates new template-type with defined template string + template_setter:function(mode,value){ + return this.type_setter("type",{template:value}); + }, + //config.css - css name for top level container + css_setter:function(mode,value){ + this._obj.className += " "+value; + return value; + } +}; + + +/* DHX DEPEND FROM FILE 'compatibility.js'*/ + + +/* + Collection of compatibility hacks +*/ + +/*DHX:Depend dhtmlx.js*/ + +dhtmlx.compat=function(name, obj){ + //check if name 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 'load.js'*/ + + +/* + ajax operations + + can be used for direct loading as + dhtmlx.ajax(ulr, callback) + or + dhtmlx.ajax().get(url) + dhtmlx.ajax().post(url) + +*/ + +/*DHX:Depend datastore.js*/ +/*DHX:Depend dhtmlx.js*/ + +dhtmlx.ajax = function(url,call,master){ + //if parameters was provided - made fast call + if (arguments.length!==0){ + var http_request = new dhtmlx.ajax(); + if (master) http_request.master=master; + http_request.get(url,null,call); + } + if (!this.getXHR) return new dhtmlx.ajax(); //allow to create new instance without direct new declaration + + return this; +}; +dhtmlx.ajax.prototype={ + //creates xmlHTTP object + getXHR:function(){ + if (dhtmlx._isIE) + return new ActiveXObject("Microsoft.xmlHTTP"); + else + return new XMLHttpRequest(); + }, + /* + send data to the server + params - hash of properties which will be added to the url + call - callback, can be an array of functions + */ + send:function(url,params,call){ + var x=this.getXHR(); + if (typeof call == "function") + call = [call]; + //add extra params to the url + if (typeof params == "object"){ + var t=[]; + for (var a in params) + t.push(a+"="+encodeURIComponent(params[a]));// utf-8 escaping + params=t.join("&"); + } + if (params && !this.post){ + url=url+(url.indexOf("?")!=-1 ? "&" : "?")+params; + params=null; + } + + x.open(this.post?"POST":"GET",url,!this._sync); + if (this.post) + x.setRequestHeader('Content-type','application/x-www-form-urlencoded'); + + //async mode, define loading callback + if (!this._sync){ + var self=this; + x.onreadystatechange= function(){ + if (!x.readyState || x.readyState == 4){ + dhtmlx.log_full_time("data_loading"); //log rendering time + if (call && self) + for (var i=0; i < call.length; i++) //there can be multiple callbacks + if (call[i]) + call[i].call((self.master||self),x.responseText,x.responseXML,x); + self.master=null; + call=x=self=null; //anti-leak + } + }; + } + + x.send(params||null); + return x; //return XHR, which can be used in case of sync. mode + }, + //GET request + get:function(url,params,call){ + this.post=false; + return this.send(url,params,call); + }, + //POST request + post:function(url,params,call){ + this.post=true; + return this.send(url,params,call); + }, + sync:function(){ + this._sync = true; + return this; + } +}; + + +/* + Behavior:DataLoader - load data in the component + + @export + load + parse +*/ +dhtmlx.DataLoader={ + _init:function(){ + //prepare data store + this.data = new dhtmlx.DataStore(); + }, + //loads data from external URL + load:function(url,call){ + this.callEvent("onXLS",[]); + if (typeof call == "string"){ //second parameter can be a loading type or callback + this.data.setDriver(call); + call = arguments[2]; + } + //prepare data feed for dyn. loading + if (!this.data.feed) + this.data.feed = function(from,count){ + //allow only single request at same time + if (this._load_count) + return this._load_count=[from,count]; //save last ignored request + else + this._load_count=true; + + this.load(url+((url.indexOf("?")==-1)?"?":"&")+"posStart="+from+"&count="+count,function(){ + //after loading check if we have some ignored requests + var temp = this._load_count; + this._load_count = false; + if (typeof temp =="object") + this.data.feed.apply(this, temp); //load last ignored request + }); + }; + //load data by async ajax call + dhtmlx.ajax(url,[this._onLoad,call],this); + }, + //loads data from object + parse:function(data,type){ + this.callEvent("onXLS",[]); + if (type) + this.data.setDriver(type); + this._onLoad(data,null); + }, + //default after loading callback + _onLoad:function(text,xml,loader){ + this.data._parse(this.data.driver.toObject(text,xml)); + this.callEvent("onXLE",[]); + } +}; + +/* + Abstraction layer for different data types +*/ + +dhtmlx.DataDriver={}; +dhtmlx.DataDriver.json={ + //convert json string to json object if necessary + toObject:function(data){ + if (typeof data == "string"){ + eval ("dhtmlx.temp="+data); + return dhtmlx.temp; + } + return data; + }, + //get array of records + getRecords:function(data){ + if (data && !(data instanceof Array)) + return [data]; + return data; + }, + //get hash of properties for single record + getDetails:function(data){ + return data; + }, + //get count of data and position at which new data need to be inserted + getInfo:function(data){ + return { + _size:(data.total_count||0), + _from:(data.pos||0) + }; + } +}; + +dhtmlx.DataDriver.html={ + /* + incoming data can be + - collection of nodes + - ID of parent container + - HTML text + */ + toObject:function(data){ + if (typeof data == "string"){ + var t=null; + if (data.indexOf("<")==-1) //if no tags inside - probably its an ID + t = dhtmlx.toNode(data); + if (!t){ + t=document.createElement("DIV"); + t.innerHTML = data; + } + + return t.getElementsByTagName(this.tag); + } + return data; + }, + //get array of records + getRecords:function(data){ + if (data.tagName) + return data.childNodes; + return data; + }, + //get hash of properties for single record + getDetails:function(data){ + return dhtmlx.DataDriver.xml.tagToObject(data); + }, + //dyn loading is not supported by HTML data source + getInfo:function(data){ + return { + _size:0, + _from:0 + }; + }, + tag: "LI" +}; + +dhtmlx.DataDriver.jsarray={ + //eval jsarray string to jsarray object if necessary + toObject:function(data){ + if (typeof data == "string"){ + eval ("dhtmlx.temp="+data); + return dhtmlx.temp; + } + return data; + }, + //get array of records + getRecords:function(data){ + return data; + }, + //get hash of properties for single record, in case of array they will have names as "data{index}" + getDetails:function(data){ + var result = {}; + for (var i=0; i < data.length; i++) + result["data"+i]=data[i]; + + return result; + }, + //dyn loading is not supported by js-array data source + getInfo:function(data){ + return { + _size:0, + _from:0 + }; + } +}; + +dhtmlx.DataDriver.csv={ + //incoming data always a string + toObject:function(data){ + return data; + }, + //get array of records + getRecords:function(data){ + return data.split(this.row); + }, + //get hash of properties for single record, data named as "data{index}" + getDetails:function(data){ + data = this.stringToArray(data); + var result = {}; + for (var i=0; i < data.length; i++) + result["data"+i]=data[i]; + + return result; + }, + //dyn loading is not supported by csv data source + getInfo:function(data){ + return { + _size:0, + _from:0 + }; + }, + //split string in array, takes string surrounding quotes in account + stringToArray:function(data){ + data = data.split(this.cell); + for (var i=0; i < data.length; i++) + data[i] = data[i].replace(/^[ \t\n\r]*(\"|)/g,"").replace(/(\"|)[ \t\n\r]*$/g,""); + return data; + }, + row:"\n", //default row separator + cell:"," //default cell separator +}; + +dhtmlx.DataDriver.xml={ + //convert xml string to xml object if necessary + toObject:function(text,xml){ + if (xml && (xml=this.checkResponse(text,xml))) //checkResponse - fix incorrect content type and extra whitespaces errors + return xml; + if (typeof text == "string"){ + return this.fromString(text); + } + return text; + }, + //get array of records + getRecords:function(data){ + return this.xpath(data,this.records); + }, + records:"/*/item", + userdata:"/*/userdata", + //get hash of properties for single record + getDetails:function(data){ + return this.tagToObject(data,{}); + }, + getUserData:function(data,col){ + col = col || {}; + var ud = this.xpath(data,this.userdata); + for (var i=0; i < ud.length; i++) { + var udx = this.tagToObject(ud[i]); + col[udx.name] = udx.value; + } + return col; + }, + //get count of data and position at which new data_loading need to be inserted + getInfo:function(data){ + return { + _size:(data.documentElement.getAttribute("total_count")||0), + _from:(data.documentElement.getAttribute("pos")||0) + }; + }, + //xpath helper + xpath:function(xml,path){ + if (window.XPathResult){ //FF, KHTML, Opera + var node=xml; + if(xml.nodeName.indexOf("document")==-1) + xml=xml.ownerDocument; + var res = []; + var col = xml.evaluate(path, node, null, XPathResult.ANY_TYPE, null); + var temp = col.iterateNext(); + while (temp){ + res.push(temp); + temp = col.iterateNext(); + } + return res; + } //IE + return xml.selectNodes(path); + }, + //convert xml tag to js object, all subtags and attributes are mapped to the properties of result object + tagToObject:function(tag,z){ + z=z||{}; + //map attributes + var a=tag.attributes; + for (var i=0; i5?10:5); + step = parseInt(stepVal,10)*calculStep; + + if(step>Math.abs(nmin)) + start = (nmin<0?-step:0); + else{ + var absNmin = Math.abs(nmin); + var powerStart = Math.floor(this._log10(absNmin)); + var nminVal = absNmin/Math.pow(10,powerStart); + start = Math.ceil(nminVal*10)/10*Math.pow(10,powerStart)-step; + if(nmin<0) start =-start-2*step; + } + var end = start; + while(end1) + for(var i=1; i < this._series.length;i++){ + var maxI = this.max(this._series[i].value); + var minI = this.min(this._series[i].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,this._settings.xAxis.template(obj)); + }, + _drawXAxisLine:function(ctx,x,y1,y2){ + if (!this._settings.xAxis||!this._settings.xAxis.lines) return; + this._drawLine(ctx,x,y1,x,y2,this._settings.xAxis.color,0.2); + }, + _drawLine:function(ctx,x1,y1,x2,y2,color,width){ + ctx.strokeStyle = color; + ctx.lineWidth = width; + ctx.beginPath(); + ctx.moveTo(x1,y1); + ctx.lineTo(x2,y2); + ctx.stroke(); + }, + _getRelativeValue:function(minValue,maxValue){ + var relValue; + var valueFactor = 1; + if(maxValue != minValue){ + relValue = maxValue - minValue; + if(Math.abs(relValue) < 1){ + while(Math.abs(relValue)<1){ + valueFactor *= 10; + relValue *= valueFactor; + } + } + } + else relValue = minValue; + return [relValue,valueFactor]; + }, + _rainbow : [ + function(pos){ return "#FF"+dhtmlx.math.toHex(pos/2,2)+"00";}, + function(pos){ return "#FF"+dhtmlx.math.toHex(pos/2+128,2)+"00";}, + function(pos){ return "#"+dhtmlx.math.toHex(255-pos,2)+"FF00";}, + function(pos){ return "#00FF"+dhtmlx.math.toHex(pos,2);}, + function(pos){ return "#00"+dhtmlx.math.toHex(255-pos,2)+"FF";}, + function(pos){ return "#"+dhtmlx.math.toHex(pos,2)+"00FF";} + ], + /** + * 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){ + this._active_serie = tag.getAttribute("userdata"); + for (var i=0; i < this._series.length; i++) { + var tip = this._series[i].tooltip; + if (tip) + tip.disable(); + } + var 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,height){ + /*position of the legend block*/ + var x=0,y=0; + /*legend config*/ + var legend = this._settings.legend; + /*the legend sizes*/ + var legendHeight,legendWidth; + + var style = (this._settings.legend.layout!="x"?"width:"+legend.width+"px":""); + /*creation of legend container*/ + var legendContainer = dhtmlx.html.create("DIV",{ + "class":"dhx_chart_legend", + "style":"left:"+x+"px; top:"+y+"px;"+style + },""); + this._obj.appendChild(legendContainer); + /*rendering legend text items*/ + var legendItems = []; + if(!legend.values) + for(var i = 0; i < data.length; i++){ + legendItems.push(this._drawLegendText(legendContainer,legend.template(data[i]))); + } + else + for(var 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; + 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{ + 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,"#000000"); + gradient.addColorStop(0.5,"#FFFFFF"); + gradient.addColorStop(0.6,"#FFFFFF"); + gradient.addColorStop(1,"#000000"); + } + return {gradient:gradient,offset:offset}; + } +}; + +dhtmlx.compat("layout"); \ No newline at end of file -- cgit v1.2.3