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