/* 2010 July 02 */ /* DHX DEPEND FROM FILE 'assert.js'*/ if (!window.dhtmlx) dhtmlx={}; //check some rule, show message as error if rule is not correct dhtmlx.assert = function(test, message){ if (!test) dhtmlx.error(message); }; dhtmlx.assert_enabled=function(){ return false; }; //register names of event, which can be triggered by the object dhtmlx.assert_event = function(obj, evs){ if (!obj._event_check){ obj._event_check = {}; obj._event_check_size = {}; } for (var a in evs){ obj._event_check[a.toLowerCase()]=evs[a]; var count=-1; for (var t in evs[a]) count++; obj._event_check_size[a.toLowerCase()]=count; } }; dhtmlx.assert_method_info=function(obj, name, descr, rules){ var args = []; for (var i=0; i < rules.length; i++) { args.push(rules[i][0]+" : "+rules[i][1]+"\n "+rules[i][2].describe()+(rules[i][3]?"; optional":"")); } return obj.name+"."+name+"\n"+descr+"\n Arguments:\n - "+args.join("\n - "); }; dhtmlx.assert_method = function(obj, config){ for (var key in config) dhtmlx.assert_method_process(obj, key, config[key].descr, config[key].args, (config[key].min||99), config[key].skip); }; dhtmlx.assert_method_process = function (obj, name, descr, rules, min, skip){ var old = obj[name]; if (!skip) obj[name] = function(){ if (arguments.length != rules.length && arguments.length < min) dhtmlx.log("warn","Incorrect count of parameters\n"+obj[name].describe()+"\n\nExpecting "+rules.length+" but have only "+arguments.length); else for (var i=0; i= 0) return true; return false; }; dhtmlx.assert_rule_dimension.describe=function(){ return "{Integer} value must be a positive number"; }; dhtmlx.assert_rule_number=function(check){ if (typeof check == "number") return true; return false; }; dhtmlx.assert_rule_number.describe=function(){ return "{Integer} value must be a number"; }; dhtmlx.assert_rule_function=function(check){ if (typeof check == "function") return true; return false; }; dhtmlx.assert_rule_function.describe=function(){ return "{Function} value must be a custom function"; }; dhtmlx.assert_rule_any=function(check){ return true; }; dhtmlx.assert_rule_any.describe=function(){ return "Any value"; }; dhtmlx.assert_rule_mix=function(a,b){ var t = function(check){ if (a(check)||b(check)) return true; return false; }; t.describe = function(){ return a.describe(); }; return t; }; } /* DHX DEPEND FROM FILE 'dhtmlx.js'*/ /*DHX:Depend assert.js*/ /* Common helpers */ dhtmlx.version="3.0"; dhtmlx.codebase="./"; //coding helpers //copies methods and properties from source to the target dhtmlx.extend = function(target, source){ for (var method in source) target[method] = source[method]; //applying asserts if (dhtmlx.assert_enabled() && source._assert){ target._assert(); target._assert=null; } //if source object has init code - call init against target if (source._init) target._init(); return target; }; //creates function with specified "this" pointer dhtmlx.bind=function(functor, object){ return function(){ return functor.apply(object,arguments); }; }; //loads module from external js file dhtmlx.require=function(module){ if (!dhtmlx._modules[module]){ dhtmlx.assert(dhtmlx.ajax,"load module is required"); //load and exec the required module dhtmlx.exec( dhtmlx.ajax().sync().get(dhtmlx.codebase+module).responseText ); dhtmlx._modules[module]=true; } }; dhtmlx._modules = {}; //hash of already loaded modules //evaluate javascript code in the global scoope dhtmlx.exec=function(code){ if (window.execScript) //special handling for IE window.execScript(code); else window.eval(code); }; /* creates method in the target object which will transfer call to the source object if event parameter was provided , each call of method will generate onBefore and onAfter events */ dhtmlx.methodPush=function(object,method,event){ return function(){ var res = false; //if (!event || this.callEvent("onBefore"+event,arguments)){ //not used anymore, probably can be removed res=object[method].apply(object,arguments); // if (event) this.callEvent("onAfter"+event,arguments); //} return res; //result of wrapped method }; }; //check === undefined dhtmlx.isNotDefined=function(a){ return typeof a == "undefined"; }; //delay call to after-render time dhtmlx.delay=function(method, obj, params){ setTimeout(function(){ var ret = method.apply(obj,params); method = obj = params = null; return ret; },1); }; //common helpers //generates unique ID (unique per window, nog GUID) dhtmlx.uid = function(){ if (!this._seed) this._seed=(new Date).valueOf(); //init seed with timestemp this._seed++; return this._seed; }; //resolve ID as html object dhtmlx.toNode = function(node){ if (typeof node == "string") return document.getElementById(node); return node; }; //adds extra methods for the array dhtmlx.toArray = function(array){ return dhtmlx.extend((array||[]),dhtmlx.PowerArray); }; //resolve function name dhtmlx.toFunctor=function(str){ return (typeof(str)=="string") ? eval(str) : str; }; //dom helpers //hash of attached events dhtmlx._events = {}; //attach event to the DOM element dhtmlx.event=function(node,event,handler,master){ node = dhtmlx.toNode(node); var id = dhtmlx.uid(); dhtmlx._events[id]=[node,event,handler]; //store event info, for detaching if (master) handler=dhtmlx.bind(handler,master); //use IE's of FF's way of event's attaching if (node.addEventListener) node.addEventListener(event, handler, false); else if (node.attachEvent) node.attachEvent("on"+event, handler); return id; //return id of newly created event, can be used in eventRemove }; //remove previously attached event dhtmlx.eventRemove=function(id){ if (!id) return; dhtmlx.assert(this._events[id],"Removing non-existing event"); var ev = dhtmlx._events[id]; //browser specific event removing if (ev[0].removeEventListener) ev[0].removeEventListener(ev[1],ev[2],false); else if (ev[0].detachEvent) ev[0].detachEvent("on"+ev[1],ev[2]); delete this._events[id]; //delete all traces }; //debugger helpers //anything starting from error or log will be removed during code compression //add message in the log dhtmlx.log = function(type,message,details){ if (window.console && console.log){ type=type.toLowerCase(); if (window.console[type]) window.console[type](message); else window.console.log(type +": "+message); if (details) window.console.log(details); } }; //register rendering time from call point dhtmlx.log_full_time = function(name){ dhtmlx._start_time_log = new Date(); dhtmlx.log("Info","Timing start ["+name+"]"); window.setTimeout(function(){ var time = new Date(); dhtmlx.log("Info","Timing end ["+name+"]:"+(time.valueOf()-dhtmlx._start_time_log.valueOf())/1000+"s"); },1); }; //register execution time from call point dhtmlx.log_time = function(name){ var fname = "_start_time_log"+name; if (!dhtmlx[fname]){ dhtmlx[fname] = new Date(); dhtmlx.log("Info","Timing start ["+name+"]"); } else { var time = new Date(); dhtmlx.log("Info","Timing end ["+name+"]:"+(time.valueOf()-dhtmlx[fname].valueOf())/1000+"s"); dhtmlx[fname] = null; } }; //log message with type=error dhtmlx.error = function(message,details){ dhtmlx.log("Error",message,details); }; //event system dhtmlx.EventSystem={ _init:function(){ this._events = {}; //hash of event handlers, name => handler this._handlers = {}; //hash of event handlers, ID => handler this._map = {}; }, //temporary block event triggering block : function(){ this._events._block = true; }, //re-enable event triggering unblock : function(){ this._events._block = false; }, mapEvent:function(map){ dhtmlx.extend(this._map, map); }, //trigger event callEvent:function(type,params){ if (this._events._block) return true; type = type.toLowerCase(); dhtmlx.assert_event_call(this, type, params); var event_stack =this._events[type.toLowerCase()]; //all events for provided name var return_value = true; if (dhtmlx.debug) //can slowdown a lot dhtmlx.log("info","["+this.name+"] event:"+type,params); if (event_stack) for(var i=0; i=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 'chart_graph.js'*/ /*DHX:Depend map.js*/ if (!dhtmlx.chart) dhtmlx.chart = {}; dhtmlx.chart.line = { /** * renders a graphic * @param: ctx - canvas object * @param: data - object those need to be displayed * @param: width - the width of the container * @param: height - the height of the container */ pvt_render_line:function(ctx, data, width, height){ var maxValue,minValue; /*necessary if maxValue - minValue < 0*/ var valueFactor; /*maxValue - minValue*/ var relValue; /*necessary to add events and tooltips*/ var map = new dhtmlx.ui.Map(this._id); /*scales*/ var yax = !!this._settings.yAxis; var xax = !!this._settings.xAxis; if(yax&&(typeof this._settings.yAxis.end!="undefied")&&(typeof this._settings.yAxis.start!="undefied")&&this._settings.yAxis.step){ maxValue = parseFloat(this._settings.yAxis.end); minValue = parseFloat(this._settings.yAxis.start); } else{ maxValue = this.max(this._settings.value); minValue = this.min(this._settings.value); } /*a vertical scale*/ this._drawYAxis(ctx,data,width,height,minValue,maxValue); /*necessary for automatic scale*/ if(yax){ maxValue = parseFloat(this._settings.yAxis.end); minValue = parseFloat(this._settings.yAxis.start); } var topPadding = parseInt(this._settings.padding.top,10); var leftPadding = parseInt(this._settings.padding.left,10); var bottomPadding = parseInt(this._settings.padding.bottom,10); var rightPadding = parseInt(this._settings.padding.right,10); /*available height*/ var total_height = height-topPadding-bottomPadding; /*unit calculation (y_position = value*unit)*/ var relativeValues = this._getRelativeValue(minValue,maxValue); relValue = relativeValues[0]; valueFactor = relativeValues[1]; var unit = total_height/relValue; if(!yax){ /*defines start value for better representation of small values*/ var startValue = (unit>10?unit:10); unit = (total_height-startValue)/relValue; } /*a space available for a single item*/ var cellWidth = Math.round((width - leftPadding - rightPadding)/data.length); /*the value that defines the map area position*/ var areaPos = Math.floor(cellWidth/2); /*drawing all items*/ if (data.length) { /*gets the vertical coordinate of an item*/ var _graph_point = function(data){ /*the real value of an object*/ var value = this._settings.value(data); /*a relative value*/ var v = (parseFloat(value) - minValue)*valueFactor; if(!yax) v += startValue/unit; /*a vertical coordinate*/ var y = height - Math.floor(unit*v) - bottomPadding; /*the limit of the minimum value is the minimum visible value*/ if(v<0) y = height - topPadding; /*the limit of the maximum value*/ if(value > maxValue) y = topPadding; /*the limit of the minimum value*/ if(value < minValue) y = height - bottomPadding; return y; }; /*the vertical position of the first item*/ var y1 = _graph_point.call(this,data[0]); /*drawing the previous item and the line between to items*/ for(var i=1; i <= data.length;i ++){ /*horizontal positions of the previous and current items (0.5 - the fix for line width)*/ var x1 = Math.floor(cellWidth*(i-0.5)) - 0.5 + leftPadding; var x2 = Math.floor(cellWidth*(i+0.5)) - 0.5 + leftPadding; /*sets label on the horizontal scale*/ this._drawXAxisLabel(x1,data[i-1]); /*draws a vertical line for the horizontal scale*/ this._drawXAxisLine(ctx,x1,height-bottomPadding,topPadding); /*a line between items*/ if (data.length!=i){ var y2 = _graph_point.call(this,data[i]); this._drawLine(ctx,x1,y1,x2,y2,this._settings.line.color(data[i-1]),this._settings.line.width); } /*draws prevous item*/ this._drawGraphicItem(ctx,x1,y1,data[i-1]); /*creates map area*/ map.addRect(data[i-1].id,[x1-areaPos,y1-areaPos,x1+areaPos,y1+areaPos]); y1=y2; } _graph_point = null; } /*horizontal scale*/ this._drawXAxis(ctx,data,width,height); map.render(this._obj); }, /** * draws an item and its label * @param: ctx - canvas object * @param: x0 - the x position of a circle * @param: y0 - the y position of a circle * @param: obj - data object */ _drawGraphicItem:function(ctx,x0,y0,obj){ var R = parseInt(this._settings.item.radius,10); ctx.lineWidth = parseInt(this._settings.item.borderWidth,10); ctx.fillStyle = this._settings.item.color(obj); ctx.strokeStyle = this._settings.item.borderColor(obj); ctx.beginPath(); ctx.arc(x0,y0,R,0,Math.PI*2,true); ctx.fill(); ctx.stroke(); /*item label*/ this.renderTextAt(false, true, x0,y0-R-this._settings.labelOffset,this._settings.label(obj)); } }; /* DHX DEPEND FROM FILE 'chart_bar.js'*/ /*DHX:Depend map.js*/ if (!dhtmlx.chart) dhtmlx.chart = {}; dhtmlx.chart.bar = { /** * renders a bar chart * @param: ctx - canvas object * @param: data - object those need to be displayed * @param: x - the width of the container * @param: y - the height of the container */ pvt_render_bar:function(ctx, data, x, y){ var maxValue,minValue; /*necessary if maxValue - minValue < 0*/ var valueFactor; /*maxValue - minValue*/ var relValue; var map = new dhtmlx.ui.Map(this._id); var yax = !!this._settings.yAxis; var xax = !!this._settings.xAxis; if(yax&&(typeof this._settings.yAxis.end!="undefied")&&(typeof this._settings.yAxis.start!="undefied")&&this._settings.yAxis.step){ maxValue = parseFloat(this._settings.yAxis.end); minValue = parseFloat(this._settings.yAxis.start); } else{ maxValue = this.max(this._settings.value); minValue = this.min(this._settings.value); } /*a vertical scale*/ this._drawYAxis(ctx,data,x,y,minValue,maxValue); /*necessary for automatic scale*/ if(yax){ maxValue = parseFloat(this._settings.yAxis.end); minValue = parseFloat(this._settings.yAxis.start); } var yPadding = parseInt(this._settings.padding.top,10); var xPadding = parseInt(this._settings.padding.left,10); var bottomPadding = parseInt(this._settings.padding.bottom,10); var total_height = y-yPadding-bottomPadding; /*unit calculation (bar_height = value*unit)*/ var relativeValues = this._getRelativeValue(minValue,maxValue); relValue = relativeValues[0]; valueFactor = relativeValues[1]; var unit = total_height/relValue; if(!yax){ /*defines start value for better representation of small values*/ var startValue = (unit>10?unit:10); unit = (total_height-startValue)/relValue; } /*an available width for one bar*/ var cellWidth = Math.floor((x-xPadding-parseInt(this._settings.padding.right,10))/data.length); /*a real bar width */ var barWidth = parseInt(this._settings.width,10); if(barWidth>cellWidth) barWidth = cellWidth-2; /*the half of distance between bars*/ var barOffset = Math.floor((cellWidth - barWidth)/2); /*the radius of rounding in the top part of each bar*/ var radius = Math.round(barWidth/5); var inner_gradient = false; var gradient = this._settings.gradient; if (gradient === true){ inner_gradient = true; gradient = false; } else if (gradient){ gradient = ctx.createLinearGradient(0,bottomPadding,0,y-yPadding); this._settings.gradient(gradient); } var scaleY = 0; /*draws a black line if the horizontal scale isn't defined*/ if(bottomPadding&&!xax){ scaleY = y-bottomPadding; this._drawLine(ctx,0,scaleY+0.5,x,scaleY+0.5,"#000000",1); //hardcoded color! } for(var i=0; i < data.length;i ++){ var value = parseFloat(this._settings.value(data[i])); if(value>maxValue) value = maxValue; value -= minValue; value *= valueFactor; /*start point (bottom left)*/ var x0 = xPadding + barOffset + i*cellWidth; var y0 = y-bottomPadding; /*sets a label in the horizontal scale*/ this._drawXAxisLabel(x0+Math.floor(barWidth/2),data[i]); if(value<0||(this._settings.yAxis&&value===0)){ this.renderTextAt(true, true, x0+Math.floor(barWidth/2),y0,this._settings.label(data[i])); continue; } /*takes start value into consideration*/ if(!yax) value += startValue/unit; var color = gradient||this._settings.color.call(this,data[i]); /*drawing the gradient border of a bar*/ if(this._settings.border){ ctx.beginPath(); ctx.fillStyle = color; this._setBarPoints(ctx,x0-1,y0,barWidth+2,radius,unit,value,0); ctx.lineTo(x0,0); ctx.fill(); ctx.fillStyle = "#000000"; ctx.globalAlpha = 0.37; ctx.beginPath(); this._setBarPoints(ctx,x0-1,y0,barWidth+2,radius,unit,value,0); ctx.fill(); } /*drawing bar body*/ ctx.globalAlpha = this._settings.alpha.call(this,data[i]); ctx.fillStyle = (gradient||this._settings.color.call(this,data[i])); ctx.beginPath(); var points = this._setBarPoints(ctx,x0,y0,barWidth,radius,unit,value,(this._settings.border?1:0)); if (gradient) ctx.lineTo(x0,0); //fix gradient sphreading ctx.fill(); ctx.globalAlpha = 1; if (inner_gradient){ var i_gradient = ctx.createLinearGradient(0,y0-unit*value+2,0,y0); i_gradient.addColorStop(0,color); i_gradient.addColorStop(0.1,color); i_gradient.addColorStop(1,"#FFFFFF"); ctx.fillStyle = i_gradient; ctx.beginPath(); var points = this._setBarPoints(ctx,x0+2,y0,barWidth-4,radius,unit,value,1); ctx.fill(); } /*sets a bar label*/ this.renderTextAt(true, true, x0+Math.floor(barWidth/2),points[1],this._settings.label(data[i])); /*defines a map area for a bar*/ map.addRect(data[i].id,[x0,points[1],points[0],y0]); } this._drawXAxis(ctx,data,x,y); map.render(this._obj); }, /** * sets points for bar and returns the position of the bottom right point * @param: ctx - canvas object * @param: x0 - the x position of start point * @param: y0 - the y position of start point * @param: barWidth - bar width * @param: radius - the rounding radius of the top * @param: unit - the value defines the correspondence between item value and bar height * @param: value - item value * @param: offset - the offset from expected bar edge (necessary for drawing border) */ _setBarPoints:function(ctx,x0,y0,barWidth,radius,unit,value,offset){ /*correction for displaing small values (when rounding radius is bigger than bar height)*/ var angle_corr = 0; if(radius>unit*value){ var cosA = (radius-unit*value)/radius; angle_corr = -Math.acos(cosA)+Math.PI/2; } /*start*/ ctx.moveTo(x0,y0); /*start of left rounding*/ var y1 = y0 - Math.floor(unit*value) + radius; if(radiusdetails.total_height)) y0 += y-details.total_height-ctx.lineWidth/2; /*considers item index*/ y0 += i*details.height; ctx.moveTo(x0,y0); var x1 = x0+details.marker.width-details.marker.height +1; ctx.lineTo(x1,y0); ctx.stroke(); /*item text*/ this.renderText(x1+details.marker.width/2+5,y0-details.marker.height/2,details.template(obj)); }, /** * returns calculated pie parameters: center position and radius * @param: x - the width of a container * @param: y - the height of a container */ _getPieParameters:function(x,y){ var offset = 0; if(this._settings.legend) offset = this._settings.legend.width*(this._settings.legend.align=="right"?-1:1); x = (x + offset)/2; y = y/2; var radius = Math.min(x,y)-this._settings.padding.top; return {"x":x,"y":y,"radius":radius}; }, /** * creates lower part of sector in 3Dpie * @param: ctx - canvas object * @param: x0 - the horizontal position of the pie center * @param: y0 - the vertical position of the pie center * @param: a0 - the angle that defines the first edge of a sector * @param: a1 - the angle that defines the second edge of a sector * @param: R - pie radius * @param: line (boolean) - if the sector needs a border */ _createLowerSector:function(ctx,x0,y0,a1,a2,R,line){ ctx.lineWidth = 1; /*checks if the lower sector needs being displayed*/ if(!((a1<=0 && a2>=0)||(a1>=0 && a2<=Math.PI)||(a1<=Math.PI && a2>=Math.PI))) return; if(a1<=0 && a2>=0){ a1 = 0; line = false; this._drawSectorLine(ctx,x0,y0,R,a1,a2); } if(a1<=Math.PI && a2>=Math.PI){ a2 = Math.PI; line = false; this._drawSectorLine(ctx,x0,y0,R,a1,a2); } /*the height of 3D pie*/ var offset = (this._settings.height||Math.floor(R/4))/this._settings.cant; ctx.beginPath(); ctx.arc(x0,y0,R,a1,a2,false); ctx.lineTo(x0+R*Math.cos(a2),y0+R*Math.sin(a2)+offset); ctx.arc(x0,y0+offset,R,a2,a1,true); ctx.lineTo(x0+R*Math.cos(a1),y0+R*Math.sin(a1)); ctx.fill(); if(line) ctx.stroke(); }, /** * draws a serctor arc */ _drawSectorLine:function(ctx,x0,y0,R,a1,a2){ ctx.beginPath(); ctx.arc(x0,y0,R,a1,a2,false); ctx.stroke(); }, /** * adds a shadow to pie * @param: ctx - canvas object * @param: x - the horizontal position of the pie center * @param: y - the vertical position of the pie center * @param: R - pie radius */ _addShadow:function(ctx,x,y,R){ var shadows = ["#676767","#7b7b7b","#a0a0a0","#bcbcbc","#d1d1d1","#d6d6d6"]; for(var i = shadows.length-1;i>-1;i--){ ctx.beginPath(); ctx.fillStyle = shadows[i]; ctx.arc(x+2,y+2,R+i,0,Math.PI*2,true); ctx.fill(); } }, /** * returns a gray gradient * @param: gradient - gradient object */ _getGrayGradient:function(gradient){ gradient.addColorStop(0.0,"#ffffff"); gradient.addColorStop(0.7,"#7a7a7a"); gradient.addColorStop(1.0,"#000000"); return gradient; }, /** * adds gray radial gradient * @param: ctx - canvas object * @param: x - the horizontal position of the pie center * @param: y - the vertical position of the pie center * @param: radius - pie radius * @param: x0 - the horizontal position of a gradient center * @param: y0 - the vertical position of a gradient center */ _showRadialGradient:function(ctx,x,y,radius,x0,y0){ ctx.globalAlpha = 0.3; ctx.beginPath(); var gradient; if(typeof this._settings.gradient!= "function"){ gradient = ctx.createRadialGradient(x0,y0,radius/4,x,y,radius); gradient = this._getGrayGradient(gradient); } else gradient = this._settings.gradient(gradient); ctx.fillStyle = gradient; ctx.arc(x,y,radius,0,Math.PI*2,true); ctx.fill(); ctx.globalAlpha = 1; }, /** * returns the calculates pie parameters: center position and radius * @param: ctx - canvas object * @param: x0 - the horizontal position of the pie center * @param: y0 - the vertical position of the pie center * @param: R - pie radius * @param: alpha1 - the angle that defines the 1st edge of a sector * @param: alpha2 - the angle that defines the 2nd edge of a sector * @param: ky - the value that defines an angle of inclination * @param: text - label text * @param: in_width (boolean) - if label needs being displayed inside a pie */ _drawSectorLabel:function(x0,y0,R,alpha1,alpha2,ky,text,in_width){ var t = this.renderText(0,0,text,0,1); if (!t) return; //get existing width of text var labelWidth = t.scrollWidth; t.style.width = labelWidth+"px"; //adjust text label to fit all text if (labelWidth>x0) labelWidth = x0; //the text can't be greater than half of view //calculate expected correction based on default font metrics var width = 8; if (in_width) width = labelWidth/1.8; var alpha = alpha1+(alpha2-alpha1)/2; //calcualteion position and correction R = R-(width-8)/2; var corr_x = - width; var corr_y = -8; var align = "left"; //for items in right upper sector if(alpha>=Math.PI/2 && alpha=Math.PI){ corr_x = -labelWidth-corr_x+1; align = "right"; } //calculate position of text //basically get point at center of pie sector var y = (y0+Math.floor(R*Math.sin(alpha)))*ky+corr_y; var x = x0+Math.floor((R+width/2)*Math.cos(alpha))+corr_x; //if pie sector starts in left of right part pie, related text //must be placed to the left of to the right of pie as well var left_end = (alpha2 < Math.PI/2+0.01) var left_start = (alpha < Math.PI/2); if (left_start && left_end) x = Math.max(x,x0+3); //right part of pie else if (!left_start && !left_end) x = Math.min(x,x0-labelWidth); //left part of pie /*correction for the lower sector of the 3D pie*/ if (!in_width && ky<1 && y > y0*ky){ y+= (this._settings.height||Math.floor(R/4)); } //we need to set position of text manually, based on above calculations t.style.top = y+"px"; t.style.left = x+"px"; t.style.width = labelWidth+"px"; t.style.textAlign = align; t.style.whiteSpace = "nowrap"; } }; /* DHX DEPEND FROM FILE 'template.js'*/ /* Template - handles html templates */ /*DHX:Depend dhtmlx.js*/ dhtmlx.Template={ _cache:{ }, empty:function(){ return ""; }, setter:function(name, value){ return dhtmlx.Template.fromHTML(value); }, obj_setter:function(name,value){ var f = dhtmlx.Template.setter(name,value); var obj = this; return function(){ return f.apply(obj, arguments); }; }, fromHTML:function(str){ if (typeof str == "function") return str; if (this._cache[str]) return this._cache[str]; //supported idioms // {obj} => value // {obj.attr} => named attribute or value of sub-tag in case of xml // {obj.attr?some:other} conditional output // {-obj => sub-template str=(str||"").toString(); str=str.replace(/[\r\n]+/g,"\\n"); str=str.replace(/\{obj\.([^}?]+)\?([^:]*):([^}]*)\}/g,"\"+(obj.$1?\"$2\":\"$3\")+\""); str=str.replace(/\{common\.([^}\(]*)\}/g,"\"+common.$1+\""); str=str.replace(/\{common\.([^\}\(]*)\(\)\}/g,"\"+(common.$1?common.$1(obj):\"\")+\""); str=str.replace(/\{obj\.([^}]*)\}/g,"\"+obj.$1+\""); str=str.replace(/#([a-z0-9_]+)#/gi,"\"+obj.$1+\""); str=str.replace(/\{obj\}/g,"\"+obj+\""); str=str.replace(/\{-obj/g,"{obj"); str=str.replace(/\{-common/g,"{common"); str="return \""+str+"\";"; return this._cache[str]= Function("obj","common",str); } }; dhtmlx.Type={ /* adds new template-type obj - object to which template will be added data - properties of template */ add:function(obj, data){ //auto switch to prototype, if name of class was provided if (!obj.types && obj.prototype.types) obj = obj.prototype; //if (typeof data == "string") // data = { template:data }; if (dhtmlx.assert_enabled()) this.assert_event(data); var name = data.name||"default"; //predefined templates - autoprocessing this._template(data); this._template(data,"edit"); this._template(data,"loading"); obj.types[name]=dhtmlx.extend(dhtmlx.extend({},(obj.types[name]||this._default)),data); return name; }, //default template value - basically empty box with 5px margin _default:{ css:"default", template:function(){ return ""; }, template_edit:function(){ return ""; }, template_loading:function(){ return "..."; }, width:150, height:80, margin:5, padding:0 }, //template creation helper _template:function(obj,name){ name = "template"+(name?("_"+name):""); var data = obj[name]; //if template is a string - check is it plain string or reference to external content if (data && (typeof data == "string")){ if (data.indexOf("->")!=-1){ data = data.split("->"); switch(data[0]){ case "html": //load from some container on the page data = dhtmlx.html.getValue(data[1]).replace(/\"/g,"\\\""); break; case "http": //load from external file data = new dhtmlx.ajax().sync().get(data[1],{uid:(new Date()).valueOf()}).responseText; break; default: //do nothing, will use template as is break; } } obj[name] = dhtmlx.Template.fromHTML(data); } } }; /* DHX DEPEND FROM FILE 'single_render.js'*/ /* REnders single item. Can be used for elements without datastore, or with complex custom rendering logic @export render */ /*DHX:Depend template.js*/ dhtmlx.SingleRender={ _init:function(){ }, //convert item to the HTML text _toHTML:function(obj){ /* this one doesn't support per-item-$template it has not sense, because we have only single item per object */ return this.type._item_start(obj,this.type)+this.type.template(obj,this.type)+this.type._item_end; }, //render self, by templating data object render:function(){ if (!this.callEvent || this.callEvent("onBeforeRender",[this.data])){ if (this.data) this._dataobj.innerHTML = this._toHTML(this.data); if (this.callEvent) this.callEvent("onAfterRender",[]); } } }; /* DHX DEPEND FROM FILE 'tooltip.js'*/ /* UI: Tooltip @export show hide */ /*DHX:Depend tooltip.css*/ /*DHX:Depend template.js*/ /*DHX:Depend single_render.js*/ dhtmlx.ui.Tooltip=function(container){ this.name = "Tooltip"; this.version = "3.0"; if (dhtmlx.assert_enabled()) this._assert(); if (typeof container == "string"){ container = { template:container }; } dhtmlx.extend(this, dhtmlx.Settings); dhtmlx.extend(this, dhtmlx.SingleRender); this._parseSettings(container,{ type:"default", dy:0, dx:20 }); //create container for future tooltip this._dataobj = this._obj = document.createElement("DIV"); this._obj.className="dhx_tooltip"; dhtmlx.html.insertBefore(this._obj,document.body.firstChild); }; dhtmlx.ui.Tooltip.prototype = { //show tooptip //pos - object, pos.x - left, pox.y - top show:function(data,pos){ //render sefl only if new data was provided if (this.data!=data){ this.data=data; this.render(data); } //show at specified position this._obj.style.top = pos.y+this._settings.dy+"px"; this._obj.style.left = pos.x+this._settings.dx+"px"; this._obj.style.display="block"; }, //hide tooltip hide:function(){ this.data=null; //nulify, to be sure that on next show it will be fresh-rendered this._obj.style.display="none"; }, types:{ "default":dhtmlx.Template.fromHTML("{obj.id}") }, template_item_start:dhtmlx.Template.empty, template_item_end:dhtmlx.Template.empty }; /* DHX DEPEND FROM FILE 'autotooltip.js'*/ /* Behavior: AutoTooltip - links tooltip to data driven item */ /*DHX:Depend tooltip.js*/ dhtmlx.AutoTooltip = { tooltip_setter:function(mode,value){ var t = new dhtmlx.ui.Tooltip(value); this.attachEvent("onMouseMove",function(id,e){ //show tooltip on mousemove t.show(this.get(id),dhtmlx.html.pos(e)); }); this.attachEvent("onMouseOut",function(id,e){ //hide tooltip on mouseout t.hide(); }); this.attachEvent("onMouseMoving",function(id,e){ //hide tooltip just after moving start t.hide(); }); return t; } }; /* DHX DEPEND FROM FILE 'datastore.js'*/ /*DHX:Depend dhtmlx.js*/ /* DataStore is not a behavior, it standalone object, which represents collection of data. Call provideAPI to map data API @export exists idByIndex indexById get set refresh dataCount sort filter next previous clearAll first last */ dhtmlx.DataStore = function(){ this.name = "DataStore"; dhtmlx.extend(this, dhtmlx.EventSystem); this.setDriver("xml"); //default data source is an XML this.pull = {}; //hash of IDs this.order = dhtmlx.toArray(); //order of IDs }; dhtmlx.DataStore.prototype={ //defines type of used data driver //data driver is an abstraction other different data formats - xml, json, csv, etc. setDriver:function(type){ dhtmlx.assert(dhtmlx.DataDriver[type],"incorrect DataDriver"); this.driver = dhtmlx.DataDriver[type]; }, //process incoming raw data _parse:function(data){ //get size and position of data var info = this.driver.getInfo(data); //get array of records var recs = this.driver.getRecords(data); var from = (info._from||0)*1; var j=0; for (var i=0; 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 || tindex >= this.order.length){ dhtmlx.error("DataStore::move","Incorrect indexes"); return; } var id = this.idByIndex(sindex); var obj = this.get(id); this.order.removeAt(sindex); //remove at old position //if (sindex>tindex) tindex--; //correct shift, caused by element removing this.order.insertAt(id,tindex); //insert at new position //repaint signal this.callEvent("onStoreUpdated",[id,obj,"move"]); }, //adds item to the store add:function(obj,index){ //generate id for the item var id = this.id(obj); //by default item is added to the end of the list var data_size = this.dataCount(); if (dhtmlx.isNotDefined(index)) index = data_size; //check to prevent too big indexes if (index > data_size){ dhtmlx.log("Warning","DataStore:add","Index of out of bounds"); index = Math.min(this.order.length,index); } if (!this.callEvent("onbeforeAdd",[id,index])) return; if (this.exists(id)) return dhtmlx.error("Not unique ID"); this.pull[id]=obj; this.order.insertAt(id,index); if (this._filter_order){ //adding during filtering //we can't know the location of new item in full dataset, making suggestion //put at end by default var original_index = this._filter_order.length; //put at start only if adding to the start and some data exists if (!index && this.order.length) original_index = 0; this._filter_order.insertAt(id,original_index); } this.callEvent("onafterAdd",[id,index]); //repaint signal this.callEvent("onStoreUpdated",[id,obj,"add"]); return id; }, //removes element from datastore remove:function(id){ //id can be an array of IDs - result of getSelect, for example if (id instanceof Array){ for (var i=0; i < id.length; i++) this.remove(id[i]); return; } if (!this.callEvent("onbeforedelete",[id])) return; if (!this.exists(id)) return dhtmlx.error("Not existing ID",id); var obj = this.get(id); //save for later event //clear from collections this.order.remove(id); if (this._filter_order) this._filter_order.remove(id); delete this.pull[id]; this.callEvent("onafterdelete",[id]); //repaint signal this.callEvent("onStoreUpdated",[id,obj,"delete"]); }, //deletes all records in datastore clearAll:function(){ //instead of deleting one by one - just reset inner collections this.pull = {}; this.order = dhtmlx.toArray(); this._filter_order = null; this.callEvent("onClearAll",[]); this.refresh(); }, //converts id to index idByIndex:function(index){ if (index>=this.order.length || index<0) dhtmlx.log("Warning","DataStore::idByIndex Incorrect index"); return this.order[index]; }, //converts index to id indexById:function(id){ var res = this.order.find(id); //slower than idByIndex if (!res && res!==0) dhtmlx.log("Warning","DataStore::indexById Non-existing ID: "+ id); return res; }, //returns ID of next element next:function(id,step){ return this.order[this.indexById(id)+(step||1)]; }, //returns ID of first element first:function(){ return this.order[0]; }, //returns ID of last element last:function(){ return this.order[this.order.length-1]; }, //returns ID of previous element previous:function(id,step){ return this.order[this.indexById(id)-(step||1)]; }, /* sort data in collection by - settings of sorting or by - sorting function dir - "asc" or "desc" or by - property dir - "asc" or "desc" as - type of sortings Sorting function will accept 2 parameters and must return 1,0,-1, based on desired order */ sort:function(by, dir, as){ var sort = by; if (typeof by == "function") sort = {as:by, dir:dir}; else if (typeof by == "string") sort = {by:by, dir:dir, as:as}; var parameters = [sort.by, sort.dir, sort.as]; if (!this.callEvent("onbeforesort",parameters)) return; var sorter = dhtmlx.sort.create(sort); //get array of IDs var neworder = this.getRange(); neworder.sort(sorter); this.order = neworder.map(function(obj){ return this.id(obj); },this); //repaint self this.refresh(); this.callEvent("onaftersort",parameters); }, /* Filter datasource text - property, by which filter value - filter mask or text - filter method Filter method will receive data object and must return true or false */ filter:function(text,value){ //remove previous filtering , if any if (this._filter_order){ this.order = this._filter_order; delete this._filter_order; } //if text not define -just unfilter previous state and exit if (text){ var filter = text; if (typeof text == "string"){ text = dhtmlx.Template.setter(0,text); filter = function(obj,value){ //default filter - string start from, case in-sensitive return text(obj).toLowerCase().indexOf(value)!=-1; }; } value = (value||"").toString().toLowerCase(); var neworder = dhtmlx.toArray(); this.order.each(function(id){ if (filter(this.get(id),value)) neworder.push(id); },this); //set new order of items, store original this._filter_order = this.order; this.order = neworder; } //repaint self this.refresh(); }, /* Iterate through collection */ each:function(method,master){ for (var i=0; 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.keyCode; //FIXME better solution is required this.callEvent((this._edit_id?"onEditKeyPress":"onKeyPress"),[code,e.ctrlKey,e.shiftKey,e]); } }; /* DHX DEPEND FROM FILE 'mouse.js'*/ /* Behavior:MouseEvents - provides inner evnets for mouse actions */ dhtmlx.MouseEvents={ _init: function(){ //attach dom events if related collection is defined if (this._click){ dhtmlx.event(this._obj,"click",this._onClick,this); dhtmlx.event(this._obj,"contextmenu",this._onContext,this); } if (this._dblclick) dhtmlx.event(this._obj,"dblclick",this._onDblClick,this); if (this._mouse_move){ dhtmlx.event(this._obj,"mousemove",this._onMouse,this); dhtmlx.event(this._obj,(dhtmlx._isIE?"mouseleave":"mouseout"),this._onMouse,this); } }, //inner onclick object handler _onClick: function(e) { return this._mouseEvent(e,this._click,"ItemClick"); }, //inner ondblclick object handler _onDblClick: function(e) { return this._mouseEvent(e,this._dblclick,"ItemDblClick"); }, //process oncontextmenu events _onContext: function(e) { var id = dhtmlx.html.locate(e, this._id); if (id && !this.callEvent("onBeforeContextMenu", [id,e])) return dhtmlx.html.preventEvent(e); }, /* event throttler - ignore events which occurs too fast during mouse moving there are a lot of event firing - we need no so much also, mouseout can fire when moving inside the same html container - we need to ignore such fake calls */ _onMouse:function(e){ if (dhtmlx._isIE) //make a copy of event, will be used in timed call e = document.createEventObject(event); if (this._mouse_move_timer) //clear old event timer window.clearTimeout(this._mouse_move_timer); //this event just inform about moving operation, we don't care about details this.callEvent("onMouseMoving",[e]); //set new event timer this._mouse_move_timer = window.setTimeout(dhtmlx.bind(function(){ //called only when we have at least 100ms after previous event if (e.type == "mousemove") this._onMouseMove(e); else this._onMouseOut(e); },this),500); }, //inner mousemove object handler _onMouseMove: function(e) { if (!this._mouseEvent(e,this._mouse_move,"MouseMove")) this.callEvent("onMouseOut",[e||event]); }, //inner mouseout object handler _onMouseOut: function(e) { this.callEvent("onMouseOut",[e||event]); }, //common logic for click and dbl-click processing _mouseEvent:function(e,hash,name){ e=e||event; var trg=e.target||e.srcElement; var css = ""; var id = null; var found = false; //loop through all parents while (trg && trg.parentNode){ if (!found){ //if element with ID mark is not detected yet id = trg.getAttribute(this._id); //check id of current one if (id){ if (!this.callEvent("on"+name,[id,e,trg])) return; //it will be triggered only for first detected ID, in case of nested elements found = true; //set found flag } } css=trg.className; if (css){ //check if pre-defined reaction for element's css name exists css = css.split(" "); css = css[0]||css[1]; //FIXME:bad solution, workaround css classes which are starting from whitespace if (hash[css]) return hash[css].call(this,e,id,trg); } trg=trg.parentNode; } return found; //returns true if item was located and event was triggered } }; /* DHX DEPEND FROM FILE 'config.js'*/ /* Behavior:Settings @export customize config */ /*DHX:Depend template.js*/ /*DHX:Depend dhtmlx.js*/ dhtmlx.Settings={ _init:function(){ /* property can be accessed as this.config.some in same time for inner call it have sense to use _settings because it will be minified in final version */ this._settings = this.config=[]; this._settings.sort = null; //mask default method }, define:function(property, value){ if (typeof property == "object") return this._parseSeetingColl(property); return this._define(property, value); }, _define:function(property,value){ dhtmlx.assert_settings.call(this,property,value); //method with name {prop}_setter will be used as property setter //setter is optional var setter = this[property+"_setter"]; return this._settings[property]=setter?setter.call(this,property,value):value; }, //process configuration object _parseSeetingColl:function(coll){ if (coll){ for (var a in coll) //for each setting this._define(a,coll[a]); //set value through config } }, //helper for object initialization _parseSettings:function(obj,initial){ //initial - set of default values var settings = dhtmlx.extend({},initial); //code below will copy all properties over default one if (typeof obj == "object" && !obj.tagName) dhtmlx.extend(settings,obj); //call config for each setting this._parseSeetingColl(settings); }, _mergeSettings:function(config, defaults){ for (var key in defaults) switch(typeof config[key]){ case "object": config[key] = this._mergeSettings((config[key]||{}), defaults[key]); break; case "undefined": config[key] = defaults[key]; break; default: //do nothing break; } return config; }, //helper for html container init _parseContainer:function(obj,name,fallback){ /* parameter can be a config object, in such case real container will be obj.container or it can be html object or ID of html object */ if (typeof obj == "object" && !obj.tagName) obj=obj.container; this._obj = dhtmlx.toNode(obj); if (!this._obj && fallback) this._obj = fallback(obj); dhtmlx.assert(this._obj, "Incorrect html container"); this._obj.className+=" "+name; this._obj.onselectstart=function(){return false;}; //block selection by default this._dataobj = this._obj;//separate reference for rendering modules }, //apply template-type _set_type:function(name){ //parameter can be a hash of settings if (typeof name == "object") return this.type_setter("type",name); dhtmlx.assert(this.types, "RenderStack :: Types are not defined"); dhtmlx.assert(this.types[name],"RenderStack :: Inccorect type name",name); //or parameter can be a name of existing template-type this.type=dhtmlx.extend({},this.types[name]); this.customize(); //init configs }, customize:function(obj){ //apply new properties if (obj) dhtmlx.extend(this.type,obj); //init tempaltes for item start and item end this.type._item_start = dhtmlx.Template.fromHTML(this.template_item_start(this.type)); this.type._item_end = this.template_item_end(this.type); //repaint self this.render(); }, //config.type - creates new template-type, based on configuration object type_setter:function(mode,value){ this._set_type(typeof value == "object"?dhtmlx.Type.add(this,value):value); return value; }, //config.template - creates new template-type with defined template string template_setter:function(mode,value){ return this.type_setter("type",{template:value}); }, //config.css - css name for top level container css_setter:function(mode,value){ this._obj.className += " "+value; return value; } }; /* DHX DEPEND FROM FILE 'compatibility.js'*/ /* Collection of compatibility hacks */ /*DHX:Depend dhtmlx.js*/ dhtmlx.compat=function(name, obj){ //check if name hach present, and applies it when necessary if (dhtmlx.compat[name]) dhtmlx.compat[name](obj); }; (function(){ if (!window.dhtmlxError){ //dhtmlxcommon is not included //create fake error tracker for connectors var dummy = function(){}; window.dhtmlxError={ catchError:dummy, throwError:dummy }; //helpers instead of ones from dhtmlxcommon window.convertStringToBoolean=function(value){ return !!value; }; window.dhtmlxEventable = function(node){ dhtmlx.extend(node,dhtmlx.EventSystem); }; //imitate ajax layer of dhtmlxcommon var loader = { getXMLTopNode:function(name){ }, doXPath:function(path){ return dhtmlx.DataDriver.xml.xpath(this.xml,path); }, xmlDoc:{ responseXML:true } }; //wrap ajax methods of dataprocessor dhtmlx.compat.dataProcessor=function(obj){ //FIXME //this is pretty ugly solution - we replace whole method , so changes in dataprocessor need to be reflected here var sendData = "_sendData"; var in_progress = "_in_progress"; var tMode = "_tMode"; var waitMode = "_waitMode"; obj[sendData]=function(a1,rowId){ if (!a1) return; //nothing to send if (rowId) this[in_progress][rowId]=(new Date()).valueOf(); if (!this.callEvent("onBeforeDataSending",rowId?[rowId,this.getState(rowId)]:[])) return false; var a2 = this; var a3=this.serverProcessor; if (this[tMode]!="POST") //use dhtmlx.ajax instead of old ajax layer dhtmlx.ajax().get(a3+((a3.indexOf("?")!=-1)?"&":"?")+a1,"",function(t,x,xml){ loader.xml = dhtmlx.DataDriver.xml.checkResponse(t,x); a2.afterUpdate(a2, null, null, null, loader); }); else dhtmlx.ajax().post(a3,a1,function(t,x,xml){ loader.xml = dhtmlx.DataDriver.xml.checkResponse(t,x); a2.afterUpdate(a2, null, null, null, loader); }); this[waitMode]++; }; }; } })(); /* DHX DEPEND FROM FILE 'compatibility_layout.js'*/ /*DHX:Depend dhtmlx.js*/ /*DHX:Depend compatibility.js*/ if (!dhtmlx.attaches) dhtmlx.attaches = {}; dhtmlx.attaches.attachAbstract=function(name, conf){ var obj = document.createElement("DIV"); obj.id = "CustomObject_"+dhtmlx.uid(); obj.style.width = "100%"; obj.style.height = "100%"; obj.cmp = "grid"; document.body.appendChild(obj); this.attachObject(obj.id); conf.container = obj.id; this.grid = new window[name](conf); this.gridId = obj.id; this.gridObj = obj; return this.grid; }; dhtmlx.attaches.attachDataView = function(conf){ return this.attachAbstract("dhtmlXDataView",conf); }; dhtmlx.attaches.attachChart = function(conf){ return this.attachAbstract("dhtmlXChart",conf); }; dhtmlx.compat.layout = function(){ if (window.dhtmlXChart) dhtmlXChart.prototype.setSizes = function(){ this.resize(); }; if (window.dhtmlXDataView) dhtmlXDataView.prototype.setSizes = function(){ this.render(); }; }; /* DHX DEPEND FROM FILE 'load.js'*/ /* ajax operations can be used for direct loading as dhtmlx.ajax(ulr, callback) or dhtmlx.ajax().get(url) dhtmlx.ajax().post(url) */ /*DHX:Depend datastore.js*/ /*DHX:Depend dhtmlx.js*/ dhtmlx.ajax = function(url,call,master){ //if parameters was provided - made fast call if (arguments.length!==0){ var http_request = new dhtmlx.ajax(); if (master) http_request.master=master; http_request.get(url,null,call); } if (!this.getXHR) return new dhtmlx.ajax(); //allow to create new instance without direct new declaration return this; }; dhtmlx.ajax.prototype={ //creates xmlHTTP object getXHR:function(){ if (dhtmlx._isIE) return new ActiveXObject("Microsoft.xmlHTTP"); else return new XMLHttpRequest(); }, /* send data to the server params - hash of properties which will be added to the url call - callback, can be an array of functions */ send:function(url,params,call){ var x=this.getXHR(); if (typeof call == "function") call = [call]; //add extra params to the url if (typeof params == "object"){ var t=[]; for (var a in params) t.push(a+"="+encodeURIComponent(params[a]));// utf-8 escaping params=t.join("&"); } if (params && !this.post){ url=url+(url.indexOf("?")!=-1 ? "&" : "?")+params; params=null; } x.open(this.post?"POST":"GET",url,!this._sync); if (this.post) x.setRequestHeader('Content-type','application/x-www-form-urlencoded'); //async mode, define loading callback if (!this._sync){ var self=this; x.onreadystatechange= function(){ if (!x.readyState || x.readyState == 4){ dhtmlx.log_full_time("data_loading"); //log rendering time if (call && self) for (var i=0; i < call.length; i++) //there can be multiple callbacks if (call[i]) call[i].call((self.master||self),x.responseText,x.responseXML,x); self.master=null; call=x=self=null; //anti-leak } }; } x.send(params||null); return x; //return XHR, which can be used in case of sync. mode }, //GET request get:function(url,params,call){ this.post=false; return this.send(url,params,call); }, //POST request post:function(url,params,call){ this.post=true; return this.send(url,params,call); }, sync:function(){ this._sync = true; return this; } }; /* Behavior:DataLoader - load data in the component @export load parse */ dhtmlx.DataLoader={ _init:function(){ //prepare data store this.data = new dhtmlx.DataStore(); }, //loads data from external URL load:function(url,call){ this.callEvent("onXLS",[]); if (typeof call == "string"){ //second parameter can be a loading type or callback this.data.setDriver(call); call = arguments[2]; } //prepare data feed for dyn. loading if (!this.data.feed) this.data.feed = function(from,count){ //allow only single request at same time if (this._load_count) return this._load_count=[from,count]; //save last ignored request else this._load_count=true; this.load(url+((url.indexOf("?")==-1)?"?":"&")+"posStart="+from+"&count="+count,function(){ //after loading check if we have some ignored requests var temp = this._load_count; this._load_count = false; if (typeof temp =="object") this.data.feed.apply(this, temp); //load last ignored request }); }; //load data by async ajax call dhtmlx.ajax(url,[this._onLoad,call],this); }, //loads data from object parse:function(data,type){ this.callEvent("onXLS",[]); if (type) this.data.setDriver(type); this._onLoad(data,null); }, //default after loading callback _onLoad:function(text,xml,loader){ this.data._parse(this.data.driver.toObject(text,xml)); this.callEvent("onXLE",[]); } }; /* Abstraction layer for different data types */ dhtmlx.DataDriver={}; dhtmlx.DataDriver.json={ //convert json string to json object if necessary toObject:function(data){ if (typeof data == "string"){ eval ("dhtmlx.temp="+data); return dhtmlx.temp; } return data; }, //get array of records getRecords:function(data){ if (data && !(data instanceof Array)) return [data]; return data; }, //get hash of properties for single record getDetails:function(data){ return data; }, //get count of data and position at which new data need to be inserted getInfo:function(data){ return { _size:(data.total_count||0), _from:(data.pos||0) }; } }; dhtmlx.DataDriver.html={ /* incoming data can be - collection of nodes - ID of parent container - HTML text */ toObject:function(data){ if (typeof data == "string"){ var t=null; if (data.indexOf("<")==-1) //if no tags inside - probably its an ID t = dhtmlx.toNode(data); if (!t){ t=document.createElement("DIV"); t.innerHTML = data; } return t.getElementsByTagName(this.tag); } return data; }, //get array of records getRecords:function(data){ if (data.tagName) return data.childNodes; return data; }, //get hash of properties for single record getDetails:function(data){ return dhtmlx.DataDriver.xml.tagToObject(data); }, //dyn loading is not supported by HTML data source getInfo:function(data){ return { _size:0, _from:0 }; }, tag: "LI" }; dhtmlx.DataDriver.jsarray={ //eval jsarray string to jsarray object if necessary toObject:function(data){ if (typeof data == "string"){ eval ("dhtmlx.temp="+data); return dhtmlx.temp; } return data; }, //get array of records getRecords:function(data){ return data; }, //get hash of properties for single record, in case of array they will have names as "data{index}" getDetails:function(data){ var result = {}; for (var i=0; i < data.length; i++) result["data"+i]=data[i]; return result; }, //dyn loading is not supported by js-array data source getInfo:function(data){ return { _size:0, _from:0 }; } }; dhtmlx.DataDriver.csv={ //incoming data always a string toObject:function(data){ return data; }, //get array of records getRecords:function(data){ return data.split(this.row); }, //get hash of properties for single record, data named as "data{index}" getDetails:function(data){ data = this.stringToArray(data); var result = {}; for (var i=0; i < data.length; i++) result["data"+i]=data[i]; return result; }, //dyn loading is not supported by csv data source getInfo:function(data){ return { _size:0, _from:0 }; }, //split string in array, takes string surrounding quotes in account stringToArray:function(data){ data = data.split(this.cell); for (var i=0; i < data.length; i++) data[i] = data[i].replace(/^[ \t\n\r]*(\"|)/g,"").replace(/(\"|)[ \t\n\r]*$/g,""); return data; }, row:"\n", //default row separator cell:"," //default cell separator }; dhtmlx.DataDriver.xml={ //convert xml string to xml object if necessary toObject:function(text,xml){ if (xml && (xml=this.checkResponse(text,xml))) //checkResponse - fix incorrect content type and extra whitespaces errors return xml; if (typeof text == "string"){ return this.fromString(text); } return text; }, //get array of records getRecords:function(data){ return this.xpath(data,this.records); }, records:"/*/item", userdata:"/*/userdata", //get hash of properties for single record getDetails:function(data){ return this.tagToObject(data,{}); }, getUserData:function(data,col){ col = col || {}; var ud = this.xpath(data,this.userdata); for (var i=0; i < ud.length; i++) { var udx = this.tagToObject(ud[i]); col[udx.name] = udx.value; } return col; }, //get count of data and position at which new data_loading need to be inserted getInfo:function(data){ return { _size:(data.documentElement.getAttribute("total_count")||0), _from:(data.documentElement.getAttribute("pos")||0) }; }, //xpath helper xpath:function(xml,path){ if (window.XPathResult){ //FF, KHTML, Opera var node=xml; if(xml.nodeName.indexOf("document")==-1) xml=xml.ownerDocument; var res = []; var col = xml.evaluate(path, node, null, XPathResult.ANY_TYPE, null); var temp = col.iterateNext(); while (temp){ res.push(temp); temp = col.iterateNext(); } return res; } //IE return xml.selectNodes(path); }, //convert xml tag to js object, all subtags and attributes are mapped to the properties of result object tagToObject:function(tag,z){ z=z||{}; //map attributes var a=tag.attributes; for (var i=0; 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(end<=nmax){ end += step; end = parseFloat((new Number(end)).toFixed(Math.abs(power))); } return {start:start,end:end,step:step,fixNum:Math.abs(power)}; }, log10:function(n){ return Math.floor((Math.log(n)/Math.LN10)); }, _drawXAxisLabel:function(x,obj){ if (!this._settings.xAxis) return; var elem = this.renderTextAt(false, true, x, this._obj.offsetHeight-parseInt(this._settings.padding.bottom,10), this._settings.xAxis.template(obj)); }, _drawXAxisLine:function(ctx,x,y1,y2){ if (!this._settings.xAxis||!this._settings.xAxis.lines) return; this._drawLine(ctx,x,y1,x,y2,this._settings.xAxis.color,0.2); }, _drawLine:function(ctx,x1,y1,x2,y2,color,width){ ctx.strokeStyle = color; ctx.lineWidth = width; ctx.beginPath(); ctx.moveTo(x1,y1); ctx.lineTo(x2,y2); ctx.stroke(); }, _getRelativeValue:function(minValue,maxValue){ var relValue; var valueFactor = 1; if(maxValue != minValue){ relValue = maxValue - minValue; if(Math.abs(relValue) < 1){ while(Math.abs(relValue)<1){ valueFactor *= 10; relValue *= valueFactor; } } } else relValue = minValue; return [relValue,valueFactor]; }, _rainbow : [ function(pos){ return "#FF"+dhtmlx.math.toHex(pos/2,2)+"00";}, function(pos){ return "#FF"+dhtmlx.math.toHex(pos/2+128,2)+"00";}, function(pos){ return "#"+dhtmlx.math.toHex(255-pos,2)+"FF00";}, function(pos){ return "#00FF"+dhtmlx.math.toHex(pos,2);}, function(pos){ return "#00"+dhtmlx.math.toHex(255-pos,2)+"FF";}, function(pos){ return "#"+dhtmlx.math.toHex(pos,2)+"00FF";} ] }; dhtmlx.compat("layout");