/*
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-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 "
"+text+"
";
}
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 = "
"+text.text+"
";
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+='
'+config.title+'
';
inner+='
'+(config.content?'':config.text)+'
';
if (ok)
inner += button(config.ok || "OK", true);
if (cancel)
inner += button(config.cancel || "Cancel", false);
if (config.buttons){
for (var i=0; i';
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 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) ? ("") : "";
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 = "
" + label + sort + "
";
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 = "";
} else {
if (col.template)
value = col.template(item);
else
value = item[col.name];
if (value instanceof Date)
value = this.templates.date_grid(value);
value = "
" + value + "
";
}
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 = "
" + tree + value + "
";
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 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 = "