mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-30 10:39:18 +01:00
8378 lines
236 KiB
JavaScript
8378 lines
236 KiB
JavaScript
|
/*
|
|||
|
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'> </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;'> – </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 ( 1900–9999 );
|
|||
|
%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> – <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'> </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;
|
|||
|
});
|
|||
|
};
|