egroupware_official/phpgwapi/js/dhtmlxGantt/codebase/sources/dhtmlxgantt.js

8378 lines
236 KiB
JavaScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
dhtmlxGantt v.2.1.1 Standard
This software is covered by GPL license. You also can obtain Commercial or Enterprise license to use it in non-GPL project - please contact sales@dhtmlx.com. Usage without proper license is prohibited.
(c) Dinamenta, UAB.
*/
if (!window.dhtmlx) {
dhtmlx = function(obj){
for (var a in obj) dhtmlx[a]=obj[a];
return dhtmlx; //simple singleton
};
}
dhtmlx.extend_api=function(name,map,ext){
var t = window[name];
if (!t) return; //component not defined
window[name]=function(obj){
var that;
if (obj && typeof obj == "object" && !obj.tagName){
that = t.apply(this,(map._init?map._init(obj):arguments));
//global settings
for (var a in dhtmlx)
if (map[a]) this[map[a]](dhtmlx[a]);
//local settings
for (var a in obj){
if (map[a]) this[map[a]](obj[a]);
else if (a.indexOf("on")===0){
this.attachEvent(a,obj[a]);
}
}
} else
that = t.apply(this,arguments);
if (map._patch) map._patch(this);
return that||this;
};
window[name].prototype=t.prototype;
if (ext)
dhtmlXHeir(window[name].prototype,ext);
};
dhtmlxAjax={
get:function(url,callback){
var t=new dtmlXMLLoaderObject(true);
t.async=(arguments.length<3);
t.waitCall=callback;
t.loadXML(url);
return t;
},
post:function(url,post,callback){
var t=new dtmlXMLLoaderObject(true);
t.async=(arguments.length<4);
t.waitCall=callback;
t.loadXML(url,true,post);
return t;
},
getSync:function(url){
return this.get(url,null,true);
},
postSync:function(url,post){
return this.post(url,post,null,true);
}
};
/**
* @desc: xmlLoader object
* @type: private
* @param: funcObject - xml parser function
* @param: object - jsControl object
* @param: async - sync/async mode (async by default)
* @param: rSeed - enable/disable random seed ( prevent IE caching)
* @topic: 0
*/
function dtmlXMLLoaderObject(funcObject, dhtmlObject, async, rSeed){
this.xmlDoc="";
if (typeof (async) != "undefined")
this.async=async;
else
this.async=true;
this.onloadAction=funcObject||null;
this.mainObject=dhtmlObject||null;
this.waitCall=null;
this.rSeed=rSeed||false;
return this;
}
dtmlXMLLoaderObject.count = 0;
/**
* @desc: xml loading handler
* @type: private
* @param: dtmlObject - xmlLoader object
* @topic: 0
*/
dtmlXMLLoaderObject.prototype.waitLoadFunction=function(dhtmlObject){
var once = true;
this.check=function (){
if ((dhtmlObject)&&(dhtmlObject.onloadAction)){
if ((!dhtmlObject.xmlDoc.readyState)||(dhtmlObject.xmlDoc.readyState == 4)){
if (!once)
return;
once=false; //IE 5 fix
dtmlXMLLoaderObject.count++;
if (typeof dhtmlObject.onloadAction == "function")
dhtmlObject.onloadAction(dhtmlObject.mainObject, null, null, null, dhtmlObject);
if (dhtmlObject.waitCall){
dhtmlObject.waitCall.call(this,dhtmlObject);
dhtmlObject.waitCall=null;
}
}
}
};
return this.check;
};
/**
* @desc: return XML top node
* @param: tagName - top XML node tag name (not used in IE, required for Safari and Mozilla)
* @type: private
* @returns: top XML node
* @topic: 0
*/
dtmlXMLLoaderObject.prototype.getXMLTopNode=function(tagName, oldObj){
var z;
if (this.xmlDoc.responseXML){
var temp = this.xmlDoc.responseXML.getElementsByTagName(tagName);
if(temp.length === 0 && tagName.indexOf(":")!=-1)
var temp = this.xmlDoc.responseXML.getElementsByTagName((tagName.split(":"))[1]);
z = temp[0];
} else
z = this.xmlDoc.documentElement;
if (z){
this._retry=false;
return z;
}
if (!this._retry&&_isIE){
this._retry=true;
var oldObj = this.xmlDoc;
this.loadXMLString(this.xmlDoc.responseText.replace(/^[\s]+/,""), true);
return this.getXMLTopNode(tagName, oldObj);
}
dhtmlxError.throwError("LoadXML", "Incorrect XML", [
(oldObj||this.xmlDoc),
this.mainObject
]);
return document.createElement("DIV");
};
/**
* @desc: load XML from string
* @type: private
* @param: xmlString - xml string
* @topic: 0
*/
dtmlXMLLoaderObject.prototype.loadXMLString=function(xmlString, silent){
if (!_isIE){
var parser = new DOMParser();
this.xmlDoc=parser.parseFromString(xmlString, "text/xml");
} else {
this.xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
this.xmlDoc.async=this.async;
this.xmlDoc.onreadystatechange = function(){};
this.xmlDoc["loadXM"+"L"](xmlString);
}
if (silent)
return;
if (this.onloadAction)
this.onloadAction(this.mainObject, null, null, null, this);
if (this.waitCall){
this.waitCall();
this.waitCall=null;
}
};
/**
* @desc: load XML
* @type: private
* @param: filePath - xml file path
* @param: postMode - send POST request
* @param: postVars - list of vars for post request
* @topic: 0
*/
dtmlXMLLoaderObject.prototype.loadXML=function(filePath, postMode, postVars, rpc){
if (this.rSeed)
filePath+=((filePath.indexOf("?") != -1) ? "&" : "?")+"a_dhx_rSeed="+(new Date()).valueOf();
this.filePath=filePath;
if ((!_isIE)&&(window.XMLHttpRequest))
this.xmlDoc=new XMLHttpRequest();
else {
this.xmlDoc=new ActiveXObject("Microsoft.XMLHTTP");
}
if (this.async)
this.xmlDoc.onreadystatechange=new this.waitLoadFunction(this);
this.xmlDoc.open(postMode ? "POST" : "GET", filePath, this.async);
if (rpc){
this.xmlDoc.setRequestHeader("User-Agent", "dhtmlxRPC v0.1 ("+navigator.userAgent+")");
this.xmlDoc.setRequestHeader("Content-type", "text/xml");
}
else if (postMode)
this.xmlDoc.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
this.xmlDoc.setRequestHeader("X-Requested-With","XMLHttpRequest");
this.xmlDoc.send(null||postVars);
if (!this.async)
(new this.waitLoadFunction(this))();
};
/**
* @desc: destructor, cleans used memory
* @type: private
* @topic: 0
*/
dtmlXMLLoaderObject.prototype.destructor=function(){
this._filterXPath = null;
this._getAllNamedChilds = null;
this._retry = null;
this.async = null;
this.rSeed = null;
this.filePath = null;
this.onloadAction = null;
this.mainObject = null;
this.xmlDoc = null;
this.doXPath = null;
this.doXPathOpera = null;
this.doXSLTransToObject = null;
this.doXSLTransToString = null;
this.loadXML = null;
this.loadXMLString = null;
// this.waitLoadFunction = null;
this.doSerialization = null;
this.xmlNodeToJSON = null;
this.getXMLTopNode = null;
this.setXSLParamValue = null;
return null;
};
dtmlXMLLoaderObject.prototype.xmlNodeToJSON = function(node){
var t={};
for (var i=0; i<node.attributes.length; i++)
t[node.attributes[i].name]=node.attributes[i].value;
t["_tagvalue"]=node.firstChild?node.firstChild.nodeValue:"";
for (var i=0; i<node.childNodes.length; i++){
var name=node.childNodes[i].tagName;
if (name){
if (!t[name]) t[name]=[];
t[name].push(this.xmlNodeToJSON(node.childNodes[i]));
}
}
return t;
};
/**
* @desc: Call wrapper
* @type: private
* @param: funcObject - action handler
* @param: dhtmlObject - user data
* @returns: function handler
* @topic: 0
*/
function callerFunction(funcObject, dhtmlObject){
this.handler=function(e){
if (!e)
e=window.event;
funcObject(e, dhtmlObject);
return true;
};
return this.handler;
}
/**
* @desc: Calculate absolute position of html object
* @type: private
* @param: htmlObject - html object
* @topic: 0
*/
function getAbsoluteLeft(htmlObject){
return getOffset(htmlObject).left;
}
/**
* @desc: Calculate absolute position of html object
* @type: private
* @param: htmlObject - html object
* @topic: 0
*/
function getAbsoluteTop(htmlObject){
return getOffset(htmlObject).top;
}
function getOffsetSum(elem) {
var top=0, left=0;
while(elem) {
top = top + parseInt(elem.offsetTop);
left = left + parseInt(elem.offsetLeft);
elem = elem.offsetParent;
}
return {top: top, left: left};
}
function getOffsetRect(elem) {
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 { top: Math.round(top), left: Math.round(left) };
}
function getOffset(elem) {
if (elem.getBoundingClientRect) {
return getOffsetRect(elem);
} else {
return getOffsetSum(elem);
}
}
/**
* @desc: Convert string to it boolean representation
* @type: private
* @param: inputString - string for covertion
* @topic: 0
*/
function convertStringToBoolean(inputString){
if (typeof (inputString) == "string")
inputString=inputString.toLowerCase();
switch (inputString){
case "1":
case "true":
case "yes":
case "y":
case 1:
case true:
return true;
default:
return false;
}
}
/**
* @desc: find out what symbol to use as url param delimiters in further params
* @type: private
* @param: str - current url string
* @topic: 0
*/
function getUrlSymbol(str){
if (str.indexOf("?") != -1)
return "&";
else
return "?";
}
function dhtmlDragAndDropObject(){
if (window.dhtmlDragAndDrop)
return window.dhtmlDragAndDrop;
this.lastLanding=0;
this.dragNode=0;
this.dragStartNode=0;
this.dragStartObject=0;
this.tempDOMU=null;
this.tempDOMM=null;
this.waitDrag=0;
window.dhtmlDragAndDrop=this;
return this;
}
dhtmlDragAndDropObject.prototype.removeDraggableItem=function(htmlNode){
htmlNode.onmousedown=null;
htmlNode.dragStarter=null;
htmlNode.dragLanding=null;
};
dhtmlDragAndDropObject.prototype.addDraggableItem=function(htmlNode, dhtmlObject){
htmlNode.onmousedown=this.preCreateDragCopy;
htmlNode.dragStarter=dhtmlObject;
this.addDragLanding(htmlNode, dhtmlObject);
};
dhtmlDragAndDropObject.prototype.addDragLanding=function(htmlNode, dhtmlObject){
htmlNode.dragLanding=dhtmlObject;
};
dhtmlDragAndDropObject.prototype.preCreateDragCopy=function(e){
if ((e||window.event) && (e||event).button == 2)
return;
if (window.dhtmlDragAndDrop.waitDrag){
window.dhtmlDragAndDrop.waitDrag=0;
document.body.onmouseup=window.dhtmlDragAndDrop.tempDOMU;
document.body.onmousemove=window.dhtmlDragAndDrop.tempDOMM;
return false;
}
if (window.dhtmlDragAndDrop.dragNode)
window.dhtmlDragAndDrop.stopDrag(e);
window.dhtmlDragAndDrop.waitDrag=1;
window.dhtmlDragAndDrop.tempDOMU=document.body.onmouseup;
window.dhtmlDragAndDrop.tempDOMM=document.body.onmousemove;
window.dhtmlDragAndDrop.dragStartNode=this;
window.dhtmlDragAndDrop.dragStartObject=this.dragStarter;
document.body.onmouseup=window.dhtmlDragAndDrop.preCreateDragCopy;
document.body.onmousemove=window.dhtmlDragAndDrop.callDrag;
window.dhtmlDragAndDrop.downtime = new Date().valueOf();
if ((e)&&(e.preventDefault)){
e.preventDefault();
return false;
}
return false;
};
dhtmlDragAndDropObject.prototype.callDrag=function(e){
if (!e)
e=window.event;
var dragger=window.dhtmlDragAndDrop;
if ((new Date()).valueOf()-dragger.downtime<100) return;
//if ((e.button == 0)&&(_isIE))
// return dragger.stopDrag();
if (!dragger.dragNode){
if (dragger.waitDrag){
dragger.dragNode=dragger.dragStartObject._createDragNode(dragger.dragStartNode, e);
if (!dragger.dragNode)
return dragger.stopDrag();
dragger.dragNode.onselectstart=function(){return false;};
dragger.gldragNode=dragger.dragNode;
document.body.appendChild(dragger.dragNode);
document.body.onmouseup=dragger.stopDrag;
dragger.waitDrag=0;
dragger.dragNode.pWindow=window;
dragger.initFrameRoute();
}
else return dragger.stopDrag(e, true);
}
if (dragger.dragNode.parentNode != window.document.body && dragger.gldragNode){
var grd = dragger.gldragNode;
if (dragger.gldragNode.old)
grd=dragger.gldragNode.old;
//if (!document.all) dragger.calculateFramePosition();
grd.parentNode.removeChild(grd);
var oldBody = dragger.dragNode.pWindow;
if (grd.pWindow && grd.pWindow.dhtmlDragAndDrop.lastLanding)
grd.pWindow.dhtmlDragAndDrop.lastLanding.dragLanding._dragOut(grd.pWindow.dhtmlDragAndDrop.lastLanding);
// var oldp=dragger.dragNode.parentObject;
if (_isIE){
var div = document.createElement("Div");
div.innerHTML=dragger.dragNode.outerHTML;
dragger.dragNode=div.childNodes[0];
} else
dragger.dragNode=dragger.dragNode.cloneNode(true);
dragger.dragNode.pWindow=window;
// dragger.dragNode.parentObject=oldp;
dragger.gldragNode.old=dragger.dragNode;
document.body.appendChild(dragger.dragNode);
oldBody.dhtmlDragAndDrop.dragNode=dragger.dragNode;
}
dragger.dragNode.style.left=e.clientX+15 +
(dragger.fx ? dragger.fx*(-1) : 0) +
(document.body.scrollLeft||document.documentElement.scrollLeft)+"px";
dragger.dragNode.style.top=e.clientY+3+
(dragger.fy ? dragger.fy*(-1) : 0) +
(document.body.scrollTop||document.documentElement.scrollTop)+"px";
var z;
if (!e.srcElement)
z = e.target;
else
z=e.srcElement;
dragger.checkLanding(z, e);
};
dhtmlDragAndDropObject.prototype.calculateFramePosition=function(n){
//this.fx = 0, this.fy = 0;
if (window.name){
var el = parent.frames[window.name].frameElement.offsetParent;
var fx = 0;
var fy = 0;
while (el){
fx+=el.offsetLeft;
fy+=el.offsetTop;
el=el.offsetParent;
}
if ((parent.dhtmlDragAndDrop)){
var ls = parent.dhtmlDragAndDrop.calculateFramePosition(1);
fx+=ls.split('_')[0]*1;
fy+=ls.split('_')[1]*1;
}
if (n)
return fx+"_"+fy;
else
this.fx=fx;
this.fy=fy;
}
return "0_0";
};
dhtmlDragAndDropObject.prototype.checkLanding=function(htmlObject, e){
if ((htmlObject)&&(htmlObject.dragLanding)){
if (this.lastLanding)
this.lastLanding.dragLanding._dragOut(this.lastLanding);
this.lastLanding=htmlObject;
this.lastLanding=this.lastLanding.dragLanding._dragIn(this.lastLanding, this.dragStartNode, e.clientX,
e.clientY, e);
this.lastLanding_scr=(_isIE ? e.srcElement : e.target);
} else {
if ((htmlObject)&&(htmlObject.tagName != "BODY"))
this.checkLanding(htmlObject.parentNode, e);
else {
if (this.lastLanding)
this.lastLanding.dragLanding._dragOut(this.lastLanding, e.clientX, e.clientY, e);
this.lastLanding=0;
if (this._onNotFound)
this._onNotFound();
}
}
};
dhtmlDragAndDropObject.prototype.stopDrag=function(e, mode){
var dragger=window.dhtmlDragAndDrop;
if (!mode){
dragger.stopFrameRoute();
var temp = dragger.lastLanding;
dragger.lastLanding=null;
if (temp)
temp.dragLanding._drag(dragger.dragStartNode, dragger.dragStartObject, temp,
(_isIE ? event.srcElement : e.target));
}
dragger.lastLanding=null;
if ((dragger.dragNode)&&(dragger.dragNode.parentNode == document.body))
dragger.dragNode.parentNode.removeChild(dragger.dragNode);
dragger.dragNode=0;
dragger.gldragNode=0;
dragger.fx=0;
dragger.fy=0;
dragger.dragStartNode=0;
dragger.dragStartObject=0;
document.body.onmouseup=dragger.tempDOMU;
document.body.onmousemove=dragger.tempDOMM;
dragger.tempDOMU=null;
dragger.tempDOMM=null;
dragger.waitDrag=0;
};
dhtmlDragAndDropObject.prototype.stopFrameRoute=function(win){
if (win)
window.dhtmlDragAndDrop.stopDrag(1, 1);
for (var i = 0; i < window.frames.length; i++){
try{
if ((window.frames[i] != win)&&(window.frames[i].dhtmlDragAndDrop))
window.frames[i].dhtmlDragAndDrop.stopFrameRoute(window);
} catch(e){}
}
try{
if ((parent.dhtmlDragAndDrop)&&(parent != window)&&(parent != win))
parent.dhtmlDragAndDrop.stopFrameRoute(window);
} catch(e){}
};
dhtmlDragAndDropObject.prototype.initFrameRoute=function(win, mode){
if (win){
window.dhtmlDragAndDrop.preCreateDragCopy();
window.dhtmlDragAndDrop.dragStartNode=win.dhtmlDragAndDrop.dragStartNode;
window.dhtmlDragAndDrop.dragStartObject=win.dhtmlDragAndDrop.dragStartObject;
window.dhtmlDragAndDrop.dragNode=win.dhtmlDragAndDrop.dragNode;
window.dhtmlDragAndDrop.gldragNode=win.dhtmlDragAndDrop.dragNode;
window.document.body.onmouseup=window.dhtmlDragAndDrop.stopDrag;
window.waitDrag=0;
if (((!_isIE)&&(mode))&&((!_isFF)||(_FFrv < 1.8)))
window.dhtmlDragAndDrop.calculateFramePosition();
}
try{
if ((parent.dhtmlDragAndDrop)&&(parent != window)&&(parent != win))
parent.dhtmlDragAndDrop.initFrameRoute(window);
}catch(e){}
for (var i = 0; i < window.frames.length; i++){
try{
if ((window.frames[i] != win)&&(window.frames[i].dhtmlDragAndDrop))
window.frames[i].dhtmlDragAndDrop.initFrameRoute(window, ((!win||mode) ? 1 : 0));
} catch(e){}
}
};
_isFF = false;
_isIE = false;
_isOpera = false;
_isKHTML = false;
_isMacOS = false;
_isChrome = false;
_FFrv = false;
_KHTMLrv = false;
_OperaRv = false;
if (navigator.userAgent.indexOf('Macintosh') != -1)
_isMacOS=true;
if (navigator.userAgent.toLowerCase().indexOf('chrome')>-1)
_isChrome=true;
if ((navigator.userAgent.indexOf('Safari') != -1)||(navigator.userAgent.indexOf('Konqueror') != -1)){
_KHTMLrv = parseFloat(navigator.userAgent.substr(navigator.userAgent.indexOf('Safari')+7, 5));
if (_KHTMLrv > 525){ //mimic FF behavior for Safari 3.1+
_isFF=true;
_FFrv = 1.9;
} else
_isKHTML=true;
} else if (navigator.userAgent.indexOf('Opera') != -1){
_isOpera=true;
_OperaRv=parseFloat(navigator.userAgent.substr(navigator.userAgent.indexOf('Opera')+6, 3));
}
else if (navigator.appName.indexOf("Microsoft") != -1){
_isIE=true;
if ((navigator.appVersion.indexOf("MSIE 8.0")!= -1 || navigator.appVersion.indexOf("MSIE 9.0")!= -1 || navigator.appVersion.indexOf("MSIE 10.0")!= -1 ) && document.compatMode != "BackCompat"){
_isIE=8;
}
} else if (navigator.appName == 'Netscape' && navigator.userAgent.indexOf("Trident") != -1){
//ie11
_isIE=8;
} else {
_isFF=true;
_FFrv = parseFloat(navigator.userAgent.split("rv:")[1]);
}
//multibrowser Xpath processor
dtmlXMLLoaderObject.prototype.doXPath=function(xpathExp, docObj, namespace, result_type){
if (_isKHTML || (!_isIE && !window.XPathResult))
return this.doXPathOpera(xpathExp, docObj);
if (_isIE){ //IE
if (!docObj)
if (!this.xmlDoc.nodeName)
docObj=this.xmlDoc.responseXML;
else
docObj=this.xmlDoc;
if (!docObj)
dhtmlxError.throwError("LoadXML", "Incorrect XML", [
(docObj||this.xmlDoc),
this.mainObject
]);
if (namespace)
docObj.setProperty("SelectionNamespaces", "xmlns:xsl='"+namespace+"'"); //
if (result_type == 'single'){
return docObj.selectSingleNode(xpathExp);
}
else {
return docObj.selectNodes(xpathExp)||new Array(0);
}
} else { //Mozilla
var nodeObj = docObj;
if (!docObj){
if (!this.xmlDoc.nodeName){
docObj=this.xmlDoc.responseXML;
}
else {
docObj=this.xmlDoc;
}
}
if (!docObj)
dhtmlxError.throwError("LoadXML", "Incorrect XML", [
(docObj||this.xmlDoc),
this.mainObject
]);
if (docObj.nodeName.indexOf("document") != -1){
nodeObj=docObj;
}
else {
nodeObj=docObj;
docObj=docObj.ownerDocument;
}
var retType = XPathResult.ANY_TYPE;
if (result_type == 'single')
retType=XPathResult.FIRST_ORDERED_NODE_TYPE;
var rowsCol = [];
var col = docObj.evaluate(xpathExp, nodeObj, function(pref){
return namespace;
}, retType, null);
if (retType == XPathResult.FIRST_ORDERED_NODE_TYPE){
return col.singleNodeValue;
}
var thisColMemb = col.iterateNext();
while (thisColMemb){
rowsCol[rowsCol.length]=thisColMemb;
thisColMemb=col.iterateNext();
}
return rowsCol;
}
};
function _dhtmlxError(type, name, params){
if (!this.catches)
this.catches=[];
return this;
}
_dhtmlxError.prototype.catchError=function(type, func_name){
this.catches[type]=func_name;
};
_dhtmlxError.prototype.throwError=function(type, name, params){
if (this.catches[type])
return this.catches[type](type, name, params);
if (this.catches["ALL"])
return this.catches["ALL"](type, name, params);
window.alert("Error type: "+arguments[0]+"\nDescription: "+arguments[1]);
return null;
};
window.dhtmlxError=new _dhtmlxError();
//opera fake, while 9.0 not released
//multibrowser Xpath processor
dtmlXMLLoaderObject.prototype.doXPathOpera=function(xpathExp, docObj){
//this is fake for Opera
var z = xpathExp.replace(/[\/]+/gi, "/").split('/');
var obj = null;
var i = 1;
if (!z.length)
return [];
if (z[0] == ".")
obj=[docObj]; else if (z[0] === ""){
obj=(this.xmlDoc.responseXML||this.xmlDoc).getElementsByTagName(z[i].replace(/\[[^\]]*\]/g, ""));
i++;
} else
return [];
for (i; i < z.length; i++)obj=this._getAllNamedChilds(obj, z[i]);
if (z[i-1].indexOf("[") != -1)
obj=this._filterXPath(obj, z[i-1]);
return obj;
};
dtmlXMLLoaderObject.prototype._filterXPath=function(a, b){
var c = [];
var b = b.replace(/[^\[]*\[\@/g, "").replace(/[\[\]\@]*/g, "");
for (var i = 0; i < a.length; i++)
if (a[i].getAttribute(b))
c[c.length]=a[i];
return c;
};
dtmlXMLLoaderObject.prototype._getAllNamedChilds=function(a, b){
var c = [];
if (_isKHTML)
b=b.toUpperCase();
for (var i = 0; i < a.length; i++)for (var j = 0; j < a[i].childNodes.length; j++){
if (_isKHTML){
if (a[i].childNodes[j].tagName&&a[i].childNodes[j].tagName.toUpperCase() == b)
c[c.length]=a[i].childNodes[j];
}
else if (a[i].childNodes[j].tagName == b)
c[c.length]=a[i].childNodes[j];
}
return c;
};
function dhtmlXHeir(a, b){
for (var c in b)
if (typeof (b[c]) == "function")
a[c]=b[c];
return a;
}
function dhtmlxEvent(el, event, handler){
if (el.addEventListener)
el.addEventListener(event, handler, false);
else if (el.attachEvent)
el.attachEvent("on"+event, handler);
}
//============= XSL Extension ===================================
dtmlXMLLoaderObject.prototype.xslDoc=null;
dtmlXMLLoaderObject.prototype.setXSLParamValue=function(paramName, paramValue, xslDoc){
if (!xslDoc)
xslDoc=this.xslDoc;
if (xslDoc.responseXML)
xslDoc=xslDoc.responseXML;
var item =
this.doXPath("/xsl:stylesheet/xsl:variable[@name='"+paramName+"']", xslDoc,
"http:/\/www.w3.org/1999/XSL/Transform", "single");
if (item)
item.firstChild.nodeValue=paramValue;
};
dtmlXMLLoaderObject.prototype.doXSLTransToObject=function(xslDoc, xmlDoc){
if (!xslDoc)
xslDoc=this.xslDoc;
if (xslDoc.responseXML)
xslDoc=xslDoc.responseXML;
if (!xmlDoc)
xmlDoc=this.xmlDoc;
if (xmlDoc.responseXML)
xmlDoc=xmlDoc.responseXML;
var result;
//Mozilla
if (!_isIE){
if (!this.XSLProcessor){
this.XSLProcessor=new XSLTProcessor();
this.XSLProcessor.importStylesheet(xslDoc);
}
result = this.XSLProcessor.transformToDocument(xmlDoc);
} else {
result = new ActiveXObject("Msxml2.DOMDocument.3.0");
try{
xmlDoc.transformNodeToObject(xslDoc, result);
}catch(e){
result = xmlDoc.transformNode(xslDoc);
}
}
return result;
};
dtmlXMLLoaderObject.prototype.doXSLTransToString=function(xslDoc, xmlDoc){
var res = this.doXSLTransToObject(xslDoc, xmlDoc);
if(typeof(res)=="string")
return res;
return this.doSerialization(res);
};
dtmlXMLLoaderObject.prototype.doSerialization=function(xmlDoc){
if (!xmlDoc)
xmlDoc=this.xmlDoc;
if (xmlDoc.responseXML)
xmlDoc=xmlDoc.responseXML;
if (!_isIE){
var xmlSerializer = new XMLSerializer();
return xmlSerializer.serializeToString(xmlDoc);
} else
return xmlDoc.xml;
};
/**
* @desc:
* @type: private
*/
dhtmlxEventable=function(obj){
obj.attachEvent=function(name, catcher, callObj){
name='ev_'+name.toLowerCase();
if (!this[name])
this[name]=new this.eventCatcher(callObj||this);
return(name+':'+this[name].addEvent(catcher)); //return ID (event name & event ID)
};
obj.callEvent=function(name, arg0){
name='ev_'+name.toLowerCase();
if (this[name])
return this[name].apply(this, arg0);
return true;
};
obj.checkEvent=function(name){
return (!!this['ev_'+name.toLowerCase()]);
};
obj.eventCatcher=function(obj){
var dhx_catch = [];
var z = function(){
var res = true;
for (var i = 0; i < dhx_catch.length; i++){
if (dhx_catch[i]){
var zr = dhx_catch[i].apply(obj, arguments);
res=res&&zr;
}
}
return res;
};
z.addEvent=function(ev){
if (typeof (ev) != "function")
ev=eval(ev);
if (ev)
return dhx_catch.push(ev)-1;
return false;
};
z.removeEvent=function(id){
dhx_catch[id]=null;
};
return z;
};
obj.detachEvent=function(id){
if (id){
var list = id.split(':'); //get EventName and ID
this[list[0]].removeEvent(list[1]); //remove event
}
};
obj.detachAllEvents = function(){
for (var name in this){
if (name.indexOf("ev_")===0){
this.detachEvent(name);
this[name] = null;
}
}
};
obj = null;
};
if(!window.dhtmlx)
window.dhtmlx = {};
(function(){
var _dhx_msg_cfg = null;
function callback(config, result){
var usercall = config.callback;
modality(false);
config.box.parentNode.removeChild(config.box);
_dhx_msg_cfg = config.box = null;
if (usercall)
usercall(result);
}
function modal_key(e){
if (_dhx_msg_cfg){
e = e||event;
var code = e.which||event.keyCode;
if (dhtmlx.message.keyboard){
if (code == 13 || code == 32)
callback(_dhx_msg_cfg, true);
if (code == 27)
callback(_dhx_msg_cfg, false);
}
if (e.preventDefault)
e.preventDefault();
return !(e.cancelBubble = true);
}
}
if (document.attachEvent)
document.attachEvent("onkeydown", modal_key);
else
document.addEventListener("keydown", modal_key, true);
function modality(mode){
if(!modality.cover){
modality.cover = document.createElement("DIV");
//necessary for IE only
modality.cover.onkeydown = modal_key;
modality.cover.className = "dhx_modal_cover";
document.body.appendChild(modality.cover);
}
var height = document.body.scrollHeight;
modality.cover.style.display = mode?"inline-block":"none";
}
function button(text, result){
var button_css = "dhtmlx_"+text.toLowerCase().replace(/ /g, "_")+"_button"; // dhtmlx_ok_button, dhtmlx_click_me_button
return "<div class='dhtmlx_popup_button "+button_css+"' result='"+result+"' ><div>"+text+"</div></div>";
}
function info(text){
if (!t.area){
t.area = document.createElement("DIV");
t.area.className = "dhtmlx_message_area";
t.area.style[t.position]="5px";
document.body.appendChild(t.area);
}
t.hide(text.id);
var message = document.createElement("DIV");
message.innerHTML = "<div>"+text.text+"</div>";
message.className = "dhtmlx-info dhtmlx-" + text.type;
message.onclick = function(){
t.hide(text.id);
text = null;
};
if (t.position == "bottom" && t.area.firstChild)
t.area.insertBefore(message,t.area.firstChild);
else
t.area.appendChild(message);
if (text.expire > 0)
t.timers[text.id]=window.setTimeout(function(){
t.hide(text.id);
}, text.expire);
t.pull[text.id] = message;
message = null;
return text.id;
}
function _boxStructure(config, ok, cancel){
var box = document.createElement("DIV");
box.className = " dhtmlx_modal_box dhtmlx-"+config.type;
box.setAttribute("dhxbox", 1);
var inner = '';
if (config.width)
box.style.width = config.width;
if (config.height)
box.style.height = config.height;
if (config.title)
inner+='<div class="dhtmlx_popup_title">'+config.title+'</div>';
inner+='<div class="dhtmlx_popup_text"><span>'+(config.content?'':config.text)+'</span></div><div class="dhtmlx_popup_controls">';
if (ok)
inner += button(config.ok || "OK", true);
if (cancel)
inner += button(config.cancel || "Cancel", false);
if (config.buttons){
for (var i=0; i<config.buttons.length; i++)
inner += button(config.buttons[i],i);
}
inner += '</div>';
box.innerHTML = inner;
if (config.content){
var node = config.content;
if (typeof node == "string")
node = document.getElementById(node);
if (node.style.display == 'none')
node.style.display = "";
box.childNodes[config.title?1:0].appendChild(node);
}
box.onclick = function(e){
e = e ||event;
var source = e.target || e.srcElement;
if (!source.className) source = source.parentNode;
if (source.className.split(" ")[0] == "dhtmlx_popup_button"){
var result = source.getAttribute("result");
result = (result == "true")||(result == "false"?false:result);
callback(config, result);
}
};
config.box = box;
if (ok||cancel)
_dhx_msg_cfg = config;
return box;
}
function _createBox(config, ok, cancel){
var box = config.tagName ? config : _boxStructure(config, ok, cancel);
if (!config.hidden)
modality(true);
document.body.appendChild(box);
var x = Math.abs(Math.floor(((window.innerWidth||document.documentElement.offsetWidth) - box.offsetWidth)/2));
var y = Math.abs(Math.floor(((window.innerHeight||document.documentElement.offsetHeight) - box.offsetHeight)/2));
if (config.position == "top")
box.style.top = "-3px";
else
box.style.top = y+'px';
box.style.left = x+'px';
//necessary for IE only
box.onkeydown = modal_key;
box.focus();
if (config.hidden)
dhtmlx.modalbox.hide(box);
return box;
}
function alertPopup(config){
return _createBox(config, true, false);
}
function confirmPopup(config){
return _createBox(config, true, true);
}
function boxPopup(config){
return _createBox(config);
}
function box_params(text, type, callback){
if (typeof text != "object"){
if (typeof type == "function"){
callback = type;
type = "";
}
text = {text:text, type:type, callback:callback };
}
return text;
}
function params(text, type, expire, id){
if (typeof text != "object")
text = {text:text, type:type, expire:expire, id:id};
text.id = text.id||t.uid();
text.expire = text.expire||t.expire;
return text;
}
dhtmlx.alert = function(){
var text = box_params.apply(this, arguments);
text.type = text.type || "confirm";
return alertPopup(text);
};
dhtmlx.confirm = function(){
var text = box_params.apply(this, arguments);
text.type = text.type || "alert";
return confirmPopup(text);
};
dhtmlx.modalbox = function(){
var text = box_params.apply(this, arguments);
text.type = text.type || "alert";
return boxPopup(text);
};
dhtmlx.modalbox.hide = function(node){
while (node && node.getAttribute && !node.getAttribute("dhxbox"))
node = node.parentNode;
if (node){
node.parentNode.removeChild(node);
modality(false);
}
};
var t = dhtmlx.message = function(text, type, expire, id){
text = params.apply(this, arguments);
text.type = text.type||"info";
var subtype = text.type.split("-")[0];
switch (subtype){
case "alert":
return alertPopup(text);
case "confirm":
return confirmPopup(text);
case "modalbox":
return boxPopup(text);
default:
return info(text);
}
};
t.seed = (new Date()).valueOf();
t.uid = function(){return t.seed++;};
t.expire = 4000;
t.keyboard = true;
t.position = "top";
t.pull = {};
t.timers = {};
t.hideAll = function(){
for (var key in t.pull)
t.hide(key);
};
t.hide = function(id){
var obj = t.pull[id];
if (obj && obj.parentNode){
window.setTimeout(function(){
obj.parentNode.removeChild(obj);
obj = null;
},2000);
obj.className+=" hidden";
if(t.timers[id])
window.clearTimeout(t.timers[id]);
delete t.pull[id];
}
};
})();
gantt = {
version:"2.1.1"
};
/*jsl:ignore*/
//import from dhtmlxcommon.js
function dhtmlxDetachEvent(el, event, handler){
if (el.removeEventListener)
el.removeEventListener(event, handler, false);
else if (el.detachEvent)
el.detachEvent("on"+event, handler);
}
/** Overrides event functionality.
* Includes all default methods from dhtmlx.common but adds _silentStart, _silendEnd
* @desc:
* @type: private
*/
dhtmlxEventable=function(obj){
obj._silent_mode = false;
obj._silentStart = function() {
this._silent_mode = true;
};
obj._silentEnd = function() {
this._silent_mode = false;
};
obj.attachEvent=function(name, catcher, callObj){
name='ev_'+name.toLowerCase();
if (!this[name])
this[name]=new this._eventCatcher(callObj||this);
return(name+':'+this[name].addEvent(catcher)); //return ID (event name & event ID)
};
obj.callEvent=function(name, arg0){
if (this._silent_mode) return true;
name='ev_'+name.toLowerCase();
if (this[name])
return this[name].apply(this, arg0);
return true;
};
obj.checkEvent=function(name){
return (!!this['ev_'+name.toLowerCase()]);
};
obj._eventCatcher=function(obj){
var dhx_catch = [];
var z = function(){
var res = true;
for (var i = 0; i < dhx_catch.length; i++){
if (dhx_catch[i]){
var zr = dhx_catch[i].apply(obj, arguments);
res=res&&zr;
}
}
return res;
};
z.addEvent=function(ev){
if (typeof (ev) != "function")
ev=eval(ev);
if (ev)
return dhx_catch.push(ev)-1;
return false;
};
z.removeEvent=function(id){
dhx_catch[id]=null;
};
return z;
};
obj.detachEvent=function(id){
if (id){
var list = id.split(':'); //get EventName and ID
this[list[0]].removeEvent(list[1]); //remove event
}
};
obj.detachAllEvents = function(){
for (var name in this){
if (name.indexOf("ev_") === 0)
delete this[name];
}
};
obj = null;
};
/*jsl:end*/
dhtmlx.copy = function(object) {
var i, t, result; // iterator, types array, result
if (object && typeof object == "object") {
result = {};
t = [Array,Date,Number,String,Boolean];
for (i=0; i<t.length; i++) {
if (object instanceof t[i])
result = i ? new t[i](object) : new t[i](); // first one is array
}
for (i in object) {
if (Object.prototype.hasOwnProperty.apply(object, [i]))
result[i] = dhtmlx.copy(object[i]);
}
}
return result || object;
};
dhtmlx.mixin = function(target, source, force){
for (var f in source)
if ((!target[f] || force)) target[f]=source[f];
return target;
};
dhtmlx.defined = function(obj) {
return typeof(obj) != "undefined";
};
dhtmlx.uid = function() {
if (!this._seed)
this._seed = (new Date()).valueOf();
this._seed++;
return this._seed;
};
//creates function with specified "this" pointer
dhtmlx.bind=function(functor, object){
return function(){ return functor.apply(object,arguments); };
};
//returns position of html element on the page
gantt._get_position = function(elem) {
var top=0, left=0;
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;
top = box.top + scrollTop - clientTop;
left = box.left + scrollLeft - clientLeft;
return { y: Math.round(top), x: Math.round(left), width:elem.offsetWidth, height:elem.offsetHeight };
} else { //fallback to naive approach
while(elem) {
top = top + parseInt(elem.offsetTop,10);
left = left + parseInt(elem.offsetLeft,10);
elem = elem.offsetParent;
}
return { y: top, x: left, width:elem.offsetWidth, height: elem.offsetHeight};
}
};
gantt._detectScrollSize = function(){
var div = document.createElement("div");
div.style.cssText="visibility:hidden;position:absolute;left:-1000px;width:100px;padding:0px;margin:0px;height:110px;min-height:100px;overflow-y:scroll;";
document.body.appendChild(div);
var width = div.offsetWidth-div.clientWidth;
document.body.removeChild(div);
return width;
};
dhtmlxEventable(gantt);
gantt._click = {};
gantt._dbl_click = {};
gantt._context_menu = {};
gantt._on_click = function(e) {
e = e || window.event;
var trg = e.target || e.srcElement;
var id = gantt.locate(e);
if (id !== null){
var res = !gantt.checkEvent("onTaskClick") || gantt.callEvent("onTaskClick", [id, e]);
if(res && gantt.config.select_task){
gantt.selectTask(id);
}
}else{
gantt.callEvent("onEmptyClick", [e]);
}
gantt._find_ev_handler(e, trg, gantt._click, id);
};
gantt._on_contextmenu = function(e){
e = e || window.event;
var src = e.target||e.srcElement,
taskId = gantt.locate(src),
linkId = gantt.locate(src, gantt.config.link_attribute);
var res = !gantt.checkEvent("onContextMenu") || gantt.callEvent("onContextMenu", [taskId, linkId, e]);
if(!res)
e.preventDefault();
return res;
};
gantt._find_ev_handler = function(e, trg, hash, id){
var res = true;
while (trg && trg.parentNode){
var css = trg.className;
if (css) {
css = css.split(" ");
for (var i = 0; i < css.length; i++) {
if (!css[i]) continue;
if (hash[css[i]]){
res = hash[css[i]].call(gantt, e, id, trg);
res = !(typeof res!="undefined"&&res!==true);
}
}
}
trg=trg.parentNode;
}
return res;
};
gantt._on_dblclick = function(e) {
e = e || window.event;
var trg = e.target || e.srcElement;
var id = gantt.locate(e);
var default_action = gantt._find_ev_handler(e, trg, gantt._dbl_click, id);
if(!default_action)
return;
if (id !== null){
var res = !gantt.checkEvent("onTaskDblClick") || gantt.callEvent("onTaskDblClick", [id, e]);
if(res && gantt.config.details_on_dblclick){
gantt.showLightbox(id);
}
}
};
gantt._on_mousemove = function(e){
if (gantt.checkEvent("onMouseMove")){
var id = gantt.locate(e);
gantt._last_move_event = e;
gantt.callEvent("onMouseMove", [id,e]);
}
};
function dhtmlxDnD(obj, config) {
if(config){
this._settings = config;
}
dhtmlxEventable(this);
dhtmlxEvent(obj, "mousedown", dhtmlx.bind(function(e) {
this.dragStart(obj, e);
}, this));
}
dhtmlxDnD.prototype = {
dragStart: function(obj, e) {
this.config = {
obj: obj,
marker: null,
started: false,
pos: this.getPosition(e),
sensitivity: 4
};
if(this._settings)
dhtmlx.mixin(this.config, this._settings, true);
var mousemove = dhtmlx.bind(function(e) { return this.dragMove(obj, e); }, this);
var scroll = dhtmlx.bind(function(e) { return this.dragScroll(obj, e); }, this);
var limited_mousemove = dhtmlx.bind(function(e) {
if(dhtmlx.defined(this.config.updates_per_second)){
if(!gantt._checkTimeout(this, this.config.updates_per_second))
return true;
}
return mousemove(e);
}, this);
var mouseup = dhtmlx.bind(function(e) {
dhtmlxDetachEvent(document.body, "mousemove", limited_mousemove);
dhtmlxDetachEvent(document.body, "mouseup", mouseup);
return this.dragEnd(obj);
}, this);
dhtmlxEvent(document.body, "mousemove", limited_mousemove);
dhtmlxEvent(document.body, "mouseup", mouseup);
document.body.className += " gantt_noselect";
},
dragMove: function(obj, e) {
if (!this.config.marker && !this.config.started) {
var pos = this.getPosition(e);
var diff_x = pos.x - this.config.pos.x;
var diff_y = pos.y - this.config.pos.y;
var distance = Math.sqrt(Math.pow(Math.abs(diff_x), 2) + Math.pow(Math.abs(diff_y), 2));
if (distance > this.config.sensitivity) {
// real drag starts here,
// when user moves mouse at first time after onmousedown
this.config.started = true;
this.config.ignore = false;
if (this.callEvent("onBeforeDragStart", [obj,e]) === false) {
this.config.ignore = true;
return true;
}
// initialize dnd marker
var marker = this.config.marker = document.createElement("div");
marker.className = "gantt_drag_marker";
marker.innerHTML = "Dragging object";
document.body.appendChild(marker);
this.callEvent("onAfterDragStart", [obj,e]);
} else
this.config.ignore = true;
}
if (!this.config.ignore) {
e.pos = this.getPosition(e);
this.config.marker.style.left = e.pos.x + "px";
this.config.marker.style.top = e.pos.y + "px";
this.callEvent("onDragMove", [obj,e]);
}
},
dragEnd: function(obj) {
if (this.config.marker) {
this.config.marker.parentNode.removeChild(this.config.marker);
this.config.marker = null;
this.callEvent("onDragEnd", []);
}
document.body.className = document.body.className.replace(" gantt_noselect", "");
},
getPosition: function(e) {
var x = 0, y = 0;
e = e || window.event;
if (e.pageX || e.pageY) {
x = e.pageX;
y = e.pageY;
} else if (e.clientX || e.clientY) {
x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
return { x:x, y:y };
}
};
gantt._init_grid = function() {
this._click.gantt_close = dhtmlx.bind(function(e, id, trg) {
this.close(id);
}, this);
this._click.gantt_open = dhtmlx.bind(function(e, id, trg) {
this.open(id);
}, this);
this._click.gantt_row = dhtmlx.bind(function(e, id, trg) {
if (id!==null) {
var el = this.getTaskNode(id);
var left = Math.max(el.offsetLeft - this.config.task_scroll_offset, 0);
this.scrollTo(left);
this.callEvent("onTaskRowClick", [id, trg]);
}
}, this);
this._click.gantt_grid_head_cell = dhtmlx.bind(function(e, id, trg) {
var column = trg.getAttribute("column_id");
if(!this.callEvent("onGridHeaderClick", [column, e]))
return;
if (column == "add") {
this._click.gantt_add(e, this.config.root_id);
} else if (this.config.sort){
var sort = (this._sort && this._sort.direction && this._sort.name == column) ? this._sort.direction : "desc";
// invert sort direction
sort = (sort == "desc") ? "asc" : "desc";
this._sort = {
name: column,
direction: sort
};
this._render_grid_header();
this.sort(column, sort == "desc");
}
}, this);
if(!this.config.sort && this.config.order_branch) {
this._init_dnd();
}
this._click.gantt_add = dhtmlx.bind(function(e, id, trg) {
if(this.config.readonly) return;
var parent = id ? this.getTask(id) : false,
startDate = '';
if(parent){
startDate = parent.start_date;
}else{
var first = this._order[0];
startDate = first ? this.getTask(first).start_date : this.getState().min_date;
}
if(parent)
parent.$open = true;
var item = { text:gantt.locale.labels.new_task, start_date:this.templates.xml_format(startDate), duration: 1, progress: 0, parent: id };
item.id = dhtmlx.uid();
this.callEvent("onTaskCreated", [item]);
if (this.config.details_on_create){
item.$new = true;
this._pull[item.id] = this._init_task(item);
this._add_branch(item);
item.$level = this._item_level(item);
this.selectTask(item.id);
this.refreshData();
this.showLightbox(item.id);
}else{
this.addTask(item);
this.showTask(item.id);
this.selectTask(item.id);
}
}, this);
};
gantt._render_grid = function(){
if(this._is_grid_visible()){
this._calc_grid_width();
this._render_grid_header();
}
};
gantt._calc_grid_width = function() {
if (this.config.autofit) {
var columns = this.config.columns;
var cols_width = 0;
var unknown = [];
var width = [];
for (var i = 0; i < columns.length; i++) {
var v = parseInt(columns[i].width, 10);
if (window.isNaN(v)) {
v = 50;
unknown.push(i);
}
width[i] = v;
cols_width += v;
}
var diff = this._get_grid_width() - cols_width;
// TODO: logic may be improved for proportional changing of width
var step = diff/(unknown.length > 0 ? unknown.length : (width.length > 0 ? width.length : 1));
if (unknown.length > 0) {
// there are several columns with undefined width
var delta = diff/(unknown.length ? unknown.length : 1);
for (var i = 0; i < unknown.length; i++) {
var index = unknown[i];
width[index] += delta;
}
} else {
// delta must be added for all columns
var delta = diff/(width.length ? width.length : 1);
for (var i = 0; i < width.length; i++)
width[i] += delta;
}
for (var i = 0; i < width.length; i++) {
columns[i].width = width[i];
}
}
};
gantt._render_grid_header = function() {
var columns = this.config.columns;
var cells = [];
var width = 0,
labels = this.locale.labels;
var lineHeigth = this.config.scale_height-2;
for (var i = 0; i < columns.length; i++) {
var last = i == columns.length-1;
var col = columns[i];
if (last && this._get_grid_width() > width+col.width)
col.width = this._get_grid_width() - width;
width += col.width;
var sort = (this._sort && col.name == this._sort.name) ? ("<div class='gantt_sort gantt_" + this._sort.direction + "'></div>") : "";
var cssClass = ["gantt_grid_head_cell",
("gantt_grid_head_" + col.name),
(last ? "gantt_last_cell" : ""),
this.templates.grid_header_class(col.name, col)].join(" ");
var style = "width:" + (col.width-(last?1:0)) + "px;";
var label = (col.label || labels["column_" + col.name]);
label = label || "";
var cell = "<div class='" + cssClass + "' style='" + style + "' column_id='" + col.name + "'>" + label + sort + "</div>";
cells.push(cell);
}
this.$grid_scale.style.height = (this.config.scale_height-1) + "px";
this.$grid_scale.style.lineHeight = lineHeigth + "px";
this.$grid_scale.style.width = (width-1) + "px";
this.$grid_scale.innerHTML = cells.join("");
};
gantt._render_grid_item = function(item) {
if(!gantt._is_grid_visible())
return null;
var columns = this.config.columns;
var cells = [];
var width = 0;
for (var i = 0; i < columns.length; i++) {
var last = i == columns.length-1;
var col = columns[i];
var cell;
var value;
if (col.name == "add" && i == columns.length-1) {
value = "<div class='gantt_add'></div>";
} else {
if (col.template)
value = col.template(item);
else
value = item[col.name];
if (value instanceof Date)
value = this.templates.date_grid(value);
value = "<div class='gantt_tree_content'>" + value + "</div>";
}
var css = "gantt_cell" + (last ? " gantt_last_cell" : "");
var tree = "";
if (col.tree) {
for (var j = 0; j < item.$level; j++)
tree += this.templates.grid_indent(item);
var has_child = (this._branches[item.id] && this._branches[item.id].length > 0);
if (has_child) {
tree += this.templates.grid_open(item);
tree += this.templates.grid_folder(item);
} else {
tree += this.templates.grid_blank(item);
tree += this.templates.grid_file(item);
}
}
var style = "width:" + (col.width-(last ? 1 : 0)) + "px;";
if (dhtmlx.defined(col.align))
style += "text-align:" + col.align + ";";
cell = "<div class='" + css + "' style='" + style + "'>" + tree + value + "</div>";
cells.push(cell);
}
var css = item.$index%2 === 0 ? "" : " odd";
css += (item.$transparent) ? " gantt_transparent" : "";
if (this.templates.grid_row_class) {
var css_template = this.templates.grid_row_class.call(this, item.start_date, item.end_date, item);
if (css_template)
css += " " + css_template;
}
if(this.getState().selected_task == item.id){
css += " gantt_selected";
}
var el = document.createElement("div");
el.className = "gantt_row" + css;
el.style.height = this.config.row_height + "px";
el.style.lineHeight = (gantt.config.row_height)+"px";
el.setAttribute(this.config.task_attribute, item.id);
el.innerHTML = cells.join("");
return el;
};
gantt.open = function(id){
gantt._set_item_state(id, true);
this.callEvent("onTaskOpened", [id]);
};
gantt.close = function(id){
gantt._set_item_state(id, false);
this.callEvent("onTaskClosed", [id]);
};
gantt._set_item_state = function(id, state) {
if (id && this._pull[id]) {
this._pull[id].$open = state;
this.refreshData();
}
};
gantt._is_grid_visible = function(){
return (this.config.grid_width && this.config.show_grid);
};
gantt._get_grid_width = function(){
if(this._is_grid_visible()){
if(this._is_chart_visible()){
return this.config.grid_width;
}else{
return this._x;
}
}else{
return 0;
}
};
gantt.getTaskIndex = function(id){
var branch = this._branches[this.getTask(id).parent];
for (var i = 0; i < branch.length; i++)
if (branch[i] == id)
return i;
return -1;
};
gantt.getGlobalTaskIndex = function(id){
var branch = this._order;
for (var i = 0; i < branch.length; i++)
if (branch[i] == id)
return i;
return -1;
};
gantt.moveTask = function(sid, tindex, parent){
//target id as 4th parameter
var id = arguments[3];
if (id){
if (id === sid) return;
parent = this.getTask(id).parent;
tindex = this.getTaskIndex(id);
}
parent = parent || this.config.root_id;
var source = this.getTask(sid);
var sbranch = this._branches[source.parent];
var tbranch = this._branches[parent];
if (tindex == -1)
tindex = tbranch.length + 1;
if (source.parent == parent){
var sindex = this.getTaskIndex(sid);
if (sindex == tindex) return;
if (sindex < tindex)
tindex--;
}
this._replace_branch_child(source.parent, sid);
tbranch = this._branches[parent];
var tid = tbranch[tindex];
if (!tid) //adding as last element
tbranch.push(sid);
else
tbranch = tbranch.slice(0, tindex).concat([ sid ]).concat(tbranch.slice(tindex));
source.parent = parent;
this._branches[parent] = tbranch;
this.refreshData();
};
gantt._init_dnd = function() {
var dnd = new dhtmlxDnD(this.$grid_data, {updates_per_second : 60});
if (dhtmlx.defined(this.config.dnd_sensitivity))
dnd.config.sensitivity = this.config.dnd_sensitivity;
dnd.attachEvent("onBeforeDragStart", dhtmlx.bind(function(obj,e) {
var el = this._locateHTML(e);
if (!el) return false;
if (this.hideQuickInfo) this._hideQuickInfo();
var id = this.locate(e);
if(!this.callEvent("onRowDragStart", [id, e.target || e.srcElement, e])){
return false;
}
}, this));
dnd.attachEvent("onAfterDragStart", dhtmlx.bind(function(obj,e) {
var el = this._locateHTML(e);
dnd.config.marker.innerHTML = el.outerHTML;
dnd.config.id = this.locate(e);
var task = this.getTask(dnd.config.id);
task.$open = false;
task.$transparent = true;
this.refreshData();
}, this));
dnd.lastTaskOfLevel = function(level){
var ids = gantt._order,
pull = gantt._pull,
last_item = null;
for(var i= 0, len = ids.length; i < len; i++){
if(pull[ids[i]].$level == level){
last_item = pull[ids[i]];
}
}
return last_item ? last_item.id : null;
};
dnd.attachEvent("onDragMove", dhtmlx.bind(function(obj,e) {
var dd = dnd.config;
var pos = this._get_position(this.$grid_data);
// row offset
var x = pos.x + 10;
var y = e.pos.y - 10;
// prevent moving row out of grid_data container
if (y < pos.y) y = pos.y;
if (y > pos.y + this.$grid_data.offsetHeight - this.config.row_height) y = pos.y + this.$grid_data.offsetHeight - this.config.row_height;
// setting position of row
dd.marker.style.left = x + "px";
dd.marker.style.top = y + "px";
// highlight row when mouseover
var target = document.elementFromPoint(pos.x-document.body.scrollLeft+1, y-document.body.scrollTop);
var el = this.locate(target);
var item = this.getTask(dnd.config.id);
if(!this.isTaskExists(el)){
el = dnd.lastTaskOfLevel(item.$level);
if(el == dnd.config.id){
el = null;
}
}
if (this.isTaskExists(el)) {
var box = gantt._get_position(target);
var over = this.getTask(el);
if (box.y + target.offsetHeight/2 < y){
//hovering over bottom part of item, check can be drop to bottom
var index = this.getGlobalTaskIndex(over.id);
var next = this._pull[this._order[index+1+(over.id == item.id ? 1 : 0)]]; //adds +1 when hovering over placeholder
if (next){
if (next.id != item.id)
over = next; //there is a valid target
else
return;
} else {
//we at end of the list, check and drop at the end of list
next = this._pull[this._order[index]];
if (next.$level == item.$level){
this.moveTask(item.id, -1, next.parent);
dd.target = "next:"+next.id;
return;
}
}
}
//replacing item under cursor
if (over.$level == item.$level && item.id != over.id){
this.moveTask(item.id, 0, 0, over.id);
dd.target = over.id;
} else {
//if item is on different level, check the one before it
if (item.id == over.id) return;
var index = this.getGlobalTaskIndex(over.id);
var prev = this._pull[this._order[index-1]];
if (prev && prev.$level == item.$level && item.id != prev.id){
this.moveTask(item.id, -1, prev.parent);
dd.target = "next:"+prev.id;
}
}
}
return true;
}, this));
dnd.attachEvent("onDragEnd", dhtmlx.bind(function(){
this.getTask(dnd.config.id).$transparent = false;
this.refreshData();
this.callEvent("onRowDragEnd", [dnd.config.id, dnd.config.target]);
}, this));
};
gantt._scale_helpers = {
getSum : function(sizes, from, to){
if(to === undefined)
to = sizes.length - 1;
if(from === undefined)
from = 0;
var summ = 0;
for(var i=from; i <= to; i++)
summ += sizes[i];
return summ;
},
setSumWidth : function(sum_width, scale, from, to){
var parts = scale.width;
if(to === undefined)
to = parts.length - 1;
if(from === undefined)
from = 0;
var length = to - from + 1;
if(from > parts.length - 1 || length <= 0 || to > parts.length - 1)
return;
var oldWidth = this.getSum(parts, from, to);
var diff = sum_width - oldWidth;
this.adjustSize(diff, parts, from, to);
this.adjustSize(- diff, parts, to + 1);
scale.full_width = this.getSum(parts);
},
splitSize : function(width, count){
var arr = [];
for(var i=0; i < count; i++) arr[i] = 0;
this.adjustSize(width, arr);
return arr;
},
adjustSize : function(width, parts, from, to){
if(!from)
from = 0;
if(to === undefined)
to = parts.length - 1;
var length = to - from + 1;
var full = this.getSum(parts, from, to);
var shared = 0;
for(var i = from; i <= to; i++){
var share = Math.floor(width*(full ? (parts[i]/full) : (1/length)));
full -= parts[i];
width -= share;
length--;
parts[i] += share;
shared += share;
}
parts[parts.length - 1] += width;
//parts[parts.length - 1] += width - shared;
},
sortScales : function(scales){
function cellSize(unit, step){
var d = new Date(1970, 0, 1);
return gantt.date.add(d, step, unit) - d;
}
scales.sort(function(a, b){
return cellSize(a.unit, a.step) < cellSize(b.unit, b.step) ? 1 : -1;
});
},
primaryScale : function(){
gantt._init_template("date_scale");
return {
unit: gantt.config.scale_unit,
step: gantt.config.step,
template : gantt.templates.date_scale,
date : gantt.config.date_scale,
css: gantt.templates.scale_cell_class
};
},
prepareConfigs : function(scales, min_coll_width, container_width, scale_height){
var heights = this.splitSize(scale_height, scales.length);
var full_width = container_width;
var configs = [];
for(var i=scales.length-1; i >= 0; i--){
var main_scale = (i == scales.length - 1);
var cfg = this.initScaleConfig(scales[i]);
if(main_scale){
this.processIgnores(cfg);
}
this.initColSizes(cfg, min_coll_width, full_width, heights[i]);
this.limitVisibleRange(cfg);
if(main_scale){
full_width = cfg.full_width;
}
configs.unshift(cfg);
}
for( var i =0; i < configs.length-1; i++){
this.alineScaleColumns(configs[configs.length-1], configs[i]);
}
return configs;
},
_ignore_time_config : function(date){
if(this.config.skip_off_time){
return !this.isWorkTime(date);
}
return false;
},
processIgnores : function(config){
var display_count = config.count;
config.ignore_x = {};
if(gantt.ignore_time || gantt.config.skip_off_time){
var ignore = gantt.ignore_time || function(){return false;};
display_count = 0;
for(var i=0; i < config.trace_x.length; i++){
if(ignore.call(gantt, config.trace_x[i]) || this._ignore_time_config.call(gantt,config.trace_x[i])){
config.ignore_x[config.trace_x[i].valueOf()] = true;
config.ignored_colls = true;
}else{
display_count++;
}
}
}
config.display_count = display_count;
},
initColSizes : function(config, min_col_width, full_width, line_height){
var cont_width = full_width;
config.height = line_height;
var column_count = config.display_count === undefined ? config.count : config.display_count;
if(!column_count)
column_count = 1;
config.col_width = Math.floor(cont_width/column_count);
if(min_col_width){
if (config.col_width < min_col_width){
config.col_width = min_col_width;
cont_width = config.col_width * column_count;
}
}
config.width = [];
var ignores = config.ignore_x || {};
for(var i =0; i < config.trace_x.length; i++){
if(ignores[config.trace_x[i].valueOf()] || (config.display_count == config.count)){
config.width[i] = 0;
}else{
config.width[i] = 1;
}
}
this.adjustSize(cont_width - this.getSum(config.width)/* 1 width per column from the code above */, config.width);
config.full_width = this.getSum(config.width);
},
initScaleConfig : function(config){
var cfg = dhtmlx.mixin({
count:0,
col_width:0,
full_width:0,
height:0,
width:[],
trace_x:[]
}, config);
this.eachColumn(config.unit, config.step, function(date){
cfg.count++;
cfg.trace_x.push(new Date(date));
});
return cfg;
},
iterateScales : function(lower_scale, upper_scale, from, to, callback){
var upper_dates = upper_scale.trace_x;
var lower_dates = lower_scale.trace_x;
var prev = from || 0;
var end = to || (lower_dates.length - 1);
var prevUpper = 0;
for(var up=1; up < upper_dates.length; up++){
for(var next=prev; next <= end; next++){
if(+lower_dates[next] == +upper_dates[up]){
if(callback){
callback.apply(this, [prevUpper, up, prev, next]);
}
prev = next;
prevUpper = up;
continue;
}
}
}
},
alineScaleColumns : function(lower_scale, upper_scale, from, to){
this.iterateScales(lower_scale, upper_scale, from, to, function(upper_start, upper_end, lower_start, lower_end){
var targetWidth = this.getSum(lower_scale.width, lower_start, lower_end - 1);
var actualWidth = this.getSum(upper_scale.width, upper_start, upper_end - 1);
if(actualWidth != targetWidth){
this.setSumWidth(targetWidth, upper_scale, upper_start, upper_end - 1);
}
});
},
eachColumn : function(unit, step, callback){
var start = new Date(gantt._min_date),
end = new Date(gantt._max_date);
if(gantt.date[unit + "_start"]){
start = gantt.date[unit + "_start"](start);
}
var curr = new Date(start);
while(+curr < +end){
callback.call(this, new Date(curr));
curr = gantt.date.add(curr, step, unit);
}
},
limitVisibleRange : function(cfg){
var dates = cfg.trace_x;
var left = 0, right = cfg.width.length-1;
var diff = 0;
if(+dates[0] < +gantt._min_date && left != right){
var width = Math.floor(cfg.width[0] * ((dates[1] - gantt._min_date)/ (dates[1] - dates[0])));
diff += cfg.width[0] - width;
cfg.width[0] = width;
dates[0] = new Date(gantt._min_date);
}
var last = dates.length - 1;
var lastDate = dates[last];
var outDate = gantt.date.add(lastDate, cfg.step, cfg.unit);
if(+outDate > +gantt._max_date && last > 0){
var width = cfg.width[last] - Math.floor(cfg.width[last] * ((outDate - gantt._max_date)/(outDate - lastDate)));
diff += cfg.width[last] - width;
cfg.width[last] = width;
}
if(diff){
var full = this.getSum(cfg.width);
var shared = 0;
for(var i =0; i < cfg.width.length; i++){
var share = Math.floor(diff*(cfg.width[i]/full));
cfg.width[i] += share;
shared += share;
}
this.adjustSize(diff - shared, cfg.width);
}
}
};
gantt._tasks_dnd = {
drag : null,
_events:{
before_start:{},
before_finish:{},
after_finish:{}
},
_handlers:{},
init:function(){
this.clear_drag_state();
var drag = gantt.config.drag_mode;
this.set_actions();
var evs = {
"before_start":"onBeforeTaskDrag",
"before_finish":"onBeforeTaskChanged",
"after_finish":"onAfterTaskDrag"
};
//for now, all drag operations will trigger the same events
for(var stage in this._events){
for(var mode in drag){
this._events[stage][mode] = evs[stage];
}
}
this._handlers[drag.move] = this._move;
this._handlers[drag.resize] = this._resize;
this._handlers[drag.progress] = this._resize_progress;
},
set_actions:function(){
var data = gantt.$task_data;
dhtmlxEvent(data, "mousemove", dhtmlx.bind(function(e){
this.on_mouse_move(e||event);
}, this));
dhtmlxEvent(data, "mousedown", dhtmlx.bind(function(e){
this.on_mouse_down(e||event);
}, this));
dhtmlxEvent(data, "mouseup", dhtmlx.bind(function(e){
this.on_mouse_up(e||event);
}, this));
},
clear_drag_state : function(){
this.drag = {
id:null,
mode:null,
pos:null,
start_x:null,
start_y:null,
obj:null,
left:null
};
},
_resize : function(ev, shift, drag){
var cfg = gantt.config;
var coords_x = this._drag_task_coords(ev, drag);
if(drag.left){
ev.start_date = gantt._date_from_pos(coords_x.start + shift);
if(!ev.start_date){
ev.start_date = new Date(gantt.getState().min_date);
}
}else{
ev.end_date =gantt._date_from_pos(coords_x.end + shift);
if(!ev.end_date){
ev.end_date = new Date(gantt.getState().max_date);
}
}
if (ev.end_date - ev.start_date < cfg.min_duration){
if(drag.left)
ev.start_date = gantt.calculateEndDate(ev.end_date, -1);
else
ev.end_date = gantt.calculateEndDate(ev.start_date, 1);
}
gantt._init_task_timing(ev);
},
_resize_progress:function(ev, shift, drag){
var coords_x = this._drag_task_coords(ev, drag);
var diff = Math.max(0, drag.pos.x - coords_x.start);
ev.progress = Math.min(1, diff / (coords_x.end-coords_x.start));
},
_move : function(ev, shift, drag){
var coords_x = this._drag_task_coords(ev, drag);
var new_start = gantt._date_from_pos(coords_x.start + shift),
new_end = gantt._date_from_pos(coords_x.end + shift);
if(!new_start){
ev.start_date = new Date(gantt.getState().min_date);
ev.end_date = gantt._date_from_pos(gantt.posFromDate(ev.start_date) + (coords_x.end - coords_x.start));
}else if(!new_end){
ev.end_date = new Date(gantt.getState().max_date);
ev.start_date = gantt._date_from_pos(gantt.posFromDate(ev.end_date) - (coords_x.end - coords_x.start));
}else{
ev.start_date = new_start;
ev.end_date = new_end;
}
},
_drag_task_coords : function(t, drag){
var start = drag.obj_s_x = drag.obj_s_x || gantt.posFromDate(t.start_date);
var end = drag.obj_e_x = drag.obj_e_x || gantt.posFromDate(t.end_date);
return {
start : start,
end : end
};
},
on_mouse_move : function(e){
if(this.drag.start_drag)
this._start_dnd(e);
var drag = this.drag;
if (drag.mode){
if(!gantt._checkTimeout(this, 40))//limit update frequency
return;
this._update_on_move(e);
}
},
_update_on_move : function(e){
var drag = this.drag;
if (drag.mode){
var pos = gantt._get_mouse_pos(e);
if(drag.pos && drag.pos.x == pos.x)
return;
drag.pos=pos;
var curr_date = gantt._date_from_pos(pos.x);
if(!curr_date || isNaN( curr_date.getTime() ))
return;
var shift = pos.x - drag.start_x;
var ev = gantt.getTask(drag.id);
if(this._handlers[drag.mode]){
var original = dhtmlx.mixin({}, ev);
var copy = dhtmlx.mixin({}, ev);
this._handlers[drag.mode].apply(this, [copy, shift, drag]);
dhtmlx.mixin(ev, copy, true);
gantt._update_parents(drag.id, true);
gantt.callEvent("onTaskDrag", [ev.id, drag.mode, copy, original, e]);
dhtmlx.mixin(ev, copy, true);
gantt._update_parents(drag.id);
gantt.refreshTask(drag.id);
}
}
},
on_mouse_down : function(e, src){
// on Mac we do not get onmouseup event when clicking right mouse button leaving us in dnd state
// let's ignore right mouse button then
if (e.button == 2)
return;
if (gantt.config.readonly || this.drag.mode) return;
this.clear_drag_state();
src = src||(e.target||e.srcElement);
var className = gantt._trim(src.className || "");
if(!className || !this._get_drag_mode(className)){
if(src.parentNode)
return this.on_mouse_down(e, src.parentNode);
else
return;
}
var drag = this._get_drag_mode(className);
if(!drag){
if (gantt.checkEvent("onMouseDown") && gantt.callEvent("onMouseDown", [className.split(" ")[0]])) {
if (src.parentNode)
return this.on_mouse_down(e,src.parentNode);
}
}else{
if (drag.mode && drag.mode != gantt.config.drag_mode.ignore && gantt.config["drag_" + drag.mode]){
var id = gantt.locate(src),
task = dhtmlx.copy(gantt.getTask(id) || {});
if(gantt._is_flex_task(task) && drag.mode != gantt.config.drag_mode.progress){//only progress drag is allowed for tasks with flexible duration
this.clear_drag_state();
return;
}
drag.id = id;
var pos = gantt._get_mouse_pos(e);
drag.start_x = pos.x;
drag.start_y = pos.y;
drag.obj = task;
this.drag.start_drag = drag;
}else
this.clear_drag_state();
}
},
_fix_dnd_scale_time:function(task, drag){
var unit = gantt._tasks.unit,
step = gantt._tasks.step;
if(!gantt.config.round_dnd_dates){
unit = 'minute';
step = gantt.config.time_step;
}
if(drag.mode == gantt.config.drag_mode.resize){
if(drag.left){
task.start_date = gantt._get_closest_date({date:task.start_date, unit:unit, step:step});
}else{
task.end_date = gantt._get_closest_date({date:task.end_date, unit:unit, step:step});
}
}else if(drag.mode == gantt.config.drag_mode.move){
task.start_date = gantt._get_closest_date({date:task.start_date, unit:unit, step:step});
task.end_date = gantt.calculateEndDate(task.start_date, task.duration, gantt.config.duration_unit);
}
},
_fix_working_times:function(task, drag){
if(gantt.config.work_time && gantt.config.correct_work_time){
if(drag.mode == gantt.config.drag_mode.resize){
if(drag.left){
task.start_date = gantt.getClosestWorkTime({date:task.start_date, dir:'future'});
}else{
task.end_date = gantt.getClosestWorkTime({date:task.end_date, dir:'past'});
}
}else if(drag.mode == gantt.config.drag_mode.move){
if(!gantt.isWorkTime(task.start_date)){
task.start_date = gantt.getClosestWorkTime({date:task.start_date, dir:'future'});
task.end_date = gantt.calculateEndDate(task.start_date, task.duration);
}else if(!gantt.isWorkTime(new Date(+task.end_date - 1))){
task.end_date = gantt.getClosestWorkTime({date:task.end_date, dir:'past'});
task.start_date = gantt.calculateEndDate(task.end_date, task.duration*-1);
}
}
}
},
on_mouse_up : function(e){
var drag = this.drag;
if (drag.mode && drag.id){
//drop
var ev=gantt.getTask(drag.id);
if(gantt.config.work_time && gantt.config.correct_work_time){
this._fix_working_times(ev, drag);
}
this._fix_dnd_scale_time(ev, drag);
gantt._init_task_timing(ev);
if(!this._fireEvent("before_finish", drag.mode, [drag.id, drag.mode, dhtmlx.copy(drag.obj), e])){
drag.obj._dhx_changed = false;
dhtmlx.mixin(ev, drag.obj, true);
gantt.updateTask(ev.id);
} else {
var drag_id = drag.id;
gantt._init_task_timing(ev);
gantt.updateTask(ev.id);
this._fireEvent("after_finish", drag.mode, [drag_id, drag.mode, e]);
this.clear_drag_state();
}
}
this.clear_drag_state();
},
_get_drag_mode : function(className){
var modes = gantt.config.drag_mode;
var classes = (className || "").split(" ");
var classname = classes[0];
var drag = {mode:null, left:null};
switch (classname) {
case "gantt_task_line":
case "gantt_task_content":
drag.mode = modes.move;
break;
case "gantt_task_drag":
drag.mode = modes.resize;
if(classes[1] && classes[1].indexOf("left", classes[1].length - "left".length) !== -1){
drag.left = true;
}else{
drag.left = false;
}
break;
case "gantt_task_progress_drag":
drag.mode = modes.progress;
break;
case "gantt_link_control":
case "gantt_link_point":
drag.mode = modes.ignore;
break;
default:
drag = null;
break;
}
return drag;
},
_start_dnd : function(e){
var drag = this.drag = this.drag.start_drag;
delete drag.start_drag;
var cfg = gantt.config;
var id = drag.id;
if (!cfg["drag_"+drag.mode] || !gantt.callEvent("onBeforeDrag",[id, drag.mode, e]) || !this._fireEvent("before_start", drag.mode, [id, drag.mode, e])){
this.clear_drag_state();
}else {
delete drag.start_drag;
}
},
_fireEvent:function(stage, mode, params){
dhtmlx.assert(this._events[stage], "Invalid stage:{" + stage + "}");
var trigger = this._events[stage][mode];
dhtmlx.assert(trigger, "Unknown after drop mode:{" + mode + "}");
dhtmlx.assert(params, "Invalid event arguments");
if(!gantt.checkEvent(trigger))
return true;
return gantt.callEvent(trigger, params);
}
};
gantt._render_link = function(id){
var link = this.getLink(id);
gantt._linkRenderer.render_item(link, this.$task_links);
};
gantt._get_link_type = function(from_start, to_start){
var type = null;
if(from_start && to_start){
type = gantt.config.links.start_to_start;
}else if(!from_start && to_start){
type = gantt.config.links.finish_to_start;
}else if(!from_start && !to_start){
type = gantt.config.links.finish_to_finish;
}else if(from_start && !to_start){
type = gantt.config.links.start_to_finish;
}
return type;
};
gantt.isLinkAllowed = function(from, to, from_start, to_start){
var link = null;
if(typeof(from) == "object"){
link = from;
}else{
link = {source:from, target:to, type: this._get_link_type(from_start, to_start)};
}
if(!link) return false;
if(!(link.source && link.target && link.type)) return false;
if(link.source == link.target) return false;
var res = true;
//any custom rules
if(this.checkEvent("onLinkValidation"))
res = this.callEvent("onLinkValidation", [link]);
return res;
};
gantt._render_link_element = function(link){
var dots = this._path_builder.get_points(link);
var drawer = gantt._drawer;
var lines = drawer.get_lines(dots);
var div = document.createElement("div");
var css = "gantt_task_link";
var cssTemplate = this.templates.link_class ? this.templates.link_class(link) : "";
if(cssTemplate){
css += " " + cssTemplate;
}
div.className = css;
div.setAttribute(gantt.config.link_attribute, link.id);
for(var i=0; i < lines.length; i++){
if(i == lines.length - 1){
lines[i].size -= gantt.config.link_arrow_size;
}
div.appendChild(drawer.render_line(lines[i], lines[i+1]));
}
var direction = lines[lines.length - 1].direction;
var endpoint = gantt._render_link_arrow(dots[dots.length - 1], direction);
div.appendChild(endpoint);
return div;
};
gantt._render_link_arrow = function(point, direction){
var div = document.createElement("div");
var drawer = gantt._drawer;
var top = point.y;
var left = point.x;
var size = gantt.config.link_arrow_size;
var line_width = gantt.config.row_height;
var className = "gantt_link_arrow gantt_link_arrow_" + direction;
switch (direction){
case drawer.dirs.right:
top -= (size - line_width)/2;
left -= size;
break;
case drawer.dirs.left:
top -= (size - line_width)/2;
break;
case drawer.dirs.up:
left -= (size - line_width)/2;
break;
case drawer.dirs.down:
top -= size;
left -= (size - line_width)/2;
break;
default:
break;
}
div.style.cssText = [
"top:"+top + "px",
"left:"+left+'px'].join(';');
div.className = className;
return div;
};
gantt._drawer = {
current_pos:null,
dirs:{"left":'left',"right":'right',"up":'up', "down":'down'},
path:[],
clear:function(){
this.current_pos = null;
this.path = [];
},
point:function(pos){
this.current_pos = dhtmlx.copy(pos);
},
get_lines:function(dots){
this.clear();
this.point(dots[0]);
for(var i=1; i<dots.length ; i++){
this.line_to(dots[i]);
}
return this.get_path();
},
line_to:function(pos){
var next = dhtmlx.copy(pos);
var prev = this.current_pos;
var line = this._get_line(prev, next);
this.path.push(line);
this.current_pos = next;
},
get_path:function(){
return this.path;
},
get_wrapper_sizes :function(v){
var res,
wrapper_size = gantt.config.link_wrapper_width,
line_size = gantt.config.link_line_width,
y = v.y + (gantt.config.row_height - wrapper_size)/2;
switch (v.direction){
case this.dirs.left:
res = { top : y,
height : wrapper_size,
lineHeight : wrapper_size,
left : v.x - v.size - wrapper_size/2 ,
width : v.size +wrapper_size};
break;
case this.dirs.right:
res = { top : y,
lineHeight : wrapper_size,
height : wrapper_size,
left : v.x - wrapper_size/2,
width : v.size + wrapper_size};
break;
case this.dirs.up:
res = { top : y - v.size,
lineHeight: v.size + wrapper_size,
height : v.size + wrapper_size,
left : v.x - wrapper_size/2,
width : wrapper_size};
break;
case this.dirs.down:
res = { top : y,
lineHeight: v.size + wrapper_size,
height : v.size + wrapper_size,
left : v.x - wrapper_size/2,
width : wrapper_size};
break;
default:
break;
}
return res;
},
get_line_sizes : function(v){
var res,
line_size = gantt.config.link_line_width,
wrapper_size = gantt.config.link_wrapper_width,
size = v.size + line_size;
switch (v.direction){
case this.dirs.left:
case this.dirs.right:
res = {
height : line_size,
width : size,
marginTop: (wrapper_size - line_size)/2,
marginLeft: (wrapper_size - line_size)/2
};
break;
case this.dirs.up:
case this.dirs.down:
res = {
height : size,
width : line_size,
marginTop: (wrapper_size - line_size)/2,
marginLeft: (wrapper_size - line_size)/2
};
break;
default:
break;
}
return res;
},
render_line : function(v){
var pos = this.get_wrapper_sizes(v);
var wrapper = document.createElement("div");
wrapper.style.cssText = [
"top:" + pos.top + "px",
"left:" + pos.left + "px",
"height:" + pos.height + "px",
"width:" + pos.width + "px"
].join(';');
wrapper.className = "gantt_line_wrapper";
var innerPos = this.get_line_sizes(v);
var inner = document.createElement("div");
inner.style.cssText = [
"height:" + innerPos.height + "px",
"width:" + innerPos.width + "px",
"margin-top:" + innerPos.marginTop + "px",
"margin-left:" + innerPos.marginLeft + "px"
].join(";");
inner.className = "gantt_link_line_" + v.direction;
wrapper.appendChild(inner);
return wrapper;
},
_get_line:function(from, to){
var direction = this.get_direction(from, to);
var vect = {
x : from.x,
y : from.y,
direction : this.get_direction(from, to)
};
if(direction == this.dirs.left || direction == this.dirs.right){
vect.size = Math.abs(from.x - to.x);
}else{
vect.size = Math.abs(from.y - to.y);
}
return vect;
},
get_direction:function(from, to){
var direction = 0;
if(to.x < from.x){
direction = this.dirs.left;
}else if (to.x > from.x){
direction = this.dirs.right;
}else if (to.y > from.y){
direction = this.dirs.down;
}else {
direction = this.dirs.up;
}
return direction;
}
};
gantt._y_from_ind = function(index){
return (index)*gantt.config.row_height;
};
gantt._path_builder = {
path:[],
clear:function(){
this.path = [];
},
current:function(){
return this.path[this.path.length - 1];
},
point:function(next){
if(!next)
return this.current();
this.path.push(dhtmlx.copy(next));
return next;
},
point_to:function(direction, diff, point){
if(!point)
point = dhtmlx.copy(this.point());
else
point = {x:point.x, y:point.y};
var dir = gantt._drawer.dirs;
switch (direction){
case (dir.left):
point.x -= diff;
break;
case (dir.right):
point.x += diff;
break;
case (dir.up):
point.y -= diff;
break;
case (dir.down):
point.y += diff;
break;
default:
break;
}
return this.point(point);
},
get_points:function(link){
var pt = this.get_endpoint(link);
var xy = gantt.config;
var dy = pt.e_y - pt.y;
var dx = pt.e_x - pt.x;
var dir = gantt._drawer.dirs;
this.clear();
this.point({x: pt.x, y : pt.y});
var shiftX = 2*xy.link_arrow_size;//just random size for first line
var forward = (pt.e_x > pt.x);
if(link.type == gantt.config.links.start_to_start){
this.point_to(dir.left, shiftX);
if(forward){
this.point_to(dir.down, dy);
this.point_to(dir.right, dx);
}else{
this.point_to(dir.right, dx);
this.point_to(dir.down, dy);
}
this.point_to(dir.right, shiftX);
}else if(link.type == gantt.config.links.finish_to_start){
forward = (pt.e_x > (pt.x + 2*shiftX));
this.point_to(dir.right, shiftX);
if(forward){
dx -= shiftX;
this.point_to(dir.down, dy);
this.point_to(dir.right, dx);
}else{
dx -= 2*shiftX;
var sign = dy > 0 ? 1 : -1;
this.point_to(dir.down, sign * (xy.row_height/2));
this.point_to(dir.right, dx);
this.point_to(dir.down, sign * ( Math.abs(dy) - (xy.row_height/2)));
this.point_to(dir.right, shiftX);
}
}else if(link.type == gantt.config.links.finish_to_finish){
this.point_to(dir.right, shiftX);
if(forward){
this.point_to(dir.right, dx);
this.point_to(dir.down, dy);
}else{
this.point_to(dir.down, dy);
this.point_to(dir.right, dx);
}
this.point_to(dir.left, shiftX);
}else if(link.type == gantt.config.links.start_to_finish){
forward = (pt.e_x > (pt.x - 2*shiftX));
this.point_to(dir.left, shiftX);
if(!forward){
dx += shiftX;
this.point_to(dir.down, dy);
this.point_to(dir.right, dx);
}else{
dx += 2*shiftX;
var sign = dy > 0 ? 1 : -1;
this.point_to(dir.down, sign * (xy.row_height/2));
this.point_to(dir.right, dx);
this.point_to(dir.down, sign * ( Math.abs(dy) - (xy.row_height/2)));
this.point_to(dir.left, shiftX);
}
}
return this.path;
},
get_endpoint : function(link){
var types = gantt.config.links;
var from_start = false, to_start = false;
if(link.type == types.start_to_start){
from_start = to_start = true;
}else if(link.type == types.finish_to_finish){
from_start = to_start = false;
}else if(link.type == types.finish_to_start){
from_start = false;
to_start = true;
}else if(link.type == types.start_to_finish){
from_start = true;
to_start = false;
}else{
dhtmlx.assert(false, "Invalid link type");
}
var from = gantt._get_task_visible_pos(gantt._pull[link.source], from_start);
var to = gantt._get_task_visible_pos(gantt._pull[link.target], to_start);
return {
x : from.x,
e_x : to.x,
y : from.y ,
e_y : to.y
};
}
};
gantt._init_links_dnd = function() {
var dnd = new dhtmlxDnD(this.$task_bars, { sensitivity : 0, updates_per_second : 60 }),
start_marker = "task_left",
end_marker = "task_right",
link_edge_marker = "gantt_link_point",
link_landing_hover_area = "gantt_link_control";
dnd.attachEvent("onBeforeDragStart", dhtmlx.bind(function(obj,e) {
if(gantt.config.readonly)
return false;
var target = (e.target||e.srcElement);
resetDndState();
if(gantt.getState().drag_id)
return false;
if(gantt._locate_css(target, link_edge_marker)){
if(gantt._locate_css(target, start_marker))
gantt._link_source_task_start = true;
var sid = gantt._link_source_task = this.locate(e);
var t = gantt.getTask(sid);
var shift = 0;
if(t.type == gantt.config.types.milestone){
shift = (gantt._get_visible_milestone_width() - gantt._get_milestone_width())/2;
}
this._dir_start = getLinePos(t, !!gantt._link_source_task_start, shift);
return true;
}else{
return false;
}
}, this));
dnd.attachEvent("onAfterDragStart", dhtmlx.bind(function(obj,e) {
updateMarkedHtml(dnd.config.marker);
}, this));
function getLinePos(task, to_start, shift){
var pos = gantt._get_task_pos(task, !!to_start);
pos.y += gantt._get_task_height()/2;
shift = shift || 0;
pos.x += (to_start ? -1 : 1)*shift;
return pos;
}
dnd.attachEvent("onDragMove", dhtmlx.bind(function(obj,e) {
var dd = dnd.config;
var pos = dnd.getPosition(e);
advanceMarker(dd.marker, pos);
var landing = gantt._is_link_drop_area(e);
var prevTarget = gantt._link_target_task;
var prevLanding = gantt._link_landing;
var prevToStart = gantt._link_target_task_start;
var targ = gantt.locate(e),
to_start = true;
if(landing){
//refreshTask
to_start = !gantt._locate_css(e, end_marker);
landing = !!targ;
}
gantt._link_target_task = targ;
gantt._link_landing = landing;
gantt._link_target_task_start = to_start;
if(landing){
var t = gantt.getTask(targ);
var node = gantt._locate_css(e, link_landing_hover_area);
var shift = 0;
if(node){
shift = Math.floor(node.offsetWidth / 2);
}
this._dir_end = getLinePos(t, !!gantt._link_target_task_start,shift);
}else{
this._dir_end = gantt._get_mouse_pos(e);
}
var targetChanged = !(prevLanding == landing && prevTarget == targ && prevToStart == to_start);
if(targetChanged){
if(prevTarget)
gantt.refreshTask(prevTarget, false);
if(targ)
gantt.refreshTask(targ, false);
}
if(targetChanged){
updateMarkedHtml(dd.marker);
}
showDirectingLine(this._dir_start.x, this._dir_start.y, this._dir_end.x, this._dir_end.y);
return true;
}, this));
dnd.attachEvent("onDragEnd", dhtmlx.bind(function() {
var link = getDndState();
if(link.from && link.to && link.from != link.to){
var type = gantt._get_link_type(link.from_start, link.to_start);
if(type)
gantt.addLink({source : link.from, target: link.to, type:type});
}
resetDndState();
if(link.from)
gantt.refreshTask(link.from, false);
if(link.to)
gantt.refreshTask(link.to, false);
removeDirectionLine();
}, this));
function updateMarkedHtml(marker){
var link = getDndState();
var css = ["gantt_link_tooltip"];
if(link.from && link.to){
if(gantt.isLinkAllowed(link.from, link.to, link.from_start, link.to_start)){
css.push("gantt_allowed_link");
}else{
css.push("gantt_invalid_link");
}
}
var className = gantt.templates.drag_link_class(link.from, link.from_start, link.to, link.to_start);
if(className)
css.push(className);
var html = "<div class='"+className+ "'>" +
gantt.templates.drag_link(link.from, link.from_start, link.to, link.to_start) +
"</div>";
marker.innerHTML = html;
}
function advanceMarker(marker, pos){
marker.style.left = pos.x + 5 + "px";
marker.style.top = pos.y + 5 + "px";
}
function getDndState(){
return { from : gantt._link_source_task,
to : gantt._link_target_task,
from_start : gantt._link_source_task_start,
to_start : gantt._link_target_task_start};
}
function resetDndState(){
gantt._link_source_task =
gantt._link_source_task_start =
gantt._link_target_task = null;
gantt._link_target_task_start = true;
}
function showDirectingLine(s_x, s_y, e_x, e_y){
var div = getDirectionLine();
var link = getDndState();
var css = ["gantt_link_direction"];
if(gantt.templates.link_direction_class){
css.push(gantt.templates.link_direction_class(link.from, link.from_start, link.to, link.to_start));
}
var dist =Math.sqrt( (Math.pow(e_x - s_x, 2)) + (Math.pow(e_y - s_y, 2)) );
dist = Math.max(0, dist - 3);
if(!dist)
return;
div.className = css.join(" ");
var tan = (e_y - s_y)/(e_x - s_x),
angle = Math.atan(tan);
if(coordinateCircleQuarter(s_x, e_x, s_y, e_y) == 2){
angle += Math.PI;
}else if(coordinateCircleQuarter(s_x, e_x, s_y, e_y) == 3){
angle -= Math.PI;
}
var sin = Math.sin(angle),
cos = Math.cos(angle),
top = Math.round(s_y),
left = Math.round(s_x);
var style = [
"-webkit-transform: rotate("+angle+"rad)",
"-moz-transform: rotate("+angle+"rad)",
"-ms-transform: rotate("+angle+"rad)",
"-o-transform: rotate("+angle+"rad)",
"transform: rotate("+angle+"rad)",
"width:" + Math.round(dist) + "px"
];
if(window.navigator.userAgent.indexOf("MSIE 8.0") != -1){
//ms-filter breaks styles in ie9, so add it only for 8th
style.push("-ms-filter: \"" + ieTransform(sin, cos) + "\"");
var shiftLeft = Math.abs(Math.round(s_x - e_x)),
shiftTop = Math.abs(Math.round(e_y - s_y));
//fix rotation axis
switch(coordinateCircleQuarter(s_x, e_x, s_y, e_y)){
case 1:
top -= shiftTop;
break;
case 2:
left -= shiftLeft;
top -= shiftTop;
break;
case 3:
left -= shiftLeft;
break;
default:
break;
}
}
style.push("top:" + top + "px");
style.push("left:" + left + "px");
div.style.cssText = style.join(";");
}
function ieTransform(sin, cos){
return "progid:DXImageTransform.Microsoft.Matrix("+
"M11 = "+cos+","+
"M12 = -"+sin+","+
"M21 = "+sin+","+
"M22 = "+cos+","+
"SizingMethod = 'auto expand'"+
")";
}
function coordinateCircleQuarter(sX, eX, sY, eY){
if(eX >= sX){
if(eY <= sY){
return 1;
}else{
return 4;
}
}else{
if(eY <= sY){
return 2;
}else{
return 3;
}
}
}
function getDirectionLine(){
if(!dnd._direction){
dnd._direction = document.createElement("div");
gantt.$task_links.appendChild(dnd._direction);
}
return dnd._direction;
}
function removeDirectionLine(){
if(dnd._direction){
if (dnd._direction.parentNode) //the event line can be detached because of data refresh
dnd._direction.parentNode.removeChild(dnd._direction);
dnd._direction = null;
}
}
gantt._is_link_drop_area = function(e){
return !!gantt._locate_css(e, link_landing_hover_area);
};
};
gantt._get_link_state = function(){
return {
link_landing_area : this._link_landing,
link_target_id : this._link_target_task,
link_target_start : this._link_target_task_start,
link_source_id : this._link_source_task,
link_source_start : this._link_source_task_start
};
};
gantt._init_tasks = function(){
//store temporary configs
this._tasks = {
col_width:this.config.columnWidth,
width: [], // width of each column
full_width: 0, // width of all columns
trace_x:[],
rendered:{}
};
this._click.gantt_task_link = dhtmlx.bind(function(e, trg){
var id = this.locate(e, gantt.config.link_attribute);
if(id){
this.callEvent("onLinkClick", [id, e]);
}
}, this);
this._dbl_click.gantt_task_link = dhtmlx.bind(function(e, id, trg){
var id = this.locate(e, gantt.config.link_attribute);
this._delete_link_handler(id, e);
}, this);
this._dbl_click.gantt_link_point = dhtmlx.bind(function(e, id, trg){
var id = this.locate(e),
task = this.getTask(id);
var link = null;
if(trg.parentNode && trg.parentNode.className){
if(trg.parentNode.className.indexOf("_left") > -1){
link = task.$target[0];
}else{
link = task.$source[0];
}
}
if(link)
this._delete_link_handler(link, e);
return false;
}, this);
this._tasks_dnd.init();
this._init_links_dnd();
var filter_grid_task = this._create_filter('_filter_task', '_is_grid_visible');
var filter_chart_task = this._create_filter('_filter_task', '_is_chart_visible');
var filter_link = this._create_filter('_filter_link', '_is_chart_visible');
this._taskRenderer = gantt._task_renderer("line", this._render_task_element, this.$task_bars, filter_chart_task);
this._linkRenderer = gantt._task_renderer("links", this._render_link_element, this.$task_links, filter_link);
this._gridRenderer = gantt._task_renderer("grid_items", this._render_grid_item, this.$grid_data, filter_grid_task);
this._bgRenderer = gantt._task_renderer("bg_lines", this._render_bg_line, this.$task_bg, filter_chart_task);
function refreshId(renders, oldId, newId, item){
for(var i =0; i < renders.length; i++){
renders[i].change_id(oldId, newId);
renders[i].render_item(item);
}
}
this.attachEvent("onTaskIdChange", function(oldId, newId){
var render = this._get_task_renderers();
refreshId(render, oldId, newId, this.getTask(newId));
});
this.attachEvent("onLinkIdChange", function(oldId, newId){
var render = this._get_link_renderers();
refreshId(render, oldId, newId, this.getLink(newId));
});
};
gantt._create_filter = function(filter_methods){
if(!(filter_methods instanceof Array)){
filter_methods = Array.prototype.slice.call(arguments, 0);
}
return function(obj){
var res = true;
for(var i = 0, len = filter_methods.length; i < len; i++){
var filter_method = filter_methods[i];
if(gantt[filter_method]){
res = res && (gantt[filter_method].apply(gantt, [obj.id, obj]) !== false);
}
}
return res;
};
};
gantt._is_chart_visible = function(){
return !!this.config.show_chart;
};
gantt._filter_task = function(id, task){
var min = null, max = null;
if(this.config.start_date && this.config.end_date){
min = this.config.start_date.valueOf();
max = this.config.end_date.valueOf();
if(+task.start_date > max || +task.end_date < +min)
return false;
}
return true;
};
gantt._filter_link = function(id, link){
if(!this.config.show_links){
return false;
}
if(!(gantt.isTaskVisible(link.source) && gantt.isTaskVisible(link.target)))
return false;
return this.callEvent("onBeforeLinkDisplay", [id, link]);
};
gantt._get_task_renderers = function(){
return [
this._taskRenderer,
this._gridRenderer,
this._bgRenderer
];
};
gantt._get_link_renderers = function(){
return [
this._linkRenderer
];
};
gantt._delete_link_handler = function(id, e){
if(id && this.callEvent("onLinkDblClick", [id, e])){
if(this.config.readonly) return;
var title = "";
var question = gantt.locale.labels.link + " " +this.templates.link_description(this.getLink(id)) + " " + gantt.locale.labels.confirm_link_deleting;
window.setTimeout(function(){
gantt._dhtmlx_confirm(question, title, function(){
gantt.deleteLink(id);
});
},(gantt.config.touch ? 300 : 1));
}
};
gantt.getTaskNode = function(id){
return this._taskRenderer.rendered[id];
};
gantt.getLinkNode = function(id){
return this._linkRenderer.rendered[id];
};
gantt._get_tasks_data = function(){
var rows = [];
for(var i=0; i < this._order.length; i++){
var item = this._pull[this._order[i]];
item.$index = i;
this._update_parents(item.id, true);
rows.push(item);
}
return rows;
};
gantt._get_links_data = function(){
var links = [];
for(var i in this._lpull)
links.push(this._lpull[i]);
return links;
};
gantt._render_data = function(){
this._update_layout_sizes();
var data = this._get_tasks_data();
var renderers = this._get_task_renderers();
for(var i=0; i < renderers.length; i++){
renderers[i].render_items(data);
}
var links = gantt._get_links_data();
renderers = this._get_link_renderers();
for(var i=0; i < renderers.length; i++)
renderers[i].render_items(links);
};
gantt._update_layout_sizes = function(){
var cfg = this._tasks;
cfg.bar_height = this._get_task_height();
//task bars layer
this.$task_data.style.height = Math.max(this.$task.offsetHeight - this.config.scale_height, 0) + 'px';
//background layer
this.$task_bg.style.width = cfg.full_width + "px";
//grid area
if(this._is_grid_visible()){
var columns = this.config.columns;
var width = 0;
for (var i = 0; i < columns.length; i++)
width += columns[i].width;
this.$grid_data.style.width = Math.max(width-1, 0) + "px";
}
};
gantt._init_tasks_range = function(){
var unit = this.config.scale_unit;
if(this.config.start_date && this.config.end_date){
this._min_date = this.date[unit + "_start"]( new Date(this.config.start_date));
this._max_date = this.date[unit + "_start"]( new Date(this.config.end_date));
return;
}
var data = this._get_tasks_data();
var root = this._init_task({id:this.config.root_id});
data.push(root);
var max = -Infinity,
min = Infinity;
this.eachTask(function(child){
if(child.end_date && +child.end_date > +max){
max = new Date(child.end_date);
}
}, this.config.root_id);
this.eachTask(function(child){
if(child.start_date && +child.start_date < +min){
min = new Date(child.start_date);
}
}, this.config.root_id);
this._min_date = min;
this._max_date = max;
if(!max || max == -Infinity){
this._min_date = new Date();
this._max_date = new Date(this._min_date);
}
this._min_date = this.date[unit + "_start"](this._min_date);
if(+this._min_date == +min)
this._min_date = this.date.add(this.date[unit + "_start"](this._min_date), -1, unit);
this._max_date = this.date[unit + "_start"](this._max_date);
this._max_date = this.date.add(this._max_date, 1, unit);
};
gantt._prepare_scale_html = function(config){
var cells = [];
var date = null, content = null, css = null;
if(config.template || config.date){
content = config.template || this.date.date_to_str(config.date);
}
css = config.css || gantt.templates.scale_cell_class;
for (var i = 0; i < config.count; i++) {
date = new Date(config.trace_x[i]);
var value = content.call(this, date),
width = config.width[i],
style = "",
template = "",
cssclass = "";
if(width){
style = "width:"+(width)+"px;";
cssclass = "gantt_scale_cell" + (i == config.count-1 ? " gantt_last_cell" : "");
template = css.call(this, date);
if(template) cssclass += " " + template;
var cell = "<div class='" + cssclass + "' style='" + style + "'>" + value + "</div>";
cells.push(cell);
}else{
//do not render ignored cells
}
}
return cells.join("");
};
gantt._render_tasks_scales = function() {
this._init_tasks_range();
this._scroll_resize();
this._set_sizes();
var scales_html = "",
outer_width = 0,
data_width = 0,
scale_height = 0;
if(this._is_chart_visible()){
var helpers = this._scale_helpers;
var scales = [helpers.primaryScale()].concat(this.config.subscales);
scale_height = (this.config.scale_height-1);
helpers.sortScales(scales);
var resize = this._get_resize_options();
var avail_width = resize.x ? 0 : this.$task.offsetWidth;
var cfgs = helpers.prepareConfigs(scales,this.config.min_column_width, avail_width, scale_height);
var cfg = this._tasks = cfgs[cfgs.length - 1];
var html = [];
var css = this.templates.scale_row_class;
for(var i=0; i < cfgs.length; i++){
var cssClass = "gantt_scale_line";
var tplClass = css(cfgs[i]);
if(tplClass){
cssClass += " " + tplClass;
}
html.push("<div class=\""+cssClass+"\" style=\"height:"+(cfgs[i].height)+"px;line-height:"+(cfgs[i].height)+"px\">" + this._prepare_scale_html(cfgs[i]) + "</div>");
}
scales_html = html.join("");
outer_width = cfg.full_width + this.$scroll_ver.offsetWidth + "px";
data_width = cfg.full_width + "px";
scale_height += "px";
}
if(this._is_chart_visible()){
this.$task.style.display = "";
}else{
this.$task.style.display = "none";
}
this.$task_scale.style.height = scale_height;
this.$task_data.style.width =
this.$task_scale.style.width = outer_width;
this.$task_links.style.width =
this.$task_bars.style.width = data_width;
this.$task_scale.innerHTML = scales_html;
};
gantt._render_bg_line = function(item){
var cfg = gantt._tasks;
var count = cfg.count;
var cells = [];
if(gantt.config.show_task_cells){
for (var j = 0; j < count; j++) {
var width = cfg.width[j],
style = "",
cssclass = "";
if(width > 0){//do not render skipped columns
style = "width:"+(width)+"px;";
cssclass = "gantt_task_cell" + (j == count-1 ? " gantt_last_cell" : "");
cssTemplate = this.templates.task_cell_class(item, cfg.trace_x[j]);
if(cssTemplate)
cssclass += " " + cssTemplate;
var cell = "<div class='" + cssclass + "' style='" + style + "'></div>";
cells.push(cell);
}
}
}
var odd = item.$index%2 !== 0;
var cssTemplate = gantt.templates.task_row_class(item.start_date, item.end_date, item);
var css = "gantt_task_row" + (odd ? " odd" : "") + (cssTemplate ? ' '+cssTemplate : '');
if(this.getState().selected_task == item.id){
css += " gantt_selected";
}
//var row = "<div class='" + css + "' " + this.config.task_attribute + "='" + item.id + "'>" + cells.join("") + "</div>";
var row = document.createElement("div");
row.className = css;
row.style.height = (gantt.config.row_height)+"px";
row.setAttribute(this.config.task_attribute, item.id);
row.innerHTML = cells.join("");
return row;
};
gantt._adjust_scales = function(){
if(this.config.fit_tasks){
var old_min = +this._min_date,
old_max = +this._max_date;
this._init_tasks_range();
if(+this._min_date != old_min || +this._max_date != old_max){
this.render();
this.callEvent("onScaleAdjusted", []);
return true;
}
}
return false;
};
//refresh task and related links
gantt.refreshTask = function(taskId, refresh_links){
var renders = this._get_task_renderers();
var task = this.getTask(taskId);
if(task && this.isTaskVisible(taskId)){
for(var i =0; i < renders.length; i++)
renders[i].render_item(task);
}else{
for(var i =0; i < renders.length; i++)
renders[i].remove_item(taskId);
}
if(refresh_links !== undefined && !refresh_links)
return;
var task = this.getTask(taskId);
for(var i=0; i < task.$source.length; i++){
gantt.refreshLink(task.$source[i]);
}
for(var i=0; i < task.$target.length; i++){
gantt.refreshLink(task.$target[i]);
}
};
gantt.refreshLink = function(linkId){
if(this.isLinkExists(linkId))
gantt._render_link(linkId);
else
gantt._linkRenderer.remove_item(linkId);
};
gantt._combine_item_class = function(basic, template, itemId){
var css = [basic];
if(template)
css.push(template);
var state = gantt.getState();
var task = this.getTask(itemId);
if(this._get_safe_type(task.type) == this.config.types.milestone){
css.push("gantt_milestone");
}
if(this._get_safe_type(task.type) == this.config.types.project){
css.push("gantt_project");
}
if(this._is_flex_task(task))
css.push("gantt_dependent_task");
if(this.config.select_task && itemId == state.selected_task)
css.push("gantt_selected");
if(itemId == state.drag_id)
css.push("gantt_drag_" + state.drag_mode);
var links = gantt._get_link_state();
if(links.link_source_id == itemId)
css.push("gantt_link_source");
if(links.link_target_id == itemId)
css.push("gantt_link_target");
if(links.link_landing_area &&
(links.link_target_id && links.link_source_id) &&
(links.link_target_id != links.link_source_id)){
var from_id = links.link_source_id;
var from_start = links.link_source_start;
var to_start = links.link_target_start;
var allowDrag = gantt.isLinkAllowed(from_id, itemId, from_start, to_start);
var dragClass = "";
if(allowDrag){
if(to_start)
dragClass = "link_start_allow";
else
dragClass = "link_finish_allow";
}else{
if(to_start)
dragClass = "link_start_deny";
else
dragClass = "link_finish_deny";
}
css.push(dragClass);
}
return css.join(" ");
};
gantt._render_pair = function(parent, css, task, content){
var state = gantt.getState();
if(+task.end_date <= +state.max_date)
parent.appendChild(content(css+" task_right"));
if(+task.start_date >= +state.min_date)
parent.appendChild(content(css+" task_left"));
};
gantt._get_task_height = function(){
// height of the bar item
var height = this.config.task_height;
if(height == "full")
height = this.config.row_height - 5;
//item height cannot be bigger than row height
height = Math.min(height, this.config.row_height);
return Math.max(height, 0);
};
gantt._get_milestone_width = function(){
return this._get_task_height();
};
gantt._get_visible_milestone_width = function(){
var origWidth = gantt._get_task_height();//m-s have square shape
return Math.sqrt(2*origWidth*origWidth);
};
gantt._get_task_width = function(task, start, end ){
return Math.round(this._get_task_pos(task, false).x - this._get_task_pos(task, true).x);
};
gantt._render_task_element = function(task){
var pos = this._get_task_pos(task);
var cfg = this.config;
var height = this._get_task_height();
var padd = Math.floor((this.config.row_height - height)/2);
if(task.type == cfg.types.milestone && cfg.link_line_width > 1){
//little adjust milestone position, so horisontal corners would match link arrow when thickness of link line is more than 1px
padd += 1;
}
var div = document.createElement("div");
var width = gantt._get_task_width(task);
var type = this._get_safe_type(task.type);
div.setAttribute(this.config.task_attribute, task.id);
//use separate div to display content above progress bar
div.appendChild(gantt._render_task_content(task, width));
div.className = this._combine_item_class("gantt_task_line",
this.templates.task_class(task.start_date, task.end_date, task),
task.id);
div.style.cssText = [
"left:" + pos.x + "px",
"top:" + (padd + pos.y) + 'px',
"height:" + height + 'px',
"line-height:" + height + 'px',
"width:" + width + 'px'
].join(";");
var side = this._render_leftside_content(task);
if(side) div.appendChild(side);
side = this._render_rightside_content(task);
if(side) div.appendChild(side);
if(cfg.show_progress && type != this.config.types.milestone){
this._render_task_progress(task,div, width);
}
if(!this.config.readonly){
if(cfg.drag_resize && !this._is_flex_task(task) && type != this.config.types.milestone){
gantt._render_pair(div, "gantt_task_drag", task, function(css){
var el = document.createElement("div");
el.className = css;
return el;
});
}
if(cfg.drag_links){
gantt._render_pair(div, "gantt_link_control", task, function(css){
var outer = document.createElement("div");
outer.className = css;
outer.style.cssText = [
"height:" + height + 'px',
"line-height:" + height + 'px'
].join(";");
var inner = document.createElement("div");
inner.className = "gantt_link_point";
outer.appendChild(inner);
return outer;
});
}
}
return div;
};
gantt._render_side_content = function(task, template, cssClass){
if(!template) return null;
var text = template(task.start_date, task.end_date, task);
if(!text) return null;
var content = document.createElement("div");
content.className = "gantt_side_content " + cssClass;
content.innerHTML = text;
return content;
};
gantt._render_leftside_content = function(task){
var css = "gantt_left " + gantt._get_link_crossing_css(true, task);
return gantt._render_side_content(task, this.templates.leftside_text, css);
};
gantt._render_rightside_content = function(task){
var css = "gantt_right " + gantt._get_link_crossing_css(false, task);
return gantt._render_side_content(task, this.templates.rightside_text, css);
};
gantt._get_conditions = function(leftside){
if(leftside){
return {
$source : [
gantt.config.links.start_to_start
],
$target : [
gantt.config.links.start_to_start,
gantt.config.links.finish_to_start
]
};
}else{
return {
$source : [
gantt.config.links.finish_to_start,
gantt.config.links.finish_to_finish
],
$target : [
gantt.config.links.finish_to_finish
]
};
}
};
gantt._get_link_crossing_css = function(left, task){
var cond = gantt._get_conditions(left);
for(var i in cond){
var links = task[i];
for(var ln =0; ln < links.length; ln++){
var link = gantt.getLink(links[ln]);
for(var tp =0; tp < cond[i].length; tp++){
if(link.type == cond[i][tp]){
return "gantt_link_crossing";
}
}
}
}
return "";
};
gantt._render_task_content = function(task, width){
var content = document.createElement("div");
if(this._get_safe_type(task.type) != this.config.types.milestone)
content.innerHTML = this.templates.task_text(task.start_date, task.end_date, task);
content.className = "gantt_task_content";
//content.style.width = width + 'px';
return content;
};
gantt._render_task_progress = function(task, element, maxWidth){
var done = task.progress*1 || 0;
maxWidth = Math.max(maxWidth - 2, 0);//2px for borders
var pr = document.createElement("div");
var width = Math.round(maxWidth*done);
width = Math.min(maxWidth, width);
pr.style.width = width + 'px';
pr.className = "gantt_task_progress";
pr.innerHTML = this.templates.progress_text(task.start_date, task.end_date, task);
element.appendChild(pr);
if(this.config.drag_progress && !gantt.config.readonly){
var drag = document.createElement("div");
drag.style.left = width + 'px';
drag.className = "gantt_task_progress_drag";
pr.appendChild(drag);
element.appendChild(drag);
}
};
gantt._get_line = function(step) {
var steps = {
"second": 1,
"minute": 60,
"hour": 60*60,
"day": 60*60*24,
"week": 60*60*24*7,
"month": 60*60*24*30,
"year": 60*60*24*365
};
return steps[step] || 0;
};
gantt._date_from_pos = function(x){
var scale = this._tasks;
if(x < 0 || x > scale.full_width){
return null;
}
var ind = 0;
var summ = 0;
while(summ + scale.width[ind] < x){
summ += scale.width[ind];
ind++;
}
var part = (x - summ)/scale.width[ind];
var unit = gantt._get_coll_duration(scale, scale.trace_x[ind]);
var date = new Date(scale.trace_x[ind].valueOf() + Math.round(part*unit));
return date;
};
gantt.posFromDate = function(date){
var ind = gantt._day_index_by_date(date);
dhtmlx.assert(ind >= 0, "Invalid day index");
var wholeCells = Math.floor(ind);
var partCell = ind % 1;
var pos = 0;
for(var i=1; i <= wholeCells; i++)
pos += gantt._tasks.width[i-1];
if(partCell){
if(wholeCells < gantt._tasks.width.length){
pos += gantt._tasks.width[wholeCells]*(partCell % 1);
}else{
pos += 1;
}
}
return pos;
};
gantt._day_index_by_date = function(date){
var pos = new Date(date);
var days = gantt._tasks.trace_x,
ignores = gantt._tasks.ignore_x;
if(+pos <= this._min_date)
return 0;
if(+pos >= this._max_date)
return days.length;
for (var xind = 0; xind < days.length-1; xind++) {
// | 8:00, 8:30 | 8:15 should be checked against 8:30
// clicking at the most left part of the cell, say 8:30 should create event in that cell, not previous one
if (+pos < days[xind+1] && !ignores[+days[xind+1]])
break;
}
return xind + ((date - days[xind]) / gantt._get_coll_duration(gantt._tasks, days[xind]));
};
gantt._get_coll_duration = function(scale, date){
return gantt.date.add(date, scale.step, scale.unit) - date;
};
gantt._get_x_pos = function(task, to_start){
to_start = to_start !== false;
var x = gantt.posFromDate(to_start ? task.start_date : task.end_date);
};
gantt._get_task_coord = function(task, to_start, x_correction){
to_start = to_start !== false;
x_correction = x_correction || 0;
var isMilestone = (task.type == this.config.types.milestone);
var x = this.posFromDate((to_start || isMilestone) ? task.start_date : task.end_date),
y = this._y_from_ind(this._get_visible_order(task.id));
if(isMilestone){
if(to_start){
x -= x_correction;
}else{
x += x_correction;
}
}
return {x:x, y:y};
};
gantt._get_task_pos = function(task, to_start){
to_start = to_start !== false;
var mstoneCorrection = gantt._get_milestone_width()/2;
return this._get_task_coord(task, to_start, mstoneCorrection);
};
gantt._get_task_visible_pos = function(task, to_start){
to_start = to_start !== false;
var mstoneCorrection = gantt._get_visible_milestone_width()/2;
return this._get_task_coord(task, to_start, mstoneCorrection);
};
gantt._correct_shift=function(start, back){
return start-=((new Date(gantt._min_date)).getTimezoneOffset()-(new Date(start)).getTimezoneOffset())*60000*(back?-1:1);
};
gantt._get_mouse_pos = function(ev){
if (ev.pageX || ev.pageY)
var pos = {x:ev.pageX, y:ev.pageY};
var d = _isIE ? document.documentElement : document.body;
var pos = {
x:ev.clientX + d.scrollLeft - d.clientLeft,
y:ev.clientY + d.scrollTop - d.clientTop
};
var box = gantt._get_position(gantt.$task_data);
pos.x = pos.x - box.x + gantt.$task_data.scrollLeft;
pos.y = pos.y - box.y + gantt.$task_data.scrollTop;
return pos;
};
//helper for rendering bars and links
gantt._task_renderer = function(id, render_one, node, filter){
//hash of dom elements is needed to redraw single bar/link
if(!this._task_area_pulls)
this._task_area_pulls = {};
if(!this._task_area_renderers)
this._task_area_renderers = {};
if(this._task_area_renderers[id])
return this._task_area_renderers[id];
if(!render_one)
dhtmlx.assert(false, "Invalid renderer call");
this._task_area_renderers[id] = {
render_item : function(item, container){
var pull = gantt._task_area_pulls[id];
container = container || node;
if(filter){
if(!filter(item)){
this.remove_item(item.id);
return;
}
}
var dom = render_one.call(gantt, item);
if(!dom) return;
if(pull[item.id]){
this.replace_item(item.id, dom);
}else{
pull[item.id] = dom;
container.appendChild(dom);
}
},
render_items : function(items, container){
this.rendered = gantt._task_area_pulls[id] = {};
container = container || node;
container.innerHTML = "";
var buffer = document.createDocumentFragment();
for(var i= 0, vis = items.length; i < vis; i++){
this.render_item(items[i], buffer);
}
container.appendChild(buffer);
},
replace_item: function(item_id, newNode){
var item = this.rendered[item_id];
if(item && item.parentNode){
item.parentNode.replaceChild(newNode, item);
}
this.rendered[item_id] = newNode;
},
remove_item:function(item_id){
var item = this.rendered[item_id];
if(item && item.parentNode){
item.parentNode.removeChild(item);
}
delete this.rendered[item_id];
},
change_id: function(oldid, newid) {
this.rendered[newid] = this.rendered[oldid];
delete this.rendered[oldid];
},
rendered : this._task_area_pulls[id],
node: node
};
return this._task_area_renderers[id];
};
gantt._pull = {};
gantt._branches = {};
gantt._order = [];
gantt._lpull = {};
gantt.load = function(url, type, callback){
dhtmlx.assert(arguments.length, "Invalid load arguments");
this.callEvent("onLoadStart", []);
var tp = 'json', cl = null;
if(arguments.length >= 3){
tp = type;
cl = callback;
}else{
if(typeof arguments[1] == "string")
tp = arguments[1];
else if(typeof arguments[1] == "function")
cl = arguments[1];
}
dhtmlxAjax.get(url, dhtmlx.bind(function(l) {
this.on_load(l, tp);
if(typeof cl == "function")
cl.call(this);
}, this));
};
gantt.parse = function(data, type) {
this.on_load({xmlDoc: {responseText: data}}, type);
};
gantt.serialize = function(type){
type = type || "json";
return this[type].serialize();
};
/*
tasks and relations
{
data:[
{
"id":"string",
"text":"...",
"start_date":"Date or string",
"end_date":"Date or string",
"duration":"number",
"progress":"0..1",
"parent_id":"string",
"order":"number"
},...],
links:[
{
id:"string",
source:"string",
target:"string",
type:"string"
},...],
collections:{
collectionName:[
{key:, label:, optional:...},...
],...
}
}
gantt._pull - id to object hash
gantt._branch - array of per branch arrays of objects|ids
gantt._order - array of visible elements
gantt._order_full - array of all elements
gantt._links
* */
gantt.on_load = function(resp, type){
if(!type)
type = "json";
dhtmlx.assert(this[type], "Invalid data type:'" + type + "'");
var raw = resp.xmlDoc.responseText;
var data = this[type].parse(raw, resp);
this._process_loading(data);
this.callEvent("onLoadEnd", []);
};
gantt._process_loading = function(data){
if(data.collections)
this._load_collections(data.collections);
var tasks = data.data;
for (var i = 0; i < tasks.length; i++) {
var task = tasks[i];
this._init_task(task);
if (!this.callEvent("onTaskLoading", [task])) continue;
this._pull[task.id] = task;
this._add_branch(task);
}
this._sync_order();
// calculating $level for each item
for (var i in this._pull)
this._pull[i].$level = this._item_level(this._pull[i]);
this._init_links(data.links || (data.collections ? data.collections.links : []));
};
gantt._init_links = function(links){
if (links)
for(var i=0; i < links.length; i++){
if(links[i]){
var link = this._init_link(links[i]);
this._lpull[link.id] = link;
}
}
this._sync_links();
};
gantt._load_collections = function(collections){
var collections_loaded = false;
for (var key in collections) {
if (collections.hasOwnProperty(key)) {
collections_loaded = true;
var collection = collections[key];
var arr = this.serverList[key];
if (!arr) continue;
arr.splice(0, arr.length); //clear old options
for (var j = 0; j < collection.length; j++) {
var option = collection[j];
var obj = dhtmlx.copy(option);
obj.key = obj.value;// resulting option object
for (var option_key in option) {
if (option.hasOwnProperty(option_key)) {
if (option_key == "value" || option_key == "label")
continue;
obj[option_key] = option[option_key]; // obj['value'] = option['value']
}
}
arr.push(obj);
}
}
}
if (collections_loaded)
this.callEvent("onOptionsLoad", []);
};
gantt._sync_order = function() {
this._order = [];
this._sync_order_item({parent:this.config.root_id, $open:true, $ignore:true, id:this.config.root_id});
this._scroll_resize();
this._set_sizes();
};
gantt.attachEvent("onBeforeTaskDisplay", function(id, task){
return !task.$ignore;
});
gantt._sync_order_item = function(item) {
if(item.id && //do not trigger event for virtual root
this._filter_task(item.id, item) &&
this.callEvent("onBeforeTaskDisplay", [item.id, item])){
this._order.push(item.id);
}
if (item.$open) {
var children = this._branches[item.id];
if (children)
for (var i = 0; i < children.length; i++)
this._sync_order_item(this._pull[children[i]]);
}
};
gantt._get_visible_order = function(id){
dhtmlx.assert(id, "Invalid argument");
var ord = this._order;
for(var i= 0, count = ord.length; i < count; i++)
if(ord[i] == id) return i;
return -1;
};
gantt.eachTask = function(code, parent, master){
parent = parent || this.config.root_id;
master = master || this;
var branch = this._branches[parent];
if (branch)
for (var i=0; i<branch.length; i++){
var item = this._pull[branch[i]];
code.call(master, item);
if (this._branches[item.id])
this.eachTask(code, item.id, master);
}
};
gantt.json = {
parse : function(data){
dhtmlx.assert(data, "Invalid data");
if (typeof data == "string") {
if(window.JSON)
data = JSON.parse(data);
else{
gantt._temp = eval("(" + data + ")");
data = gantt._temp || {};
gantt._temp = null;
}
}
if (data.dhx_security)
dhtmlx.security_key = data.dhx_security;
return data;
},
_copyLink:function(obj){
var copy = {};
for (var key in obj)
copy[key] = obj[key];
return copy;
},
_copyObject:function(obj){
var copy = {};
for (var key in obj){
if (key.charAt(0) == "$")
continue;
copy[key] = obj[key];
}
copy.start_date = gantt.templates.xml_format(copy.start_date);
if (copy.end_date)
copy.end_date = gantt.templates.xml_format(copy.end_date);
return copy;
},
serialize:function(){
var tasks = [];
var links = [];
gantt.eachTask(function(obj){
tasks.push(this._copyObject(obj));
}, gantt.config.root_id, this);
for (var key in gantt._lpull)
links.push(this._copyLink(gantt._lpull[key]));
return {
data : tasks,
links: links
};
}
};
/*
<data>
<task id:"some" parent_id="0" progress="0.5">
<text>My task 1</text>
<start_date>16.08.2013</start_date>
<end_date>22.08.2013</end_date>
</task>
<coll_options>
<links>
<link source='a1' target='b2' type='c3' />
</links>
</coll_options>
</data>
*/
gantt.xml = {
_xmlNodeToJSON:function(node, attrs_only){
var t = {};
for (var i = 0; i < node.attributes.length; i++)
t[node.attributes[i].name] = node.attributes[i].value;
if (!attrs_only){
for (var i = 0; i < node.childNodes.length; i++) {
var child = node.childNodes[i];
if (child.nodeType == 1)
t[child.tagName] = child.firstChild ? child.firstChild.nodeValue : "";
}
if (!t.text) t.text = node.firstChild ? node.firstChild.nodeValue : "";
}
return t;
},
_getCollections:function(loader){
var collection = {};
var opts = loader.doXPath("//coll_options");
for (var i = 0; i < opts.length; i++) {
var bind = opts[i].getAttribute("for");
var arr = collection[bind] = [];
var itms = loader.doXPath(".//item", opts[i]);
for (var j = 0; j < itms.length; j++) {
var itm = itms[j];
var attrs = itm.attributes;
var obj = { key: itms[j].getAttribute("value"), label: itms[j].getAttribute("label")};
for (var k = 0; k < attrs.length; k++) {
var attr = attrs[k];
if (attr.nodeName == "value" || attr.nodeName == "label")
continue;
obj[attr.nodeName] = attr.nodeValue;
}
arr.push(obj);
}
}
return collection;
},
_getXML:function(text, loader, toptag){
toptag = toptag || "data";
if (!loader.getXMLTopNode){
loader = new dtmlXMLLoaderObject(function() {});
loader.loadXMLString(text);
}
var xml = loader.getXMLTopNode(toptag);
if (xml.tagName != toptag) throw "Invalid XML data";
var skey = xml.getAttribute("dhx_security");
if (skey)
dhtmlx.security_key = skey;
return loader;
},
parse:function(text, loader){
loader = this._getXML(text, loader);
var data = { };
var evs = data.data = [];
var xml = loader.doXPath("//task");
for (var i = 0; i < xml.length; i++)
evs[i] = this._xmlNodeToJSON(xml[i]);
data.collections = this._getCollections(loader);
return data;
},
_copyLink:function(obj){
return "<item id='"+obj.id+"' source='"+obj.source+"' target='"+obj.target+"' type='"+obj.type+"' />";
},
_copyObject:function(obj){
var start_date = gantt.templates.xml_format(obj.start_date);
var end_date = gantt.templates.xml_format(obj.end_date);
return "<task id='"+obj.id+"' parent='"+(obj.parent||"")+"' start_date='"+start_date+"' duration='"+obj.duration+"' open='"+(!!obj.open)+"' progress='"+obj.progress+"' end_date='"+end_date+"'><![CDATA["+obj.text+"]]></task>";
},
serialize:function(){
var tasks = [];
var links = [];
gantt.eachTask(function(obj){
tasks.push(this._copyObject(obj));
},this.config.root_id, this);
for (var key in gantt._lpull)
links.push(this._copyLink(gantt._lpull[key]));
return "<data>"+tasks.join("")+"<coll_options for='links'>"+links.join("")+"</coll_options></data>";
}
};
gantt.oldxml = {
parse:function(text, loader){
loader = gantt.xml._getXML(text, loader, "projects");
var data = { collections:{ links:[] } };
var evs = data.data = [];
var xml = loader.doXPath("//task");
for (var i = 0; i < xml.length; i++){
evs[i] = gantt.xml._xmlNodeToJSON(xml[i]);
var parent = xml[i].parentNode;
if (parent.tagName == "project")
evs[i].parent = "project-"+parent.getAttribute("id");
else
evs[i].parent = parent.parentNode.getAttribute("id");
}
xml = loader.doXPath("//project");
for (var i = 0; i < xml.length; i++){
var ev = gantt.xml._xmlNodeToJSON(xml[i], true);
ev.id ="project-"+ev.id;
evs.push(ev);
}
for (var i=0; i<evs.length; i++){
var ev = evs[i];
ev.start_date = ev.startdate || ev.est;
ev.end_date = ev.enddate;
ev.text = ev.name;
ev.duration = ev.duration / 8;
ev.open = 1;
if (!ev.duration && !ev.end_date) ev.duration = 1;
if (ev.predecessortasks)
data.collections.links.push({ target:ev.id, source:ev.predecessortasks, type:gantt.config.links.finish_to_start });
}
return data;
},
serialize:function(){
dhtmlx.message("Serialization to 'old XML' is not implemented");
}
};
gantt.serverList = function(name, array) {
if (array) {
this.serverList[name] = array.slice(0);
}else if(!this.serverList[name]){
this.serverList[name] = [];
}
return this.serverList[name];
};
gantt._working_time_helper = {
units : [
"year",
"month",
"week",
"day",
"hour",
"minute"
],
hours:[8, 17],
dates:{
0:false,
6:false
},
_get_unit_order : function(unit){
for(var i= 0, len = this.units.length; i < len; i++){
if(this.units[i] == unit)
return i;
}
dhtmlx.assert(false, "Incorrect duration unit");
},
_timestamp:function(settings){
var timestamp = null;
if((settings.day || settings.day === 0)){
timestamp = settings.day;
}else if(settings.date){
timestamp = gantt.date.date_part(new Date(settings.date)).valueOf();
}
return timestamp;
},
set_time:function(settings){
var hours = settings.hours !== undefined ? settings.hours : true;
var timestamp = this._timestamp(settings);
if(timestamp !== null){
this.dates[timestamp] = hours;
}else{
this.hours = hours;
}
},
unset_time:function(settings){
if(!settings){
this.hours = [];
}else{
var timestamp = this._timestamp(settings);
if(timestamp !== null){
delete this.dates[timestamp];
}
}
},
is_working_unit : function(date, unit, order){
if(!gantt.config.work_time) return true;
if(order === undefined){
order = this._get_unit_order(unit);
}
if(order === undefined){
return false;
}
if(order){
//check if bigger time unit is a work time (hour < day < month...)
//i.e. don't check particular hour if the whole day is marked as not working
if(!this.is_working_unit(date, this.units[order-1], order-1))
return false;
}
if(!this["is_work_" + unit])
return true;
return this["is_work_" + unit](date);
},
//checkings for particular time units
//methods for month-year-week can be defined, otherwise always return 'true'
is_work_day:function(date){
var val = this.get_working_hours(date);
if(val instanceof Array){
return val.length > 0;
}
return false;
},
is_work_hour:function(date){
var hours = this.get_working_hours(date);
var hour = date.getHours();
for(var i=0; i < hours.length; i += 2){
if(hours[i+1] === undefined){
return hours[i] == hour;
}else{
if(hour >= hours[i] && hour < hours[i+1])
return true;
}
}
return false;
},
get_working_hours:function(date){
var t = this._timestamp({date:date});
var hours = true;
if(this.dates[t] !== undefined){
hours = this.dates[t];//custom day
}else if(this.dates[date.getDay()] !== undefined){
hours = this.dates[date.getDay()];//week day
}
if(hours === true){
return this.hours;
}else if(hours){
return hours;
}
return [];
},
get_work_units_between:function(from, to, unit, step){
if(!unit){
return false;
}
var start = new Date(from),
end = new Date(to),
step = step || 1;
var units = 0;
while(start.valueOf() < end.valueOf()){
if(this.is_working_unit(start, unit))
units++;
start = gantt.date.add(start, step, unit);
}
return units;
},
add_worktime : function(from, duration, unit, step){
if(!unit)
return false;
var start = new Date(from),
added = 0,
step = step || 1,
duration = duration*1;
while(added < duration){
var next = gantt.date.add(start, step, unit);
if(this.is_working_unit(step > 0 ? start : next, unit))
added++;
start = next;
}
return start;
},
/* settings:
{
date:date,
unit:'day'/'hour'...,
dir:'future'/'past'/'any'/''
}
*/
get_closest_worktime : function(settings){
if(this.is_working_unit(settings.date, settings.unit))
return settings.date;
var unit = settings.unit;
var curr = gantt.date[unit + '_start'](settings.date);
var future_target = new Date(curr),
prev_target = new Date(curr),
tick = true,
maximum_loop = 3000,//be extra sure we won't fall into infinite loop, 3k seems big enough
count = 0,
both_directins = (settings.dir == 'any' || !settings.dir);
var inc = 1;
if(settings.dir == 'past')
inc = -1;
//will seek closest working hour in future or in past, one step in one direction per iteration
while(!this.is_working_unit(curr, unit)){
if(both_directins){
curr = tick ? future_target : prev_target;
inc = inc*(-1);
}
curr = gantt.date.add(curr, inc, unit);
if(both_directins){
if(tick){
future_target = curr;
}else{
prev_target = curr;
}
}
tick = !tick;
count++;
if(count > maximum_loop){
dhtmlx.assert(false, "Invalid working time check");
return false;
}
}
if(curr == prev_target || settings.dir == 'past'){
curr = gantt.date.add(curr, 1, unit);
}
return curr;
}
};
gantt.getTask = function(id) {
dhtmlx.assert(this._pull[id]);
return this._pull[id];
};
gantt.getTaskByTime = function(from, to){
var p = this._pull,
res = [],
pos = 0,
taken = 0;
if(!(from || to)){
for (var t in p) res.push(p[t]);
}else{
from = +from || -Infinity;
to = +to || Infinity;
for (var t in p){
var task = p[t];
if (+task.start_date < to && +task.end_date > from)
res.push(task);
}
}
return res;
};
gantt.isTaskExists = function(id) {
return dhtmlx.defined(this._pull[id]);
};
gantt.isTaskVisible = function(id){
if(!this._pull[id])
return false;
if(!(+this._pull[id].start_date < +this._max_date && +this._pull[id].end_date > +this._min_date))
return false;
for(var i= 0, count = this._order.length; i < count; i++)
if(this._order[i] == id) return true;
return false;
};
gantt.updateTask = function(id, item) {
if (!dhtmlx.defined(item)) item = this.getTask(id);
if (this.callEvent("onBeforeTaskUpdate", [id, item])===false) return false;
this._pull[item.id] = item;
if(!this._is_parent_sync(item)){
this._resync_parent(item);
}
this._update_parents(item.id);
this.refreshTask(item.id);
this.callEvent("onAfterTaskUpdate", [id, item]);
this._sync_order();
this._adjust_scales();
};
gantt._add_branch = function(task){
if (!this._branches[task.parent])
this._branches[task.parent] = [];
var branch = this._branches[task.parent];
var added_already = false;
for(var i = 0, length = branch.length; i < length; i++){
if(branch[i] == task.id){
added_already = true;
break;
}
}
if(!added_already)
branch.push(task.id);
this._sync_parent(task);
this._sync_order();
};
gantt._move_branch = function(task, old_parent, new_parent){
task.parent = new_parent;
this._sync_parent(task);
this._replace_branch_child(old_parent, task.id);
if(new_parent){
this._add_branch(task);
}else{
delete this._branches[task.id];
}
task.$level = this._item_level(task);
this._sync_order();
};
gantt._resync_parent = function(task){
this._move_branch(task, task.$rendered_parent, task.parent);
};
gantt._sync_parent = function(task){
task.$rendered_parent = task.parent;
};
gantt._is_parent_sync = function(task){
return (task.$rendered_parent == task.parent);
};
gantt._replace_branch_child = function(node, old_id, new_id){
var branch = this._branches[node];
if (branch){
var newbranch = [];
for (var i=0; i<branch.length; i++){
if (branch[i] != old_id)
newbranch.push(branch[i]);
else if (new_id)
newbranch.push(new_id);
}
this._branches[node] = newbranch;
}
this._sync_order();
};
gantt.addTask = function(item, parent) {
if (!dhtmlx.defined(parent)) parent = item.parent || 0;
if (!dhtmlx.defined(this._pull[parent])) parent = 0;
item.parent = parent;
item = this._init_task(item);
if (this.callEvent("onBeforeTaskAdd", [item.id, item])===false) return false;
this._pull[item.id] = item;
this._add_branch(item);
this.refreshData();
this.callEvent("onAfterTaskAdd", [item.id, item]);
this._adjust_scales();
return item.id;
};
gantt.deleteTask = function(id) {
return this._deleteTask(id);
};
gantt._deleteTask = function(id, silent) {
var item = this.getTask(id);
if (!silent && this.callEvent("onBeforeTaskDelete", [id, item])===false) return false;
if (!silent && this._dp)
this._dp.setUpdateMode("off");
var branches = this._branches[item.id] || [];
this._update_flags(id, false);
for (var i = 0; i < branches.length; i++) {
this._silentStart();
this._deleteTask(branches[i], true);
// add deleted subrow into dataprocessor update list manually
// because silent mode is on
if (this._dp) {
this._dp._ganttMode = "tasks";
this._dp.setUpdated(branches[i],true,"deleted");
}
this._silentEnd();
}
if (!silent && this._dp)
this._dp.setUpdateMode("cell");
while (item.$source.length > 0)
this.deleteLink(item.$source[0]);
while (item.$target.length > 0)
this.deleteLink(item.$target[0]);
delete this._pull[id];
this._move_branch(item, item.parent, null);
if (!silent) {
this.callEvent("onAfterTaskDelete", [id, item]);
this.refreshData();
}
return true;
};
gantt.clearAll = function() {
this._pull = {};
this._branches = {};
this._order = [];
this._order_full = [];
this._lpull = {};
this.refreshData();
this.callEvent("onClear", []);
};
gantt._update_flags = function(oldid, newid){
// TODO: need a proper way to update all possible flags
if (this._lightbox_id == oldid)
this._lightbox_id = newid;
if (this._selected_task == oldid){
this._selected_task = newid;
}
if (this._tasks_dnd.drag && this._tasks_dnd.drag.id == oldid){
this._tasks_dnd.drag.id = newid;
}
};
gantt.changeTaskId = function(oldid, newid) {
var item = this._pull[newid] = this._pull[oldid];
this._pull[newid].id = newid;
delete this._pull[oldid];
for (var id in this._pull) {
if (this._pull[id].parent == oldid)
this._pull[id].parent = newid;
}
this._update_flags(oldid, newid);
this._replace_branch_child(item.parent, oldid, newid);
this.callEvent("onTaskIdChange", [oldid, newid]);
};
gantt._get_duration_unit = function(){
return (gantt._get_line(this.config.duration_unit)*1000) || this.config.duration_unit;
};
gantt._get_safe_type = function(type){
for(var i in this.config.types){
if(this.config.types[i] == type){
return type;
}
}
return gantt.config.types.task;
};
gantt._get_type_name = function(type_value){
for(var i in this.config.types){
if(this.config.types[i] == type_value){
return i;
}
}
return "task";
};
gantt.getWorkHours = function(date){
return this._working_time_helper.get_working_hours(date);
};
gantt.setWorkTime = function(config){
this._working_time_helper.set_time(config);
};
gantt.isWorkTime = function(date, unit){
var helper = this._working_time_helper;
return helper.is_working_unit(date, unit || this.config.duration_unit);
};
gantt.getClosestWorkTime = function(config){
var helper = this._working_time_helper;
if(config instanceof Date){
config = {
date:config
};
}
config.dir = config.dir || 'any';
config.unit = config.unit || this.config.duration_unit;
return helper.get_closest_worktime(config);
};
gantt.calculateDuration = function(start_date, end_date){
var helper = this._working_time_helper;
return helper.get_work_units_between(start_date, end_date, this.config.duration_unit, this.config.duration_step);
};
gantt.calculateEndDate = function(start, duration, unit){
var helper = this._working_time_helper;
var mult = duration >= 0 ? 1 : -1;
return helper.add_worktime(start, Math.abs(duration), this.config.duration_unit, mult*this.config.duration_step);
};
gantt._init_task = function(task){
if (!dhtmlx.defined(task.id))
task.id = dhtmlx.uid();
if(task.start_date)
task.start_date = gantt.date.parseDate(task.start_date, "xml_date");
if(task.end_date)
task.end_date = gantt.date.parseDate(task.end_date, "xml_date");
if(task.start_date){
if(!task.end_date && task.duration){
task.end_date = this.calculateEndDate(task.start_date, task.duration);
}
}
if(gantt.config.work_time && gantt.config.correct_work_time){
if(task.start_date)
task.start_date = gantt.getClosestWorkTime(task.start_date);
if(task.end_date)
task.end_date = gantt.getClosestWorkTime(task.end_date);
}
gantt._init_task_timing(task);
task.$source = [];
task.$target = [];
task.parent = task.parent || this.config.root_id;
task.$open = dhtmlx.defined(task.open) ? task.open : false;
task.$level = this._item_level(task);
return task;
};
gantt._init_task_timing = function(task){
if(task.$rendered_type === undefined){
task.$rendered_type = task.type;
}else if(task.$rendered_type != task.type){
delete task.$no_end;
delete task.$no_start;
task.$rendered_type = task.type;
}
if((task.$no_end === undefined || task.$no_start === undefined) && task.type != this.config.types.milestone){
if(task.type == this.config.types.project){
//project duration is always defined by children duration
task.$no_end = task.$no_start = true;
}else{
//tasks can have fixed duration, children duration(as projects), or one date fixed, and other defined by nested items
task.$no_end = !(task.end_date || task.duration);
task.$no_start = !task.start_date;
}
}
if(task.type == this.config.types.milestone){
task.end_date = task.start_date;
}
if (task.start_date && task.end_date){
task.duration = this.calculateDuration(task.start_date, task.end_date);
}
task.duration = task.duration || 0;
};
gantt._is_flex_task = function(task){
return !!(task.$no_end || task.$no_start);
};
gantt._update_parents = function(taskId, silent){
if(!taskId) return;
var task = this.getTask(taskId);
while(!(task.$no_end || task.$no_start) && task.parent && this.isTaskExists(task.parent)){
task = this.getTask(task.parent);
}
if(task.$no_end){
var max = 0;
this.eachTask(function(child){
if(child.end_date && +child.end_date > +max){
max = new Date(child.end_date);
}
}, task.id);
if(max){
task.end_date = max;
}
}
if(task.$no_start){
var min = Infinity;
this.eachTask(function(child){
if(child.start_date && +child.start_date < +min){
min = new Date(child.start_date);
}
}, task.id);
if(min != Infinity){
task.start_date = min;
}
}
if((task.$no_end || task.$no_start)){
this._init_task_timing(task);
if(!silent)
this.refreshTask(task.id, true);
}
if(task.parent && this.isTaskExists(task.parent)){
this._update_parents(task.parent, silent);
}
};
gantt.isChildOf = function(child_id, parent_id){
if(!this.isTaskExists(child_id))
return false;
if(parent_id === this.config.root_id)
return this.isTaskExists(child_id);
var task = this.getTask(child_id);
while(task && this.isTaskExists(task.parent)){
task = this.getTask(task.parent);
if(task && task.id == parent_id)
return true;
}
return false;
};
gantt._get_closest_date = function(config){
var date = config.date,
steps = config.step,
unit = config.unit;
var upper = gantt.date[unit + "_start"](new Date(this._min_date));
while(+upper < +date){
upper = gantt.date.add(upper, steps, unit);
}
var lower = gantt.date.add(upper, -1*steps, unit);
if(config.dir && config.dir == 'future')
return upper;
if(config.dir && config.dir == 'past')
return lower;
if(Math.abs(date - lower) < Math.abs(upper - date)){
return lower;
}else{
return upper;
}
};
gantt.attachEvent("onBeforeTaskUpdate", function(id, task){
gantt._init_task_timing(task);
return true;
});
gantt.attachEvent("onBeforeTaskAdd", function(id, task){
gantt._init_task_timing(task);
return true;
});
gantt._item_level = function(item) {
var level = 0;
while (item.parent) {
if (!dhtmlx.defined(this._pull[item.parent])) break;
item = this._pull[item.parent];
level++;
}
return level;
};
gantt.sort = function(field, desc, parent) {
var render = !arguments[3];//4th argument to cancel redraw after sorting
if (!dhtmlx.defined(parent)) {
parent = this.config.root_id;
}
if (!dhtmlx.defined(field)) field = "order";
var criteria = (typeof(field) == "string") ? (function(a, b) {
var result = a[field] > b[field];
if (desc) result = !result;
return result ? 1 : -1;
}) : field;
var els = this._branches[parent];
if (els){
var temp = [];
for (var i = els.length - 1; i >= 0; i--)
temp[i] = this._pull[els[i]];
temp.sort(criteria);
for (var i = 0; i < temp.length; i++) {
els[i] = temp[i].id;
this.sort(field, desc, els[i], true);
}
}
if (render) {
this.refreshData();
}
};
gantt.getNext = function(id) {
for (var i = 0; i < this._order.length-1; i++) {
if (this._order[i] == id)
return this._order[i+1];
}
return null;
};
gantt.getPrev = function(id) {
for (var i = 1; i < this._order.length; i++) {
if (this._order[i] == id)
return this._order[i-1];
}
return null;
};
gantt._dp_init = function(dp) {
dp.setTransactionMode("POST", true);
dp.serverProcessor += (dp.serverProcessor.indexOf("?") != -1 ? "&" : "?") + "editing=true";
dp._serverProcessor = dp.serverProcessor;
dp.styles = {
updated:"gantt_updated",
inserted:"gantt_inserted",
deleted:"gantt_deleted",
invalid:"gantt_invalid",
error:"gantt_error",
clear:""
};
dp._methods=["_row_style","setCellTextStyle","_change_id","_delete_task"];
this.attachEvent("onAfterTaskAdd", function(id, item) {
dp._ganttMode = "tasks";
dp.setUpdated(id,true,"inserted");
});
this.attachEvent("onAfterTaskUpdate", function(id, item) {
dp._ganttMode = "tasks";
dp.setUpdated(id,true);
});
this.attachEvent("onAfterTaskDelete", function(id, item) {
dp._ganttMode = "tasks";
dp.setUpdated(id,true,"deleted");
});
this.attachEvent("onAfterLinkUpdate", function(id, item) {
dp._ganttMode = "links";
dp.setUpdated(id, true);
});
this.attachEvent("onAfterLinkAdd", function(id, item) {
dp._ganttMode = "links";
dp.setUpdated(id, true,"inserted");
});
this.attachEvent("onAfterLinkDelete", function(id, item) {
dp._ganttMode = "links";
dp.setUpdated(id, true,"deleted");
});
this.attachEvent("onRowDragEnd", function(id, target) {
dp._ganttMode = "tasks";
this.getTask(id).target = target;
dp.setUpdated(id, true,"order");
});
dp.attachEvent("onBeforeDataSending", function() {
this.serverProcessor = this._serverProcessor + getUrlSymbol(this._serverProcessor) + "gantt_mode=" + this._ganttMode;
return true;
});
dp._getRowData=dhtmlx.bind(function(id, pref) {
var task;
if (dp._ganttMode == "tasks")
task = this.isTaskExists(id) ? this.getTask(id) : { id: id };
else
task = this.isLinkExists(id) ? this.getLink(id) : { id: id };
var data = {};
for (var key in task) {
if (key.substr(0, 1) == "$") continue;
var value = task[key];
if (value instanceof Date)
data[key] = this.templates.xml_format(value);
else
data[key] = value;
}
if(task.$no_start){
task.start_date = "";
task.duration = "";
}
if(task.$no_end){
task.end_date = "";
task.duration = "";
}
data[dp.action_param] = this.getUserData(id, dp.action_param);
return data;
}, this);
this._change_id = dhtmlx.bind(function(oldid, newid) {
if (dp._ganttMode != "tasks")
this.changeLinkId(oldid, newid);
else
this.changeTaskId(oldid, newid);
}, this);
this._row_style = function(row_id, classname){
if (dp._ganttMode != "tasks") return;
var el = gantt.getTaskRowNode(row_id);
if (!el) return;
if (!classname) {
var regexp = / (gantt_updated|gantt_inserted|gantt_deleted|gantt_invalid|gantt_error)/g;
el.className = el.className.replace(regexp, "");
} else
el.className += " " + classname;
};
// fake method for dataprocessor
this._delete_task = function(row_id, node){};
this._dp = dp;
};
gantt.getUserData = function(id, name) {
if (!this.userdata) this.userdata = {};
if (this.userdata[id] && this.userdata[id][name]) return this.userdata[id][name];
return "";
};
gantt.setUserData = function(id, name, value) {
if (!this.userdata) this.userdata = {};
if (!this.userdata[id]) this.userdata[id] = {};
this.userdata[id][name] = value;
};
gantt._init_link = function(link) {
if (!dhtmlx.defined(link.id))
link.id = dhtmlx.uid();
return link;
};
gantt._sync_links = function() {
for (var id in this._pull) {
this._pull[id].$source = [];
this._pull[id].$target = [];
}
for (var id in this._lpull) {
var link = this._lpull[id];
if(this._pull[link.source])
this._pull[link.source].$source.push(id);
if(this._pull[link.target])
this._pull[link.target].$target.push(id);
}
};
gantt.getLink = function(id) {
dhtmlx.assert(this._lpull[id], "Link doesn't exist");
return this._lpull[id];
};
gantt.isLinkExists = function(id) {
return dhtmlx.defined(this._lpull[id]);
};
gantt.addLink = function(link) {
link = this._init_link(link);
if (this.callEvent("onBeforeLinkAdd", [link.id, link])===false) return false;
this._lpull[link.id] = link;
this._sync_links();
this._render_link(link.id);
this.callEvent("onAfterLinkAdd", [link.id, link]);
return link.id;
};
gantt.updateLink = function(id, data) {
if (!dhtmlx.defined(data))
data = this.getLink(id);
if (this.callEvent("onBeforeLinkUpdate", [id, data]) === false) return false;
this._lpull[id] = data;
this._sync_links();
this._render_link(id);
this.callEvent("onAfterLinkUpdate", [id, data]);
return true;
};
gantt.deleteLink = function(id) {
return this._deleteLink(id);
};
gantt._deleteLink = function(id, silent) {
var link = this.getLink(id);
if (!silent && this.callEvent("onBeforeLinkDelete", [id, link])===false) return false;
delete this._lpull[id];
this._sync_links();
this.refreshLink(id);
if (!silent) this.callEvent("onAfterLinkDelete", [id, link]);
return true;
};
gantt.changeLinkId = function(oldid, newid) {
this._lpull[newid] = this._lpull[oldid];
this._lpull[newid].id = newid;
delete this._lpull[oldid];
this._sync_links();
this.callEvent("onLinkIdChange", [oldid, newid]);
};
gantt.getChildren = function(id) {
return dhtmlx.defined(this._branches[id]) ? this._branches[id] : [];
};
gantt.hasChild = function(id) {
return dhtmlx.defined(this._branches[id]);
};
gantt.refreshData = function(){
this._sync_order();
this._render_data();
};
gantt._configure = function(col, data){
for (var key in data)
if (typeof col[key] == "undefined")
col[key] = data[key];
};
gantt._init_skin = function(){
if (!gantt.skin){
var links = document.getElementsByTagName("link");
for (var i = 0; i < links.length; i++) {
var res = links[i].href.match("dhtmlxgantt_([a-z]+).css");
if (res){
gantt.skin = res[1];
break;
}
}
}
if (!gantt.skin) gantt.skin = "terrace";
var skinset = gantt.skins[gantt.skin];
//apply skin related settings
this._configure(gantt.config, skinset.config);
var config = gantt.config.columns;
if (config[1] && typeof config[1].width == "undefined")
config[1].width = skinset._second_column_width;
if (config[2] && typeof config[2].width == "undefined")
config[2].width = skinset._third_column_width;
if (skinset._lightbox_template)
gantt._lightbox_template = skinset._lightbox_template;
gantt._init_skin = function(){};
};
gantt.skins = {};
gantt._lightbox_methods = {};
gantt._lightbox_template="<div class='dhx_cal_ltitle'><span class='dhx_mark'>&nbsp;</span><span class='dhx_time'></span><span class='dhx_title'></span></div><div class='dhx_cal_larea'></div>";
gantt.showLightbox=function(id){
if (!id || this.config.readonly) return;
if (!this.callEvent("onBeforeLightbox",[id])) return;
var task = this.getTask(id);
var box = this.getLightbox(this._get_safe_type(task.type));
this._center_lightbox(box);
this.showCover();
this._fill_lightbox(id,box);
this.callEvent("onLightbox",[id]);
};
gantt._get_timepicker_step = function(){
if(this.config.round_dnd_dates){
var scale = gantt._tasks,
step = (this._get_line(scale.unit) * scale.step)/60;//timepicker step is measured in minutes
if(step >= 60*24){
step = this.config.time_step;
}
return step;
}
return this.config.time_step;
};
gantt.getLabel = function(property, key) {
var sections = this._get_typed_lightbox_config();
for (var i=0; i<sections.length; i++) {
if(sections[i].map_to == property) {
var options = sections[i].options;
for (var j=0; j<options.length; j++) {
if(options[j].key == key) {
return options[j].label;
}
}
}
}
return "";
};
gantt.updateCollection = function(list_name, collection) {
var collection = collection.slice(0);
var list = gantt.serverList(list_name);
if (!list) return false;
list.splice(0, list.length);
list.push.apply(list, collection || []);
gantt.resetLightbox();
};
gantt.getLightboxType = function(){
return this._get_safe_type(this._lightbox_type);
};
gantt.getLightbox = function(type){
if(type === undefined)
type = this.getLightboxType();
if (!this._lightbox || this.getLightboxType() != this._get_safe_type(type)){
this._lightbox_type = this._get_safe_type(type);
var d=document.createElement("DIV");
d.className="dhx_cal_light";
var full_width = this._is_lightbox_timepicker();
if (gantt.config.wide_form || full_width)
d.className+=" dhx_cal_light_wide";
if (full_width) {
gantt.config.wide_form = true;
d.className+=" dhx_cal_light_full";
}
d.style.visibility="hidden";
var html = this._lightbox_template;
var buttons = this.config.buttons_left;
for (var i in buttons)
html+="<div class='dhx_btn_set dhx_left_btn_set "+buttons[i]+"_set'><div dhx_button='1' class='"+buttons[i]+"'></div><div>"+this.locale.labels[buttons[i]]+"</div></div>";
buttons = this.config.buttons_right;
for (var i in buttons)
html+="<div class='dhx_btn_set dhx_right_btn_set "+buttons[i]+"_set' style='float:right;'><div dhx_button='1' class='"+buttons[i]+"'></div><div>"+this.locale.labels[buttons[i]]+"</div></div>";
html+="</div>";
d.innerHTML=html;
if (gantt.config.drag_lightbox){
d.firstChild.onmousedown = gantt._ready_to_dnd;
d.firstChild.onselectstart = function(){ return false; };
d.firstChild.style.cursor = "pointer";
gantt._init_dnd_events();
}
document.body.insertBefore(d,document.body.firstChild);
this._lightbox=d;
var sns = this._get_typed_lightbox_config(type);
html = this._render_sections(sns);
var ds=d.getElementsByTagName("div");
for (var i=0; i<ds.length; i++) {
var t_ds = ds[i];
if (t_ds.className == "dhx_cal_larea") {
t_ds.innerHTML = html;
break;
}
}
//sizes
this.resizeLightbox();
this._init_lightbox_events(this);
d.style.display="none";
d.style.visibility="visible";
}
return this._lightbox;
};
gantt._render_sections = function(sns) {
var html="";
for (var i=0; i < sns.length; i++) {
var block=this.form_blocks[sns[i].type];
if (!block) continue; //ignore incorrect blocks
sns[i].id="area_"+dhtmlx.uid();
var display = sns[i].hidden ? " style='display:none'" : "";
var button = "";
if (sns[i].button){
button = "<div class='dhx_custom_button' index='"+i+"'><div class='dhx_custom_button_"+sns[i].button+"'></div><div>"+this.locale.labels["button_"+sns[i].button]+"</div></div>";
}
if (this.config.wide_form){
html+="<div class='dhx_wrap_section' " + display+">";
}
html+="<div id='"+sns[i].id+"' class='dhx_cal_lsection'>"+button+this.locale.labels["section_"+sns[i].name]+"</div>"+block.render.call(this,sns[i]);
html+="</div>";
}
return html;
};
gantt.resizeLightbox=function(){
var d = this._lightbox;
if (!d) return;
var con = d.childNodes[1];
con.style.height="0px";
con.style.height=con.scrollHeight+"px";
d.style.height=con.scrollHeight+this.config.lightbox_additional_height+"px";
con.style.height=con.scrollHeight+"px"; //it is incredible , how ugly IE can be
};
gantt._center_lightbox = function(box) {
if (box){
box.style.display="block";
var scroll_top = window.pageYOffset||document.body.scrollTop||document.documentElement.scrollTop;
var scroll_left = window.pageXOffset||document.body.scrollLeft||document.documentElement.scrollLeft;
var view_height = window.innerHeight||document.documentElement.clientHeight;
if(scroll_top) // if vertical scroll on window
box.style.top=Math.round(scroll_top+Math.max((view_height-box.offsetHeight)/2, 0))+"px";
else // vertical scroll on body
box.style.top=Math.round(Math.max(((view_height-box.offsetHeight)/2), 0) + 9)+"px"; // +9 for compatibility with auto tests
// not quite accurate but used for compatibility reasons
if(document.documentElement.scrollWidth > document.body.offsetWidth) // if horizontal scroll on the window
box.style.left=Math.round(scroll_left+(document.body.offsetWidth-box.offsetWidth)/2)+"px";
else // horizontal scroll on the body
box.style.left=Math.round((document.body.offsetWidth-box.offsetWidth)/2)+"px";
}
};
gantt.showCover = function(){
if(this._cover) return;
this._cover=document.createElement("DIV");
this._cover.className="dhx_cal_cover";
var _document_height = ((document.height !== undefined) ? document.height : document.body.offsetHeight);
var _scroll_height = ((document.documentElement) ? document.documentElement.scrollHeight : 0);
this._cover.style.height = Math.max(_document_height, _scroll_height) + 'px';
document.body.appendChild(this._cover);
};
gantt._init_lightbox_events = function(){
gantt.lightbox_events = {};
gantt.lightbox_events["dhx_save_btn"] = function(e) {
gantt._save_lightbox();
};
gantt.lightbox_events["dhx_delete_btn"] = function(e) {
if(!gantt.callEvent("onLightboxDelete", [gantt._lightbox_id]))
return;
gantt.$click.buttons["delete"](gantt._lightbox_id);
};
gantt.lightbox_events["dhx_cancel_btn"] = function(e) {
gantt._cancel_lightbox();
};
gantt.lightbox_events["default"] = function(e, src) {
if (src.getAttribute("dhx_button")) {
gantt.callEvent("onLightboxButton", [src.className, src, e]);
} else {
var index, block, sec;
if (src.className.indexOf("dhx_custom_button") != -1) {
if (src.className.indexOf("dhx_custom_button_") != -1) {
index = src.parentNode.getAttribute("index");
sec = src.parentNode.parentNode;
} else {
index = src.getAttribute("index");
sec = src.parentNode;
src = src.firstChild;
}
}
var sections = gantt._get_typed_lightbox_config();
if (index) {
block = gantt.form_blocks[sections[index].type];
block.button_click(index, src, sec, sec.nextSibling);
}
}
};
dhtmlxEvent(gantt.getLightbox(), "click", function(e) {
e = e || window.event;
var src = e.target ? e.target : e.srcElement;
if (!src.className)
src = src.previousSibling;
if (src && src.className && src.className.indexOf("dhx_btn_set") === 0)
src = src.firstChild;
if (src && src.className) {
var func = dhtmlx.defined(gantt.lightbox_events[src.className]) ? gantt.lightbox_events[src.className] : gantt.lightbox_events["default"];
return func(e, src);
}
return false;
});
gantt.getLightbox().onkeydown=function(e){
switch((e||event).keyCode){
case gantt.keys.edit_save:
if ((e||event).shiftKey) return;
gantt._save_lightbox();
break;
case gantt.keys.edit_cancel:
gantt._cancel_lightbox();
break;
default:
break;
}
};
};
gantt._cancel_lightbox=function(){
var task = this.getLightboxValues();
this.callEvent("onLightboxCancel",[this._lightbox_id, task.$new]);
if(task.$new){
this._deleteTask(task.id, true);
this.refreshData();
}
this.hideLightbox();
};
gantt._save_lightbox=function(){
var task = this.getLightboxValues();
if(!this.callEvent("onLightboxSave", [this._lightbox_id, task, !!task.$new]))
return;
if (task.$new){
delete task.$new;
this.addTask(task);
}else{
dhtmlx.mixin(this.getTask(task.id), task, true);
this.updateTask(task.id);
}
this.refreshData();
// TODO: do we need any blockable events here to prevent closing lightbox?
this.hideLightbox();
};
gantt.getLightboxValues=function(){
var task = dhtmlx.mixin({}, this.getTask(this._lightbox_id));
var sns = this._get_typed_lightbox_config();
for (var i=0; i < sns.length; i++) {
var node = document.getElementById(sns[i].id);
node=(node?node.nextSibling:node);
var block=this.form_blocks[sns[i].type];
var res=block.get_value.call(this,node,task, sns[i]);
if (sns[i].map_to!="auto")
task[sns[i].map_to]=res;
}
return task;
};
gantt.hideLightbox=function(id){
var box = this.getLightbox();
if (box) box.style.display="none";
this._lightbox_id=null;
this.hideCover();
this.callEvent("onAfterLightbox",[]);
};
gantt.hideCover=function(){
if (this._cover)
this._cover.parentNode.removeChild(this._cover);
this._cover=null;
};
gantt.resetLightbox = function(){
if (gantt._lightbox && !gantt._custom_lightbox)
gantt._lightbox.parentNode.removeChild(gantt._lightbox);
gantt._lightbox = null;
};
gantt._set_lightbox_values = function(data, box){
var task = data;
var s = box.getElementsByTagName("span");
if (gantt.templates.lightbox_header) {
s[1].innerHTML = "";
s[2].innerHTML = gantt.templates.lightbox_header(task.start_date, task.end_date, task);
} else {
s[1].innerHTML = this.templates.task_time(task.start_date, task.end_date, task);
s[2].innerHTML = (this.templates.task_text(task.start_date, task.end_date, task) || "").substr(0, 70); //IE6 fix
}
var sns = this._get_typed_lightbox_config(this.getLightboxType());
for (var i = 0; i < sns.length; i++) {
var section = sns[i];
if(!this.form_blocks[section.type]){
continue;//skip incorrect sections, same check is done during rendering
}
var node = document.getElementById(section.id).nextSibling;
var block = this.form_blocks[section.type];
var value = dhtmlx.defined(task[section.map_to]) ? task[section.map_to] : section.default_value;
block.set_value.call(this, node, value, task, section);
if (section.focus)
block.focus.call(this, node);
}
if(data.id)
gantt._lightbox_id = data.id;
};
gantt._fill_lightbox = function(id, box) {
var task = this.getTask(id);
this._set_lightbox_values(task, box);
};
gantt.getLightboxSection = function(name){
var config = this._get_typed_lightbox_config();
var i =0;
for (i; i < config.length; i++)
if (config[i].name == name)
break;
var section = config[i];
if (!this._lightbox)
this.getLightbox();
var header = document.getElementById(section.id);
var node = header.nextSibling;
var result = {
section: section,
header: header,
node: node,
getValue:function(ev){
return this.form_blocks[section.type].get_value(node, (ev||{}), section);
},
setValue:function(value, ev){
return this.form_blocks[section.type].set_value(node, value, (ev||{}), section);
}
};
var handler = this._lightbox_methods["get_"+section.type+"_control"];
return handler?handler(result):result;
};
gantt._lightbox_methods.get_template_control = function(result) {
result.control = result.node;
return result;
};
gantt._lightbox_methods.get_select_control = function(result) {
result.control = result.node.getElementsByTagName('select')[0];
return result;
};
gantt._lightbox_methods.get_textarea_control = function(result) {
result.control = result.node.getElementsByTagName('textarea')[0];
return result;
};
gantt._lightbox_methods.get_time_control = function(result) {
result.control = result.node.getElementsByTagName('select'); // array
return result;
};
gantt._init_dnd_events = function(){
dhtmlxEvent(document.body, "mousemove", gantt._move_while_dnd);
dhtmlxEvent(document.body, "mouseup", gantt._finish_dnd);
gantt._init_dnd_events = function(){};
};
gantt._move_while_dnd = function(e){
if (gantt._dnd_start_lb){
if (!document.dhx_unselectable){
document.body.className += " dhx_unselectable";
document.dhx_unselectable = true;
}
var lb = gantt.getLightbox();
var now = (e&&e.target)?[e.pageX, e.pageY]:[event.clientX, event.clientY];
lb.style.top = gantt._lb_start[1]+now[1]-gantt._dnd_start_lb[1]+"px";
lb.style.left = gantt._lb_start[0]+now[0]-gantt._dnd_start_lb[0]+"px";
}
};
gantt._ready_to_dnd = function(e){
var lb = gantt.getLightbox();
gantt._lb_start = [parseInt(lb.style.left,10), parseInt(lb.style.top,10)];
gantt._dnd_start_lb = (e&&e.target)?[e.pageX, e.pageY]:[event.clientX, event.clientY];
};
gantt._finish_dnd = function(){
if (gantt._lb_start){
gantt._lb_start = gantt._dnd_start_lb = false;
document.body.className = document.body.className.replace(" dhx_unselectable","");
document.dhx_unselectable = false;
}
};
gantt._focus = function(node, select){
if (node && node.focus){
if (gantt.config.touch){
//do not focus editor, to prevent auto-zoom
} else {
if (select && node.select) node.select();
node.focus();
}
}
};
gantt.form_blocks={
getTimePicker: function(sns, hidden) {
var time_format = sns.time_format;
if (!time_format) {
// default order
var time_format = ["%d", "%m", "%Y"];
if(gantt._get_line(gantt._tasks.unit) < gantt._get_line("day")){
time_format.push("%H:%i");
}
}
// map: default order => real one
sns._time_format_order = { size:0 };
var cfg = this.config;
var dt = this.date.date_part(new Date(gantt._min_date.valueOf()));
var last = 24*60, first = 0;
if(gantt.config.limit_time_select){
last = 60*cfg.last_hour+1;
first = 60*cfg.first_hour;
dt.setHours(cfg.first_hour);
}
var html = "";
for (var p = 0; p < time_format.length; p++) {
var time_option = time_format[p];
// adding spaces between selects
if (p > 0) {
html += " ";
}
var options = '';
switch (time_option) {
case "%Y":
sns._time_format_order[2] = p;
sns._time_format_order.size++;
//year
var year = dt.getFullYear()-5; //maybe take from config?
for (var i=0; i < 10; i++)
options+="<option value='"+(year+i)+"'>"+(year+i)+"</option>";
break;
case "%m":
sns._time_format_order[1] = p;
sns._time_format_order.size++;
//month
for (var i=0; i < 12; i++)
options+="<option value='"+i+"'>"+this.locale.date.month_full[i]+"</option>";
break;
case "%d":
sns._time_format_order[0] = p;
sns._time_format_order.size++;
//days
for (var i=1; i < 32; i++)
options+="<option value='"+i+"'>"+i+"</option>";
break;
case "%H:%i":
var last = 24*60, first = 0;
sns._time_format_order[3] = p;
sns._time_format_order.size++;
//hours
var i = first;
var tdate = dt.getDate();
sns._time_values = [];
while(i<last){
var time=this.templates.time_picker(dt);
options+="<option value='"+i+"'>"+time+"</option>";
sns._time_values.push(i);
dt.setTime(dt.valueOf()+this._get_timepicker_step()*60*1000);
var diff = (dt.getDate()!=tdate)?1:0; // moved or not to the next day
i=diff*24*60+dt.getHours()*60+dt.getMinutes();
}
break;
default:
break;
}
if(options){
var readonly = sns.readonly ? "disabled='disabled'" : "";
var display = hidden ? " style='display:none'" : "";
html += "<select "+readonly+display +">"+options+"</select>";
}
}
return html;
},
_fill_lightbox_select: function (s,i,d,map,cfg) {
s[i+map[0]].value=d.getDate();
s[i+map[1]].value=d.getMonth();
s[i+map[2]].value=d.getFullYear();
if (dhtmlx.defined(map[3])) {
var v = d.getHours()*60+ d.getMinutes();
v = Math.round(v/gantt._get_timepicker_step())*gantt._get_timepicker_step();
s[i+map[3]].value= v;
}
},
template:{
render: function(sns){
var height=(sns.height||"30")+"px";
return "<div class='dhx_cal_ltext dhx_cal_template' style='height:"+height+";'></div>";
},
set_value:function(node,value,ev,config){
node.innerHTML = value||"";
},
get_value:function(node,ev,config){
return node.innerHTML||"";
},
focus: function(node){
}
},
textarea:{
render:function(sns){
var height=(sns.height||"130")+"px";
return "<div class='dhx_cal_ltext' style='height:"+height+";'><textarea></textarea></div>";
},
set_value:function(node,value,ev){
node.firstChild.value=value||"";
},
get_value:function(node,ev){
return node.firstChild.value;
},
focus:function(node){
var a=node.firstChild; gantt._focus(a, true);
}
},
select:{
render:function(sns){
var height=(sns.height||"23")+"px";
var html="<div class='dhx_cal_ltext' style='height:"+height+";'><select style='width:100%;'>";
for (var i=0; i < sns.options.length; i++)
html+="<option value='"+sns.options[i].key+"'>"+sns.options[i].label+"</option>";
html+="</select></div>";
return html;
},
set_value:function(node,value,ev,sns){
var select = node.firstChild;
if (!select._dhx_onchange && sns.onchange) {
select.onchange = sns.onchange;
select._dhx_onchange = true;
}
if (typeof value == "undefined")
value = (select.options[0]||{}).value;
select.value=value||"";
},
get_value:function(node,ev){
return node.firstChild.value;
},
focus:function(node){
var a=node.firstChild; gantt._focus(a, true);
}
},
time:{
render:function(sns) {
var time = this.form_blocks.getTimePicker.call(this, sns);
var parts = ["<div style='height:30px;padding-top:0px;font-size:inherit;text-align:center;' class='dhx_section_time'>"];
parts.push(time);
if(sns.single_date){
time = this.form_blocks.getTimePicker.call(this, sns, true);
parts.push("<span></span>");
}else{
parts.push("<span style='font-weight:normal; font-size:10pt;'> &nbsp;&ndash;&nbsp; </span>");
}
parts.push(time);
parts.push("</div>");
return parts.join('');
},
set_value:function(node,value,ev,config){
var cfg = this.config;
var s=node.getElementsByTagName("select");
var map = config._time_format_order;
var map_size = config._time_format_size;
if(cfg.auto_end_date) {
var _update_lightbox_select = function() {
var start_date = new Date(s[map[2]].value,s[map[1]].value,s[map[0]].value,0,0);
var end_date = gantt.calculateEndDate(start_date, 1);
this.form_blocks._fill_lightbox_select(s,map.size, end_date,map,cfg);
};
for(var i=0; i<4; i++) {
s[i].onchange = _update_lightbox_select;
}
}
this.form_blocks._fill_lightbox_select(s,0,ev.start_date,map,cfg);
this.form_blocks._fill_lightbox_select(s,map.size,ev.end_date,map,cfg);
},
get_value:function(node, ev, config) {
var s=node.getElementsByTagName("select");
var map = config._time_format_order;
var hours = 0, minutes = 0;
if (dhtmlx.defined(map[3])) {
var time = parseInt(s[map[3]].value, 10);
hours = Math.floor(time/60);
minutes = time%60;
}
ev.start_date=new Date(s[map[2]].value,s[map[1]].value,s[map[0]].value,hours,minutes);
hours = minutes = 0;
if (dhtmlx.defined(map[3])) {
var time = parseInt(s[map.size+map[3]].value, 10);
hours = Math.floor(time/60);
minutes = time%60;
}
ev.end_date=new Date(s[map[2]+map.size].value,s[map[1]+map.size].value,s[map[0]+map.size].value,hours,minutes);
if (ev.end_date<=ev.start_date)
ev.end_date=gantt.date.add(ev.start_date, gantt._get_timepicker_step(),"minute");
return {
start_date: new Date(ev.start_date),
end_date: new Date(ev.end_date)
};
},
focus:function(node){
gantt._focus(node.getElementsByTagName("select")[0]);
}
},
duration:{
render:function(sns) {
var time = this.form_blocks.getTimePicker.call(this, sns);
time = "<div class='dhx_time_selects'>"+time+"</div>";
var label = this.locale.labels[this.config.duration_unit + "s"];
var singleDate = sns.single_date ? ' style="display:none"' : "";
var readonly = sns.readonly ? " disabled='disabled'" : "";
var duration = "<div class='dhx_gantt_duration' "+singleDate+">" +
"<input type='button' class='dhx_gantt_duration_dec' value='-'"+readonly+">" +
"<input type='text' value='5' class='dhx_gantt_duration_value'"+readonly+">" +
"<input type='button' class='dhx_gantt_duration_inc' value='+'"+readonly+"> " + label + " <span></span>" +
"</div>";
var html = "<div style='height:30px;padding-top:0px;font-size:inherit;' class='dhx_section_time'>"+time+" "+duration+"</div>";
return html;
},
set_value:function(node,value,ev,config){
var cfg = this.config;
var s=node.getElementsByTagName("select");
var inps = node.getElementsByTagName("input");
var duration = inps[1];
var btns=[inps[0],inps[2]];
var endspan = node.getElementsByTagName("span")[0];
var map = config._time_format_order;
function _calc_date() {
var start_date = gantt.form_blocks.duration._get_start_date.call(gantt, node ,config);
var duration = gantt.form_blocks.duration._get_duration.call(gantt, node ,config);
var end_date = gantt.calculateEndDate(start_date, duration);
endspan.innerHTML = gantt.templates.task_date(end_date);
}
function _change_duration(step) {
var value = duration.value;
value = parseInt(value, 10);
if (window.isNaN(value))
value = 0;
value+=step;
if (value < 1) value = 1;
duration.value = value;
_calc_date();
}
btns[0].onclick = dhtmlx.bind(function() { _change_duration(-1*this.config.duration_step); }, this);
btns[1].onclick = dhtmlx.bind(function() { _change_duration(1*this.config.duration_step); }, this);
s[0].onchange = _calc_date;
s[1].onchange = _calc_date;
s[2].onchange = _calc_date;
if (s[3]) s[3].onchange = _calc_date;
duration.onkeydown = dhtmlx.bind(function(e) {
e = e || window.event;
// up
var code = (e.charCode || e.keyCode || e.which);
if (code == 40) {
_change_duration(-1*this.config.duration_step);
return false;
}
// down
if (code == 38) {
_change_duration(1*this.config.duration_step);
return false;
}
window.setTimeout(function(e) {
_calc_date();
}, 1);
}, this);
duration.onchange = dhtmlx.bind(function(e) { _calc_date(); }, this);
this.form_blocks._fill_lightbox_select(s,0,ev.start_date,map,cfg);
var final_value;
if (!ev.end_date)
final_value = ev.duration;
else
final_value = gantt.calculateDuration(ev.start_date, ev.end_date);
final_value = Math.round(final_value);
duration.value = final_value;
_calc_date();
},
_get_start_date: function(node, config) {
var s=node.getElementsByTagName("select");
var map = config._time_format_order;
var hours = 0;
var minutes = 0;
if (dhtmlx.defined(map[3])) {
var time = parseInt(s[map[3]].value, 10);
hours = Math.floor(time/60);
minutes = time%60;
}
return new Date(s[map[2]].value,s[map[1]].value,s[map[0]].value,hours,minutes);
},
_get_duration: function(node, config) {
var duration = node.getElementsByTagName("input")[1];
duration = parseInt(duration.value, 10);
if (window.isNaN(duration)) duration = 1;
if (duration < 0) duration *= -1;
return duration;
},
get_value:function(node, ev, config) {
ev.start_date = this.form_blocks.duration._get_start_date(node, config);
var duration = this.form_blocks.duration._get_duration(node, config);
ev.end_date = this.calculateEndDate(ev.start_date, duration);
ev.duration = duration;
return {
start_date: new Date(ev.start_date),
end_date: new Date(ev.end_date)
};
},
focus:function(node){
gantt._focus(node.getElementsByTagName("select")[0]);
}
},
typeselect : {
render : function(sns){
var types = gantt.config.types,
locale = gantt.locale.labels,
options = [];
for(var i in types){
options.push({key: types[i], label:locale["type_" + i]});
}
sns.options = options;
var oldOnChange = sns.onchange;
sns.onchange = function(){
var tId = gantt.getState().lightbox;
gantt.changeLightboxType(this.value);
if(typeof oldOnChange == 'function'){
oldOnChange.apply(this, arguments);
}
};
return gantt.form_blocks.select.render.apply(this, arguments);
},
set_value:function(){
return gantt.form_blocks.select.set_value.apply(this, arguments);
},
get_value:function(){
return gantt.form_blocks.select.get_value.apply(this, arguments);
},
focus:function(){
return gantt.form_blocks.select.focus.apply(this, arguments);
}
},
parent: {
_filter : function(options, config, item_id){
var filter = config.filter || function(){ return true;};
options = options.slice(0);
for(var i=0; i < options.length; i++){
var task = options[i];
if(task.id == item_id || gantt.isChildOf(task.id, item_id) || filter(task.id, task) === false){
options.splice(i, 1);
i--;
}
}
return options;
},
_display : function(config, item_id){
var tasks = [],
options = [];
if(item_id){
tasks = gantt.getTaskByTime();
if(config.allow_root){
tasks.unshift({id:gantt.config.root_id, text:config.root_label || ""});
}
tasks = this._filter(tasks, config, item_id);
if(config.sort){
tasks.sort(config.sort);
}
}
var text = config.template || gantt.templates.task_text;
for(var i = 0; i < tasks.length; i++){
var label = text.apply(gantt, [tasks[i].start_date, tasks[i].end_date, tasks[i]]);
if(label === undefined){
label = "";
}
options.push({
key: tasks[i].id,
label: label
});
}
config.options = options;
config.map_to = config.map_to || "parent";
return gantt.form_blocks.select.render.apply(this, arguments);
},
render : function(sns){
return gantt.form_blocks.parent._display(sns, false);
},
set_value:function(node,value,ev,config){
var tmpDom = document.createElement("div");
tmpDom.innerHTML = gantt.form_blocks.parent._display(config, ev.id);
var newOptions = tmpDom.removeChild(tmpDom.firstChild);
node.onselect = null;
node.parentNode.replaceChild(newOptions, node);
return gantt.form_blocks.select.set_value.apply(this, [newOptions,value,ev,config]);
},
get_value:function(){
return gantt.form_blocks.select.get_value.apply(this, arguments);
},
focus:function(){
return gantt.form_blocks.select.focus.apply(this, arguments);
}
}
};
gantt._is_lightbox_timepicker = function() {
var s = this._get_typed_lightbox_config();
for (var i = 0; i < s.length; i++)
if (s[i].name == "time" && s[i].type == "time")
return true;
return false;
};
gantt._dhtmlx_confirm = function(message, title, callback, ok) {
if (!message)
return callback();
var opts = { text: message };
if (title)
opts.title = title;
if(ok){
opts.ok = ok;
}
if (callback) {
opts.callback = function(result) {
if (result)
callback();
};
}
dhtmlx.confirm(opts);
};
gantt._get_typed_lightbox_config = function(type){
if(type === undefined){
type = this.getLightboxType();
}
var field = this._get_type_name(type);
if(gantt.config.lightbox[field+"_sections"]){
return gantt.config.lightbox[field+"_sections"];
}else{
return gantt.config.lightbox.sections;
}
};
gantt._silent_redraw_lightbox = function(type){
var oldType = this.getLightboxType();
if(this.getState().lightbox){
var taskId = this.getState().lightbox;
var formData = this.getLightboxValues(),
task = dhtmlx.copy(this.getTask(taskId));
this.resetLightbox();
var updTask = dhtmlx.mixin(task, formData, true);
var box = this.getLightbox(type ? type : undefined);
this._set_lightbox_values(updTask, box);
this._center_lightbox(this.getLightbox());
this.callEvent("onLightboxChange", [oldType, this.getLightboxType()]);
}else{
this.resetLightbox();
this.getLightbox(type ? type : undefined);
}
this.callEvent("onLightboxChange", [oldType, this.getLightboxType()]);
};
/**
* @desc: constructor, data processor object
* @param: serverProcessorURL - url used for update
* @type: public
*/
function dataProcessor(serverProcessorURL){
this.serverProcessor = serverProcessorURL;
this.action_param="!nativeeditor_status";
this.object = null;
this.updatedRows = []; //ids of updated rows
this.autoUpdate = true;
this.updateMode = "cell";
this._tMode="GET";
this.post_delim = "_";
this._waitMode=0;
this._in_progress={};//?
this._invalid={};
this.mandatoryFields=[];
this.messages=[];
this.styles={
updated:"font-weight:bold;",
inserted:"font-weight:bold;",
deleted:"text-decoration : line-through;",
invalid:"background-color:FFE0E0;",
invalid_cell:"border-bottom:2px solid red;",
error:"color:red;",
clear:"font-weight:normal;text-decoration:none;"
};
this.enableUTFencoding(true);
dhtmlxEventable(this);
return this;
}
dataProcessor.prototype={
/**
* @desc: select GET or POST transaction model
* @param: mode - GET/POST
* @param: total - true/false - send records row by row or all at once (for grid only)
* @type: public
*/
setTransactionMode:function(mode,total){
this._tMode=mode;
this._tSend=total;
},
escape:function(data){
if (this._utf)
return encodeURIComponent(data);
else
return escape(data);
},
/**
* @desc: allows to set escaping mode
* @param: true - utf based escaping, simple - use current page encoding
* @type: public
*/
enableUTFencoding:function(mode){
this._utf=convertStringToBoolean(mode);
},
/**
* @desc: allows to define, which column may trigger update
* @param: val - array or list of true/false values
* @type: public
*/
setDataColumns:function(val){
this._columns=(typeof val == "string")?val.split(","):val;
},
/**
* @desc: get state of updating
* @returns: true - all in sync with server, false - some items not updated yet.
* @type: public
*/
getSyncState:function(){
return !this.updatedRows.length;
},
/**
* @desc: enable/disable named field for data syncing, will use column ids for grid
* @param: mode - true/false
* @type: public
*/
enableDataNames:function(mode){
this._endnm=convertStringToBoolean(mode);
},
/**
* @desc: enable/disable mode , when only changed fields and row id send to the server side, instead of all fields in default mode
* @param: mode - true/false
* @type: public
*/
enablePartialDataSend:function(mode){
this._changed=convertStringToBoolean(mode);
},
/**
* @desc: set if rows should be send to server automaticaly
* @param: mode - "row" - based on row selection changed, "cell" - based on cell editing finished, "off" - manual data sending
* @type: public
*/
setUpdateMode:function(mode,dnd){
this.autoUpdate = (mode=="cell");
this.updateMode = mode;
this.dnd=dnd;
},
ignore:function(code,master){
this._silent_mode=true;
code.call(master||window);
this._silent_mode=false;
},
/**
* @desc: mark row as updated/normal. check mandatory fields,initiate autoupdate (if turned on)
* @param: rowId - id of row to set update-status for
* @param: state - true for "updated", false for "not updated"
* @param: mode - update mode name
* @type: public
*/
setUpdated:function(rowId,state,mode){
if (this._silent_mode) return;
var ind=this.findRow(rowId);
mode=mode||"updated";
var existing = this.obj.getUserData(rowId,this.action_param);
if (existing && mode == "updated") mode=existing;
if (state){
this.set_invalid(rowId,false); //clear previous error flag
this.updatedRows[ind]=rowId;
this.obj.setUserData(rowId,this.action_param,mode);
if (this._in_progress[rowId])
this._in_progress[rowId]="wait";
} else{
if (!this.is_invalid(rowId)){
this.updatedRows.splice(ind,1);
this.obj.setUserData(rowId,this.action_param,"");
}
}
//clear changed flag
if (!state)
this._clearUpdateFlag(rowId);
this.markRow(rowId,state,mode);
if (state && this.autoUpdate) this.sendData(rowId);
},
_clearUpdateFlag:function(id){},
markRow:function(id,state,mode){
var str="";
var invalid=this.is_invalid(id);
if (invalid){
str=this.styles[invalid];
state=true;
}
if (this.callEvent("onRowMark",[id,state,mode,invalid])){
//default logic
str=this.styles[state?mode:"clear"]+str;
this.obj[this._methods[0]](id,str);
if (invalid && invalid.details){
str+=this.styles[invalid+"_cell"];
for (var i=0; i < invalid.details.length; i++)
if (invalid.details[i])
this.obj[this._methods[1]](id,i,str);
}
}
},
getState:function(id){
return this.obj.getUserData(id,this.action_param);
},
is_invalid:function(id){
return this._invalid[id];
},
set_invalid:function(id,mode,details){
if (details) mode={value:mode, details:details, toString:function(){ return this.value.toString(); }};
this._invalid[id]=mode;
},
/**
* @desc: check mandatory fields and varify values of cells, initiate update (if specified)
* @param: rowId - id of row to set update-status for
* @type: public
*/
checkBeforeUpdate:function(rowId){
return true;
},
/**
* @desc: send row(s) values to server
* @param: rowId - id of row which data to send. If not specified, then all "updated" rows will be send
* @type: public
*/
sendData:function(rowId){
if (this._waitMode && (this.obj.mytype=="tree" || this.obj._h2)) return;
if (this.obj.editStop) this.obj.editStop();
if(typeof rowId == "undefined" || this._tSend) return this.sendAllData();
if (this._in_progress[rowId]) return false;
this.messages=[];
if (!this.checkBeforeUpdate(rowId) && this.callEvent("onValidationError",[rowId,this.messages])) return false;
this._beforeSendData(this._getRowData(rowId),rowId);
},
_beforeSendData:function(data,rowId){
if (!this.callEvent("onBeforeUpdate",[rowId,this.getState(rowId),data])) return false;
this._sendData(data,rowId);
},
serialize:function(data, id){
if (typeof data == "string")
return data;
if (typeof id != "undefined")
return this.serialize_one(data,"");
else{
var stack = [];
var keys = [];
for (var key in data)
if (data.hasOwnProperty(key)){
stack.push(this.serialize_one(data[key],key+this.post_delim));
keys.push(key);
}
stack.push("ids="+this.escape(keys.join(",")));
if (dhtmlx.security_key)
stack.push("dhx_security="+dhtmlx.security_key);
return stack.join("&");
}
},
serialize_one:function(data, pref){
if (typeof data == "string")
return data;
var stack = [];
for (var key in data)
if (data.hasOwnProperty(key))
stack.push(this.escape((pref||"")+key)+"="+this.escape(data[key]));
return stack.join("&");
},
_sendData:function(a1,rowId){
if (!a1) return; //nothing to send
if (!this.callEvent("onBeforeDataSending",rowId?[rowId,this.getState(rowId),a1]:[null, null, a1])) return false;
if (rowId)
this._in_progress[rowId]=(new Date()).valueOf();
var a2=new dtmlXMLLoaderObject(this.afterUpdate,this,true);
var a3 = this.serverProcessor+(this._user?(getUrlSymbol(this.serverProcessor)+["dhx_user="+this._user,"dhx_version="+this.obj.getUserData(0,"version")].join("&")):"");
if (this._tMode!="POST")
a2.loadXML(a3+((a3.indexOf("?")!=-1)?"&":"?")+this.serialize(a1,rowId));
else
a2.loadXML(a3,true,this.serialize(a1,rowId));
this._waitMode++;
},
sendAllData:function(){
if (!this.updatedRows.length) return;
this.messages=[]; var valid=true;
for (var i=0; i<this.updatedRows.length; i++)
valid&=this.checkBeforeUpdate(this.updatedRows[i]);
if (!valid && !this.callEvent("onValidationError",["",this.messages])) return false;
if (this._tSend)
this._sendData(this._getAllData());
else
for (var i=0; i<this.updatedRows.length; i++)
if (!this._in_progress[this.updatedRows[i]]){
if (this.is_invalid(this.updatedRows[i])) continue;
this._beforeSendData(this._getRowData(this.updatedRows[i]),this.updatedRows[i]);
if (this._waitMode && (this.obj.mytype=="tree" || this.obj._h2)) return; //block send all for tree
}
},
_getAllData:function(rowId){
var out={};
var has_one = false;
for(var i=0;i<this.updatedRows.length;i++){
var id=this.updatedRows[i];
if (this._in_progress[id] || this.is_invalid(id)) continue;
if (!this.callEvent("onBeforeUpdate",[id,this.getState(id)])) continue;
out[id]=this._getRowData(id,id+this.post_delim);
has_one = true;
this._in_progress[id]=(new Date()).valueOf();
}
return has_one?out:null;
},
/**
* @desc: specify column which value should be varified before sending to server
* @param: ind - column index (0 based)
* @param: verifFunction - function (object) which should verify cell value (if not specified, then value will be compared to empty string). Two arguments will be passed into it: value and column name
* @type: public
*/
setVerificator:function(ind,verifFunction){
this.mandatoryFields[ind] = verifFunction||(function(value){return (value !== "");});
},
/**
* @desc: remove column from list of those which should be verified
* @param: ind - column Index (0 based)
* @type: public
*/
clearVerificator:function(ind){
this.mandatoryFields[ind] = false;
},
findRow:function(pattern){
var i=0;
for(i=0;i<this.updatedRows.length;i++)
if(pattern==this.updatedRows[i]) break;
return i;
},
/**
* @desc: define custom actions
* @param: name - name of action, same as value of action attribute
* @param: handler - custom function, which receives a XMl response content for action
* @type: private
*/
defineAction:function(name,handler){
if (!this._uActions) this._uActions=[];
this._uActions[name]=handler;
},
/**
* @desc: used in combination with setOnBeforeUpdateHandler to create custom client-server transport system
* @param: sid - id of item before update
* @param: tid - id of item after up0ate
* @param: action - action name
* @type: public
* @topic: 0
*/
afterUpdateCallback:function(sid, tid, action, btag) {
var marker = sid;
var correct=(action!="error" && action!="invalid");
if (!correct) this.set_invalid(sid,action);
if ((this._uActions)&&(this._uActions[action])&&(!this._uActions[action](btag)))
return (delete this._in_progress[marker]);
if (this._in_progress[marker]!="wait")
this.setUpdated(sid, false);
var soid = sid;
switch (action) {
case "inserted":
case "insert":
if (tid != sid) {
this.obj[this._methods[2]](sid, tid);
sid = tid;
}
break;
case "delete":
case "deleted":
this.obj.setUserData(sid, this.action_param, "true_deleted");
this.obj[this._methods[3]](sid);
delete this._in_progress[marker];
return this.callEvent("onAfterUpdate", [sid, action, tid, btag]);
}
if (this._in_progress[marker]!="wait"){
if (correct) this.obj.setUserData(sid, this.action_param,'');
delete this._in_progress[marker];
} else {
delete this._in_progress[marker];
this.setUpdated(tid,true,this.obj.getUserData(sid,this.action_param));
}
this.callEvent("onAfterUpdate", [soid, action, tid, btag]);
},
/**
* @desc: response from server
* @param: xml - XMLLoader object with response XML
* @type: private
*/
afterUpdate:function(that,b,c,d,xml){
xml.getXMLTopNode("data"); //fix incorrect content type in IE
if (!xml.xmlDoc.responseXML) return;
var atag=xml.doXPath("//data/action");
for (var i=0; i<atag.length; i++){
var btag=atag[i];
var action = btag.getAttribute("type");
var sid = btag.getAttribute("sid");
var tid = btag.getAttribute("tid");
that.afterUpdateCallback(sid,tid,action,btag);
}
that.finalizeUpdate();
},
finalizeUpdate:function(){
if (this._waitMode) this._waitMode--;
if ((this.obj.mytype=="tree" || this.obj._h2) && this.updatedRows.length)
this.sendData();
this.callEvent("onAfterUpdateFinish",[]);
if (!this.updatedRows.length)
this.callEvent("onFullSync",[]);
},
/**
* @desc: initializes data-processor
* @param: anObj - dhtmlxGrid object to attach this data-processor to
* @type: public
*/
init:function(anObj){
this.obj = anObj;
if (this.obj._dp_init)
this.obj._dp_init(this);
},
setOnAfterUpdate:function(ev){
this.attachEvent("onAfterUpdate",ev);
},
enableDebug:function(mode){
},
setOnBeforeUpdateHandler:function(func){
this.attachEvent("onBeforeDataSending",func);
},
/*! starts autoupdate mode
@param interval
time interval for sending update requests
*/
setAutoUpdate: function(interval, user) {
interval = interval || 2000;
this._user = user || (new Date()).valueOf();
this._need_update = false;
this._loader = null;
this._update_busy = false;
this.attachEvent("onAfterUpdate",function(sid,action,tid,xml_node){
this.afterAutoUpdate(sid, action, tid, xml_node);
});
this.attachEvent("onFullSync",function(){
this.fullSync();
});
var self = this;
window.setInterval(function(){
self.loadUpdate();
}, interval);
},
/*! process updating request answer
if status == collision version is depricated
set flag for autoupdating immidiatly
*/
afterAutoUpdate: function(sid, action, tid, xml_node) {
if (action == 'collision') {
this._need_update = true;
return false;
} else {
return true;
}
},
/*! callback function for onFillSync event
call update function if it's need
*/
fullSync: function() {
if (this._need_update === true) {
this._need_update = false;
this.loadUpdate();
}
return true;
},
/*! sends query to the server and call callback function
*/
getUpdates: function(url,callback){
if (this._update_busy)
return false;
else
this._update_busy = true;
this._loader = this._loader || new dtmlXMLLoaderObject(true);
this._loader.async=true;
this._loader.waitCall=callback;
this._loader.loadXML(url);
},
/*! returns xml node value
@param node
xml node
*/
_v: function(node) {
if (node.firstChild) return node.firstChild.nodeValue;
return "";
},
/*! returns values array of xml nodes array
@param arr
array of xml nodes
*/
_a: function(arr) {
var res = [];
for (var i=0; i < arr.length; i++) {
res[i]=this._v(arr[i]);
}
return res;
},
/*! loads updates and processes them
*/
loadUpdate: function(){
var self = this;
var version = this.obj.getUserData(0,"version");
var url = this.serverProcessor+getUrlSymbol(this.serverProcessor)+["dhx_user="+this._user,"dhx_version="+version].join("&");
url = url.replace("editing=true&","");
this.getUpdates(url, function(){
var vers = self._loader.doXPath("//userdata");
self.obj.setUserData(0,"version",self._v(vers[0]));
var upds = self._loader.doXPath("//update");
if (upds.length){
self._silent_mode = true;
for (var i=0; i<upds.length; i++) {
var status = upds[i].getAttribute('status');
var id = upds[i].getAttribute('id');
var parent = upds[i].getAttribute('parent');
switch (status) {
case 'inserted':
self.callEvent("insertCallback",[upds[i], id, parent]);
break;
case 'updated':
self.callEvent("updateCallback",[upds[i], id, parent]);
break;
case 'deleted':
self.callEvent("deleteCallback",[upds[i], id, parent]);
break;
}
}
self._silent_mode = false;
}
self._update_busy = false;
self = null;
});
}
};
/*
asserts will be removed in final code, so you can place them anythere
without caring about performance impacts
*/
dhtmlx.assert = function(check, message){
//jshint -W087
if (!check){
dhtmlx.message({ type:"error", text:message, expire:-1 });
debugger;
}
};
//initial initialization
gantt.init = function(node, from, to){
if(from && to){
this.config.start_date = this._min_date = new Date(from);
this.config.end_date = this._max_date = new Date(to);
}
this._init_skin();
if (!this.config.scroll_size)
this.config.scroll_size = this._detectScrollSize();
this._reinit(node);
this.attachEvent("onLoadEnd", this.render);
dhtmlxEvent(window, "resize", this._on_resize);
//can be called only once
this.init = function(node){
if (this.$container)
this.$container.innerHTML = "";
this._reinit(node);
};
this.callEvent("onGanttReady", []);
};
gantt._reinit = function(node){
this._init_html_area(node);
this._set_sizes();
this._task_area_pulls = {};
this._task_area_renderers = {};
this._init_touch_events();
this._init_templates();
this._init_grid();
this._init_tasks();
this.render();
this._set_scroll_events();
dhtmlxEvent(this.$container, "click", this._on_click);
dhtmlxEvent(this.$container, "dblclick", this._on_dblclick);
dhtmlxEvent(this.$container, "mousemove", this._on_mousemove);
dhtmlxEvent(this.$container, "contextmenu", this._on_contextmenu);
};
//renders initial html markup
gantt._init_html_area = function(node){
if (typeof node == "string")
this._obj = document.getElementById(node);
else
this._obj = node;
dhtmlx.assert(this._obj, "Invalid html container: "+node);
var html = "<div class='gantt_container'><div class='gantt_grid'></div><div class='gantt_task'></div>";
html += "<div class='gantt_ver_scroll'><div></div></div><div class='gantt_hor_scroll'><div></div></div></div>";
this._obj.innerHTML = html;
//store links for further reference
this.$container = this._obj.firstChild;
var childs = this.$container.childNodes;
this.$grid = childs[0];
this.$task = childs[1];
this.$scroll_ver = childs[2];
this.$scroll_hor = childs[3];
this.$grid.innerHTML = "<div class='gantt_grid_scale'></div><div class='gantt_grid_data'></div>";
this.$grid_scale = this.$grid.childNodes[0];
this.$grid_data = this.$grid.childNodes[1];
this.$task.innerHTML = "<div class='gantt_task_scale'></div><div class='gantt_data_area'><div class='gantt_task_bg'></div><div class='gantt_links_area'></div><div class='gantt_bars_area'></div></div>";
this.$task_scale = this.$task.childNodes[0];
this.$task_data = this.$task.childNodes[1];
this.$task_bg = this.$task_data.childNodes[0];
this.$task_links = this.$task_data.childNodes[1];
this.$task_bars = this.$task_data.childNodes[2];
};
gantt.$click={
buttons:{
"edit":function(id){
gantt.showLightbox(id);
},
"delete":function(id){
var question = gantt.locale.labels.confirm_deleting;
var title = gantt.locale.labels.confirm_deleting_title;
gantt._dhtmlx_confirm(question, title, function(){
var task = gantt.getTask(id);
if(task.$new){
gantt._deleteTask(id, true);
gantt.refreshData();
}else{
gantt.deleteTask(id);
}
gantt.hideLightbox();
});
}
}
};
gantt._calculate_content_height = function(){
var scale_height = this.config.scale_height,
rows_height = this._order.length*this.config.row_height,
hor_scroll_height = this._scroll_hor ? this.config.scroll_size + 1 : 0;
if(!(this._is_grid_visible() || this._is_chart_visible())){
return 0;
}else{
return scale_height + rows_height + 2 + hor_scroll_height;
}
};
gantt._calculate_content_width = function(){
var grid_width = this._get_grid_width(),
chart_width = this._tasks ? this._tasks.full_width : 0,
ver_scroll_width = this._scroll_ver ? this.config.scroll_size + 1 : 0;
if(!this._is_chart_visible()){
chart_width = 0;
}
if(!this._is_grid_visible()){
grid_width = 0;
}
return grid_width + chart_width + 1;
};
gantt._get_resize_options = function(){
var res = {x:false, y:false};
if(this.config.autosize == "xy"){
res.x = res.y = true;
}else if(this.config.autosize == "y" || this.config.autosize === true){
res.y = true;
}else if(this.config.autosize == "x"){
res.x = true;
}
return res;
};
//set sizes to top level html element
gantt._set_sizes = function(){
var resize = this._get_resize_options();
if(resize.y){
this._obj.style.height = this._calculate_content_height() + 'px';
}
if(resize.x){
this._obj.style.width = this._calculate_content_width() + 'px';
}
this._y = this._obj.clientHeight;
if (this._y < 20) return;
//same height
this.$grid.style.height = this.$task.style.height = Math.max(this._y - this.$scroll_hor.offsetHeight - 2, 0) +"px";
var dataHeight = Math.max((this._y - (this.config.scale_height||0) - this.$scroll_hor.offsetHeight - 2), 0);
this.$grid_data.style.height = this.$task_data.style.height = dataHeight + "px";
//share width
var gridWidth = Math.max(this._get_grid_width()-1, 0);
this.$grid.style.width = gridWidth +"px";
this.$grid.style.display = gridWidth === 0 ? 'none' : '';
this._x = this._obj.clientWidth;
if (this._x < 20) return;
this.$grid_data.style.width = Math.max(this._get_grid_width()-1, 0) +"px";
this.$task.style.width = Math.max(this._x - this._get_grid_width() - 2, 0) +"px";
};
gantt.getScrollState=function(){
return { x:this.$task.scrollLeft, y:this.$task_data.scrollTop };
};
gantt.scrollTo = function(left, top){
if (left*1 == left)
this.$task.scrollLeft = left;
if(top*1 == top){
this.$task_data.scrollTop = top;
this.$grid_data.scrollTop = top;
}
};
gantt.showDate = function(date){
var date_x = this.posFromDate(date);
var scroll_to = Math.max(date_x - this.config.task_scroll_offset, 0);
this.scrollTo(scroll_to);
};
gantt.showTask = function(id) {
var el = this.getTaskNode(id);
if(!el)
return;
var left = Math.max(el.offsetLeft - this.config.task_scroll_offset, 0);
var top = el.offsetTop - (this.$task_data.offsetHeight - this.config.row_height)/2;
this.scrollTo(left, top);
};
//called after window resize
gantt._on_resize = gantt.setSizes = function(){
gantt._set_sizes();
gantt._scroll_resize();
};
//renders self
gantt.render = function(){
this._render_grid(); //grid.js
this._render_tasks_scales(); //tasks.js
this._scroll_resize();
this._on_resize();
this._render_data();
if(this.config.initial_scroll){
var id = (this._order[0] || this.config.root_id);
if(id)
this.showTask(id);
}
this.callEvent("onGanttRender", []);
};
gantt._set_scroll_events = function(){
dhtmlxEvent(this.$scroll_hor, "scroll", function() {
if (gantt._touch_scroll_active) return;
var left = gantt.$scroll_hor.scrollLeft;
gantt.scrollTo(left);
});
dhtmlxEvent(this.$scroll_ver, "scroll", function() {
if (gantt._touch_scroll_active) return;
var top = gantt.$scroll_ver.scrollTop;
gantt.$grid_data.scrollTop = top;
gantt.scrollTo(null, top);
});
dhtmlxEvent(this.$task, "scroll", function() {
var left = gantt.$task.scrollLeft,
barLeft = gantt.$scroll_hor.scrollLeft;
if(barLeft != left)
gantt.$scroll_hor.scrollLeft = left;
});
dhtmlxEvent(this.$task_data, "scroll", function() {
var top = gantt.$task_data.scrollTop,
barTop = gantt.$scroll_ver.scrollTop;
if(barTop != top)
gantt.$scroll_ver.scrollTop = top;
});
dhtmlxEvent(gantt.$container, "mousewheel", function(e){
var res = gantt._get_resize_options();
if (e.wheelDeltaX){
if(res.x) return true;//no horisontal scroll, must not block scrolling
var dir = e.wheelDeltaX/-40;
var left = gantt.$task.scrollLeft+dir*30;
gantt.scrollTo(left, null);
gantt.$scroll_hor.scrollTop = top;
} else {
if(res.y) return true;//no vertical scroll, must not block scrolling
var dir = e.wheelDelta/-40;
if (typeof e.wheelDelta == "undefined")
dir = e.detail;
var top = gantt.$grid_data.scrollTop+dir*30;
gantt.scrollTo(null, top);
gantt.$scroll_ver.scrollTop = top;
}
if (e.preventDefault)
e.preventDefault();
e.cancelBubble=true;
return false;
});
};
gantt._scroll_resize = function() {
if (this._x < 20 || this._y < 20) return;
var grid_width = this._get_grid_width();
var task_width = this._x - grid_width;
var task_height = this._y - this.config.scale_height;
var scroll_size = this.config.scroll_size + 1;//1px for inner content
var task_data_width = this.$task_data.offsetWidth - scroll_size;
var task_data_height = this.config.row_height*this._order.length;
var resize = this._get_resize_options();
var scroll_hor = this._scroll_hor = resize.x ? false : (task_data_width > task_width);
var scroll_ver = this._scroll_ver = resize.y ? false : (task_data_height > task_height);
this.$scroll_hor.style.display = scroll_hor ? "block" : "none";
this.$scroll_hor.style.height = (scroll_hor ? scroll_size : 0) + "px";
this.$scroll_hor.style.width = (this._x - (scroll_ver ? scroll_size : 2)) + "px";
this.$scroll_hor.firstChild.style.width = (task_data_width + grid_width + scroll_size + 2) + "px";
this.$scroll_ver.style.display = scroll_ver ? "block" : "none";
this.$scroll_ver.style.width = (scroll_ver ? scroll_size : 0) + "px";
this.$scroll_ver.style.height = (this._y - (scroll_hor ? scroll_size : 0) - this.config.scale_height) + "px";
this.$scroll_ver.style.top = this.config.scale_height + "px";
this.$scroll_ver.firstChild.style.height = (this.config.scale_height + task_data_height) + "px";
};
gantt.locate = function(e) {
var trg = gantt._get_target_node(e);
//ignore empty cells
if (trg.className == "gantt_task_cell") return null;
var attribute = arguments[1] || this.config.task_attribute;
while (trg){
if (trg.getAttribute){ //text nodes has not getAttribute
var test = trg.getAttribute(attribute);
if (test) return test;
}
trg=trg.parentNode;
}
return null;
};
gantt._get_target_node = function(e){
var trg;
if (e.tagName)
trg = e;
else {
e=e||window.event;
trg=e.target||e.srcElement;
}
return trg;
};
gantt._trim = function(str){
var func = String.prototype.trim || function(){ return this.replace(/^\s+|\s+$/g, ""); };
return func.apply(str);
};
gantt._locate_css = function(e, classname, strict){
if(strict === undefined)
strict = true;
var trg = gantt._get_target_node(e);
var css = '';
var test = false;
while (trg){
css = trg.className;
if(css){
var ind = css.indexOf(classname);
if (ind >= 0){
if (!strict)
return trg;
//check that we have exact match
var left = (ind === 0) || (!gantt._trim(css.charAt(ind - 1)));
var right = ((ind + classname.length >= css.length)) || (!gantt._trim(css.charAt(ind + classname.length)));
if (left && right)
return trg;
}
}
trg=trg.parentNode;
}
return null;
};
gantt._locateHTML = function(e, attribute) {
var trg = gantt._get_target_node(e);
attribute = attribute || this.config.task_attribute;
while (trg){
if (trg.getAttribute){ //text nodes has not getAttribute
var test = trg.getAttribute(attribute);
if (test) return trg;
}
trg=trg.parentNode;
}
return null;
};
gantt.getTaskRowNode = function(id) {
var els = this.$grid_data.childNodes;
var attribute = this.config.task_attribute;
for (var i = 0; i < els.length; i++) {
if (els[i].getAttribute) {
var value = els[i].getAttribute(attribute);
if (value == id) return els[i];
}
}
return null;
};
gantt.getState = function(){
return {
drag_id : this._tasks_dnd.drag.id,
drag_mode : this._tasks_dnd.drag.mode,
drag_from_start : this._tasks_dnd.drag.left,
selected_task : this._selected_task,
min_date : new Date(this._min_date),
max_date : new Date(this._max_date),
lightbox : this._lightbox_id
};
};
gantt._checkTimeout = function(host, updPerSecond){
if(!updPerSecond)
return true;
var timeout = 1000/updPerSecond;
if(timeout < 1) return true;
if(host._on_timeout)
return false;
setTimeout(function(){
delete host._on_timeout;
}, timeout);
host._on_timeout = true;
return true;
};
gantt.selectTask = function(id){
if(!this.config.select_task)
return false;
if (id){
if(this._selected_task == id)
return this._selected_task;
if(!this.callEvent("onBeforeTaskSelected", [id])){
return false;
}
this.unselectTask();
this._selected_task = id;
this.refreshTask(id);
this.callEvent("onTaskSelected", [id]);
}
return this._selected_task;
};
gantt.unselectTask = function(){
var id = this._selected_task;
if(!id)
return;
this._selected_task = null;
this.refreshTask(id);
this.callEvent("onTaskUnselected", [id]);
};
gantt.getSelectedId = function() {
return dhtmlx.defined(this._selected_task) ? this._selected_task : null;
};
gantt.changeLightboxType = function(type){
if(this.getLightboxType() == type)
return true;
gantt._silent_redraw_lightbox(type);
};
gantt.date={
init:function(){
var s = gantt.locale.date.month_short;
var t = gantt.locale.date.month_short_hash = {};
for (var i = 0; i < s.length; i++)
t[s[i]]=i;
var s = gantt.locale.date.month_full;
var t = gantt.locale.date.month_full_hash = {};
for (var i = 0; i < s.length; i++)
t[s[i]]=i;
},
date_part:function(date){
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
date.setMilliseconds(0);
if (date.getHours())
date.setTime(date.getTime() + 60 * 60 * 1000 * (24 - date.getHours()));
return date;
},
time_part:function(date){
return (date.valueOf()/1000 - date.getTimezoneOffset()*60)%86400;
},
week_start:function(date){
var shift=date.getDay();
if (gantt.config.start_on_monday){
if (shift===0) shift=6;
else shift--;
}
return this.date_part(this.add(date,-1*shift,"day"));
},
month_start:function(date){
date.setDate(1);
return this.date_part(date);
},
year_start:function(date){
date.setMonth(0);
return this.month_start(date);
},
day_start:function(date){
return this.date_part(date);
},
hour_start:function(date){
var hour = date.getHours();
this.day_start(date);
date.setHours(hour);
return date;
},
minute_start:function(date){
var min = date.getMinutes();
this.hour_start(date);
date.setMinutes(min);
return date;
},
_add_days:function(date, inc){
var ndate = new Date(date.valueOf());
ndate.setDate(ndate.getDate() + inc);
if (!date.getHours() && ndate.getHours()) //shift to yesterday
ndate.setTime(ndate.getTime() + 60 * 60 * 1000 * (24 - ndate.getHours()));
return ndate;
},
add:function(date,inc,mode){
/*jsl:ignore*/
var ndate=new Date(date.valueOf());
switch(mode){
case "day":
ndate = gantt.date._add_days(ndate, inc);
break;
case "week":
ndate = gantt.date._add_days(ndate, inc * 7);
break;
case "month": ndate.setMonth(ndate.getMonth()+inc); break;
case "year": ndate.setYear(ndate.getFullYear()+inc); break;
case "hour":
/*
adding hours/minutes via setHour(getHour() + inc) gives weird result when
adding one hour to the time before switch to a Daylight Saving time
example: //Sun Mar 30 2014 01:00:00 GMT+0100 (W. Europe Standard Time)
new Date(2014, 02, 30, 1).setHours(2)
>>Sun Mar 30 2014 01:00:00 GMT+0100 (W. Europe Standard Time)
setTime seems working as expected
*/
ndate.setTime(ndate.getTime()+inc * 60 * 60 * 1000);
break;
case "minute":
ndate.setTime(ndate.getTime() + inc * 60 * 1000);
break;
default:
return gantt.date["add_"+mode](date,inc,mode);
}
return ndate;
/*jsl:end*/
},
to_fixed:function(num){
if (num<10) return "0"+num;
return num;
},
copy:function(date){
return new Date(date.valueOf());
},
date_to_str:function(format,utc){
format=format.replace(/%[a-zA-Z]/g,function(a){
switch(a){
case "%d": return "\"+gantt.date.to_fixed(date.getDate())+\"";
case "%m": return "\"+gantt.date.to_fixed((date.getMonth()+1))+\"";
case "%j": return "\"+date.getDate()+\"";
case "%n": return "\"+(date.getMonth()+1)+\"";
case "%y": return "\"+gantt.date.to_fixed(date.getFullYear()%100)+\"";
case "%Y": return "\"+date.getFullYear()+\"";
case "%D": return "\"+gantt.locale.date.day_short[date.getDay()]+\"";
case "%l": return "\"+gantt.locale.date.day_full[date.getDay()]+\"";
case "%M": return "\"+gantt.locale.date.month_short[date.getMonth()]+\"";
case "%F": return "\"+gantt.locale.date.month_full[date.getMonth()]+\"";
case "%h": return "\"+gantt.date.to_fixed((date.getHours()+11)%12+1)+\"";
case "%g": return "\"+((date.getHours()+11)%12+1)+\"";
case "%G": return "\"+date.getHours()+\"";
case "%H": return "\"+gantt.date.to_fixed(date.getHours())+\"";
case "%i": return "\"+gantt.date.to_fixed(date.getMinutes())+\"";
case "%a": return "\"+(date.getHours()>11?\"pm\":\"am\")+\"";
case "%A": return "\"+(date.getHours()>11?\"PM\":\"AM\")+\"";
case "%s": return "\"+gantt.date.to_fixed(date.getSeconds())+\"";
case "%W": return "\"+gantt.date.to_fixed(gantt.date.getISOWeek(date))+\"";
default: return a;
}
});
if (utc) format=format.replace(/date\.get/g,"date.getUTC");
return new Function("date","return \""+format+"\";");
},
str_to_date:function(format,utc){
var splt="var temp=date.match(/[a-zA-Z]+|[0-9]+/g);";
var mask=format.match(/%[a-zA-Z]/g);
for (var i=0; i<mask.length; i++){
switch(mask[i]){
case "%j":
case "%d": splt+="set[2]=temp["+i+"]||1;";
break;
case "%n":
case "%m": splt+="set[1]=(temp["+i+"]||1)-1;";
break;
case "%y": splt+="set[0]=temp["+i+"]*1+(temp["+i+"]>50?1900:2000);";
break;
case "%g":
case "%G":
case "%h":
case "%H":
splt+="set[3]=temp["+i+"]||0;";
break;
case "%i":
splt+="set[4]=temp["+i+"]||0;";
break;
case "%Y": splt+="set[0]=temp["+i+"]||0;";
break;
case "%a":
case "%A": splt+="set[3]=set[3]%12+((temp["+i+"]||'').toLowerCase()=='am'?0:12);";
break;
case "%s": splt+="set[5]=temp["+i+"]||0;";
break;
case "%M": splt+="set[1]=gantt.locale.date.month_short_hash[temp["+i+"]]||0;";
break;
case "%F": splt+="set[1]=gantt.locale.date.month_full_hash[temp["+i+"]]||0;";
break;
default:
break;
}
}
var code ="set[0],set[1],set[2],set[3],set[4],set[5]";
if (utc) code =" Date.UTC("+code+")";
return new Function("date","var set=[0,0,1,0,0,0]; "+splt+" return new Date("+code+");");
},
getISOWeek: function(ndate) {
if(!ndate) return false;
var nday = ndate.getDay();
if (nday === 0) {
nday = 7;
}
var first_thursday = new Date(ndate.valueOf());
first_thursday.setDate(ndate.getDate() + (4 - nday));
var year_number = first_thursday.getFullYear(); // year of the first Thursday
var ordinal_date = Math.round( (first_thursday.getTime() - new Date(year_number, 0, 1).getTime()) / 86400000); //ordinal date of the first Thursday - 1 (so not really ordinal date)
var week_number = 1 + Math.floor( ordinal_date / 7);
return week_number;
},
getUTCISOWeek: function(ndate){
return this.getISOWeek(ndate);
},
convert_to_utc: function(date) {
return new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
},
parseDate: function(date, format) {
if (typeof(date) == "string") {
if (dhtmlx.defined(format)){
if (typeof(format) == "string")
format = dhtmlx.defined(gantt.templates[format]) ? gantt.templates[format] : gantt.date.str_to_date(format);
else
format = gantt.templates.xml_date;
}
date = format(date);
}
return date;
}
};
/*
%d - the day as a number with a leading zero ( 01 to 31 );
%j - the day as a number without a leading zero ( 1 to 31 );
%D - the day as an abbreviation ( Sun to Sat );
%l - the day as a full name ( Sunday to Saturday );
%W - the ISO-8601 week number of the year. Weeks start on Monday; 1)
%m - the month as a number without a leading zero ( 1 to 12 );
%n - the month as a number with a leading zero ( 01 to 12);
%M - the month as an abbreviation ( Jan to Dec );
%F - the month as a full name ( January to December );
%y - the year as a two-digit number ( 00 to 99 );
%Y - the year as a four-digit number ( 19009999 );
%h - the hour based on the 12-hour clock ( 00 to 11 );
%H - the hour based on the 24-hour clock ( 00 to 23 );
%i - the minute as a number with a leading zero ( 00 to 59 );
%s - the second as a number without a leading zero ( 00 to 59 ); 2)
%a - displays am (for times from midnight until noon) and pm (for times from noon until midnight);
%A - displays AM (for times from midnight until noon) and PM (for times from noon until midnight).
*/
if(!gantt.config) gantt.config = {};
if(!gantt.config) gantt.config = {};
if(!gantt.templates) gantt.templates = {};
(function(){
dhtmlx.mixin(gantt.config,
{links : {
"finish_to_start":"0",
"start_to_start":"1",
"finish_to_finish":"2",
"start_to_finish":"3"
},
types : {
'task':'task',
'project':'project',
'milestone':'milestone'
},
duration_unit : "day",
work_time:false,
correct_work_time:false,
skip_off_time:false,
autosize:false,
show_links : true,
show_task_cells : true,
show_chart : true,
show_grid : true,
min_duration : 60*60*1000,
xml_date : "%d-%m-%Y %H:%i",
api_date : "%d-%m-%Y %H:%i",
start_on_monday: true,
server_utc : false,
show_progress:true,
fit_tasks : false,
select_task:true,
readonly:false,
/*grid */
date_grid: "%Y-%m-%d",
drag_links : true,
drag_progress:true,
drag_resize:true,
drag_move:true,
drag_mode:{
"resize":"resize",
"progress":"progress",
"move":"move",
"ignore":"ignore"
},
round_dnd_dates:true,
link_wrapper_width:20,
root_id:0,
autofit: true, // grid column automatic fit
columns: [
{name:"text", tree:true, width:'*' },
{name:"start_date", align: "center" },
{name:"duration", align: "center" },
{name:"add", width:'44' }
],
/*scale*/
step: 1,
scale_unit: "day",
subscales : [
],
time_step: 60,
duration_step: 1,
date_scale: "%d %M",
task_date: "%d %F %Y",
time_picker: "%H:%i",
task_attribute: "task_id",
link_attribute: "link_id",
buttons_left: [
"dhx_save_btn",
"dhx_cancel_btn"
],
buttons_right: [
"dhx_delete_btn"
],
lightbox: {
sections: [
{name: "description", height: 70, map_to: "text", type: "textarea", focus: true},
{name: "time", height: 72, type: "duration", map_to: "auto"}
],
project_sections: [
{name: "description", height: 70, map_to: "text", type: "textarea", focus: true},
{name: "type", type: "typeselect", map_to: "type"},
{name: "time", height: 72, type: "duration", readonly:true, map_to: "auto"}
],
milestone_sections: [
{name: "description", height: 70, map_to: "text", type: "textarea", focus: true},
{name: "type", type: "typeselect", map_to: "type"},
{name: "time", height: 72, type: "duration", single_date:true, map_to: "auto"}
]
},
drag_lightbox: true,
sort: false,
details_on_create: true,
details_on_dblclick:true,
initial_scroll : true,
task_scroll_offset : 100,
task_height: "full",//number px of 'full' for row height
min_column_width:70
});
gantt.keys={
edit_save:13,
edit_cancel:27
};
gantt._init_template = function(name, initial){
var registeredTemplates = this._reg_templates || {};
if(this.config[name] && registeredTemplates[name] != this.config[name]){
if(!(initial && this.templates[name])){
this.templates[name] = this.date.date_to_str(this.config[name]);
registeredTemplates[name] = this.config[name];
}
}
this._reg_templates = registeredTemplates;
};
gantt._init_templates = function(){
var labels = gantt.locale.labels;
labels.dhx_save_btn = labels.icon_save;
labels.dhx_cancel_btn = labels.icon_cancel;
labels.dhx_delete_btn = labels.icon_delete;
//build configuration based templates
var d = this.date.date_to_str;
var c = this.config;
gantt._init_template("date_scale", true);
gantt._init_template("date_grid", true);
gantt._init_template("task_date", true);
dhtmlx.mixin(this.templates,{
xml_date:this.date.str_to_date(c.xml_date,c.server_utc),
xml_format:d(c.xml_date,c.server_utc),
api_date:this.date.str_to_date(c.api_date),
progress_text:function(start, end, task){return "";},
grid_header_class : function(column, config){
return "";
},
task_text:function(start, end, task){
return task.text;
},
task_class:function(start, end, task){return "";},
grid_row_class:function(start, end, task){
return "";
},
task_row_class:function(start, end, task){
return "";
},
task_cell_class:function(item, date){return "";},
scale_cell_class:function(date){return "";},
scale_row_class:function(date){return "";},
grid_indent:function(item) {
return "<div class='gantt_tree_indent'></div>";
},
grid_folder:function(item) {
return "<div class='gantt_tree_icon gantt_folder_" + (item.$open ? "open" : "closed") + "'></div>";
},
grid_file:function(item) {
return "<div class='gantt_tree_icon gantt_file'></div>";
},
grid_open:function(item) {
return "<div class='gantt_tree_icon gantt_" + (item.$open ? "close" : "open") + "'></div>";
},
grid_blank:function(item) {
return "<div class='gantt_tree_icon gantt_blank'></div>";
},
task_time:function(start,end,ev){
return gantt.templates.task_date(start)+" - "+gantt.templates.task_date(end);
},
time_picker:d(c.time_picker),
link_class : function(link){
return "";
},
link_description : function(link){
var from = gantt.getTask(link.source),
to = gantt.getTask(link.target);
return "<b>" + from.text + "</b> &ndash; <b>" + to.text+"</b>";
},
drag_link : function(from, from_start, to, to_start) {
from = gantt.getTask(from);
var labels = gantt.locale.labels;
var text = "<b>" + from.text + "</b> " + (from_start ? labels.link_start : labels.link_end)+"<br/>";
if(to){
to = gantt.getTask(to);
text += "<b> " + to.text + "</b> "+ (to_start ? labels.link_start : labels.link_end)+"<br/>";
}
return text;
},
drag_link_class: function(from, from_start, to, to_start) {
var add = "";
if(from && to){
var allowed = gantt.isLinkAllowed(from, to, from_start, to_start);
add = " " + (allowed ? "gantt_link_allow" : "gantt_link_deny");
}
return "gantt_link_tooltip" + add;
}
});
this.callEvent("onTemplatesReady",[]);
};
})();
if (window.jQuery){
(function( $ ){
var methods = [];
$.fn.dhx_gantt = function(config){
config = config || {};
if (typeof(config) === 'string') {
if (methods[config] ) {
return methods[config].apply(this, []);
}else {
$.error('Method ' + config + ' does not exist on jQuery.dhx_gantt');
}
} else {
var views = [];
this.each(function() {
if (this && this.getAttribute){
if (!this.getAttribute("dhxgantt")){
for (var key in config)
if (key!="data")
gantt.config[key] = config[key];
gantt.init(this);
if (config.data)
gantt.parse(config.data);
views.push(gantt);
}
}
});
if (views.length === 1) return views[0];
return views;
}
};
})(jQuery);
}
if (window.dhtmlx){
if (!dhtmlx.attaches)
dhtmlx.attaches = {};
dhtmlx.attaches.attachGantt=function(start, end){
var obj = document.createElement("DIV");
obj.id = "gantt_"+dhtmlx.uid();
obj.style.width = "100%";
obj.style.height = "100%";
obj.cmp = "grid";
document.body.appendChild(obj);
this.attachObject(obj.id);
var that = this.vs[this.av];
that.grid = gantt;
gantt.init(obj.id, start, end);
obj.firstChild.style.border = "none";
that.gridId = obj.id;
that.gridObj = obj;
var method_name="_viewRestore";
return this.vs[this[method_name]()].grid;
};
}
gantt.locale = {
date:{
month_full:["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
month_short:["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
day_full:["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
day_short:["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
},
labels:{
new_task:"New task",
icon_save:"Save",
icon_cancel:"Cancel",
icon_details:"Details",
icon_edit:"Edit",
icon_delete:"Delete",
confirm_closing:"",//Your changes will be lost, are your sure ?
confirm_deleting:"Task will be deleted permanently, are you sure?",
section_description:"Description",
section_time:"Time period",
section_type:"Type",
/* grid columns */
column_text : "Task name",
column_start_date : "Start time",
column_duration : "Duration",
column_add : "",
/* link confirmation */
link: "Link",
confirm_link_deleting:"will be deleted",
link_start: " (start)",
link_end: " (end)",
type_task: "Task",
type_project: "Project",
type_milestone: "Milestone",
minutes: "Minutes",
hours: "Hours",
days: "Days",
weeks: "Week",
months: "Months",
years: "Years"
}
};
gantt.skins.skyblue = {
config:{
grid_width:350,
row_height: 27,
scale_height: 27,
task_height: 24,
link_line_width:1,
link_arrow_size:8,
lightbox_additional_height:75
},
_second_column_width:95,
_third_column_width:80
};
gantt.skins.meadow = {
config:{
grid_width:350,
row_height: 27,
scale_height: 30,
task_height:24,
link_line_width:2,
link_arrow_size:6,
lightbox_additional_height:72
},
_second_column_width:95,
_third_column_width:80
};
gantt.skins.terrace = {
config:{
grid_width:360,
row_height: 35,
scale_height: 35,
task_height: 24,
link_line_width:2,
link_arrow_size:6,
lightbox_additional_height:75
},
_second_column_width:90,
_third_column_width:70
};
gantt.skins.broadway = {
config:{
grid_width:360,
row_height: 35,
scale_height: 35,
task_height: 24,
link_line_width:1,
link_arrow_size:7,
lightbox_additional_height:86
},
_second_column_width:90,
_third_column_width:80,
_lightbox_template:"<div class='dhx_cal_ltitle'><span class='dhx_mark'>&nbsp;</span><span class='dhx_time'></span><span class='dhx_title'></span><div class='dhx_cancel_btn'></div></div><div class='dhx_cal_larea'></div>",
_config_buttons_left: {},
_config_buttons_right: {
"dhx_delete_btn": "icon_delete",
"dhx_save_btn": "icon_save"
}
};
gantt.config.touch_drag = 50; //nearly immediate dnd
gantt.config.touch = true;
gantt._init_touch_events = function(){
if (this.config.touch != "force")
this.config.touch = this.config.touch &&
((navigator.userAgent.indexOf("Mobile")!=-1) ||
(navigator.userAgent.indexOf("iPad")!=-1) ||
(navigator.userAgent.indexOf("Android")!=-1) ||
(navigator.userAgent.indexOf("Touch")!=-1));
if (this.config.touch){
if (window.navigator.msPointerEnabled){
this._touch_events(["MSPointerMove", "MSPointerDown", "MSPointerUp"], function(ev){
if (ev.pointerType == ev.MSPOINTER_TYPE_MOUSE ) return null;
return ev;
}, function(ev){
return (!ev || ev.pointerType == ev.MSPOINTER_TYPE_MOUSE);
});
} else
this._touch_events(["touchmove", "touchstart", "touchend"], function(ev){
if (ev.touches && ev.touches.length > 1) return null;
if (ev.touches[0])
return { target:ev.target, pageX:ev.touches[0].pageX, pageY:ev.touches[0].pageY };
else
return ev;
}, function(){ return false; });
}
};
//we can't use native scrolling, as we need to sync momentum between different parts
//so we will block native scroll and use the custom one
//in future we can add custom momentum
gantt._touch_events = function(names, accessor, ignore){
//webkit on android need to be handled separately
var dblclicktime = 0;
var action_mode = false;
var scroll_mode = false;
var dblclick_timer = 0;
var action_start = null;
var scroll_state;
//touch move
if (!this._gantt_touch_event_ready){
this._gantt_touch_event_ready = 1;
dhtmlxEvent(document.body, names[0], function(e){
if (ignore(e)) return;
//ignore common and scrolling moves
if (!action_mode) return;
var source = accessor(e);
if (source && action_start){
var dx = action_start.pageX - source.pageX;
var dy = action_start.pageY - source.pageY;
if (!scroll_mode && (Math.abs(dx) > 5 || Math.abs(dy) > 5)){
gantt._touch_scroll_active = scroll_mode = true;
dblclicktime = 0;
scroll_state = gantt.getScrollState();
}
if (scroll_mode){
gantt.scrollTo(scroll_state.x + dx, scroll_state.y + dy);
}
}
return block_action(e);
});
}
//common helper, prevents event
function block_action(e){
if (e && e.preventDefault)
e.preventDefault();
(e||event).cancelBubble = true;
return false;
}
//block touch context menu in IE10
dhtmlxEvent(this.$container, "contextmenu", function(e){
if (action_mode)
return block_action(e);
});
//touch start
dhtmlxEvent(this.$container, names[1], function(e){
if (ignore(e)) return;
if (e.touches && e.touches.length > 1){
action_mode = false;
return;
}
action_mode = true;
action_start = accessor(e);
//dbl-tap handling
if (action_start && dblclicktime){
var now = new Date();
if ((now - dblclicktime) < 500 ){
gantt._on_dblclick(action_start);
block_action(e);
} else
dblclicktime = now;
} else {
dblclicktime = new Date();
}
});
//touch end
dhtmlxEvent(this.$container, names[2], function(e){
if (ignore(e)) return;
gantt._touch_scroll_active = action_mode = scroll_mode = false;
});
};