/*
@license
dhtmlxGantt v.3.2.0 Stardard
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 (typeof(window.dhx4) == "undefined") {
window.dhx4 = {
version: "4.1.3",
skin: null, // allow to be set by user
skinDetect: function(comp) {
return {10:"dhx_skyblue",20:"dhx_web",30:"dhx_terrace"}[this.readFromCss(comp+"_skin_detect")]||null;
},
// read value from css
readFromCss: function(className, property) {
var t = document.createElement("DIV");
t.className = className;
if (document.body.firstChild != null) document.body.insertBefore(t, document.body.firstChild); else document.body.appendChild(t);
var w = t[property||"offsetWidth"];
t.parentNode.removeChild(t);
t = null;
return w;
},
// id manager
lastId: 1,
newId: function() {
return this.lastId++;
},
// z-index manager
zim: {
data: {},
step: 5,
first: function() {
return 100;
},
last: function() {
var t = this.first();
for (var a in this.data) t = Math.max(t, this.data[a]);
return t;
},
reserve: function(id) {
this.data[id] = this.last()+this.step;
return this.data[id];
},
clear: function(id) {
if (this.data[id] != null) {
this.data[id] = null;
delete this.data[id];
}
}
},
// string to boolean
s2b: function(r) {
if (typeof(r) == "string") r = r.toLowerCase();
return (r == true || r == 1 || r == "true" || r == "1" || r == "yes" || r == "y");
},
// string to json
s2j: function(s) {
var obj = null;
dhx4.temp = null;
try { eval("dhx4.temp="+s); } catch(e) { dhx4.temp = null; }
obj = dhx4.temp;
dhx4.temp = null;
return obj;
},
// absolute top/left position on screen
absLeft: function(obj) {
if (typeof(obj) == "string") obj = document.getElementById(obj);
return this.getOffset(obj).left;
},
absTop: function(obj) {
if (typeof(obj) == "string") obj = document.getElementById(obj);
return this.getOffset(obj).top;
},
_aOfs: function(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};
},
_aOfsRect: function(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) };
},
getOffset: function(elem) {
if (elem.getBoundingClientRect) {
return this._aOfsRect(elem);
} else {
return this._aOfs(elem);
}
},
// copy obj
_isObj: function(k) {
return (k != null && typeof(k) == "object" && typeof(k.length) == "undefined");
},
_copyObj: function(r) {
if (this._isObj(r)) {
var t = {};
for (var a in r) {
if (typeof(r[a]) == "object" && r[a] != null) t[a] = this._copyObj(r[a]); else t[a] = r[a];
}
} else {
var t = [];
for (var a=0; a= 0);
var dim = {};
dim.left = document.body.scrollLeft;
dim.right = dim.left+(window.innerWidth||document.body.clientWidth);
dim.top = Math.max((isIE?document.documentElement:document.getElementsByTagName("html")[0]).scrollTop, document.body.scrollTop);
dim.bottom = dim.top+(isIE?Math.max(document.documentElement.clientHeight||0,document.documentElement.offsetHeight||0):window.innerHeight);
return dim;
},
// input/textarea range selection
selectTextRange: function(inp, start, end) {
inp = (typeof(inp)=="string"?document.getElementById(inp):inp);
var len = inp.value.length;
start = Math.max(Math.min(start, len), 0);
end = Math.min(end, len);
if (inp.setSelectionRange) {
try {inp.setSelectionRange(start, end);} catch(e){}; // combo in grid under IE requires try/catch
} else if (inp.createTextRange) {
var range = inp.createTextRange();
range.moveStart("character", start);
range.moveEnd("character", end-len);
try {range.select();} catch(e){};
}
},
// transition
transData: null,
transDetect: function() {
if (this.transData == null) {
this.transData = {transProp: false, transEv: null};
// transition, MozTransition, WebkitTransition, msTransition, OTransition
var k = {
"MozTransition": "transitionend",
"WebkitTransition": "webkitTransitionEnd",
"OTransition": "oTransitionEnd",
"msTransition": "transitionend",
"transition": "transitionend"
};
for (var a in k) {
if (this.transData.transProp == false && document.documentElement.style[a] != null) {
this.transData.transProp = a;
this.transData.transEv = k[a];
}
}
k = null;
}
return this.transData;
},
// xml parser
_xmlNodeValue: function(node) {
var value = "";
for (var q=0; q= 0 || navigator.userAgent.indexOf("Trident") >= 0);
window.dhx4.isIE6 = (window.XMLHttpRequest == null && navigator.userAgent.indexOf("MSIE") >= 0);
window.dhx4.isIE7 = (navigator.userAgent.indexOf("MSIE 7.0") >= 0 && navigator.userAgent.indexOf("Trident") < 0);
window.dhx4.isIE8 = (navigator.userAgent.indexOf("MSIE 8.0") >= 0 && navigator.userAgent.indexOf("Trident") >= 0);
window.dhx4.isOpera = (navigator.userAgent.indexOf("Opera") >= 0);
window.dhx4.isChrome = (navigator.userAgent.indexOf("Chrome") >= 0);
window.dhx4.isKHTML = (navigator.userAgent.indexOf("Safari") >= 0 || navigator.userAgent.indexOf("Konqueror") >= 0);
window.dhx4.isFF = (navigator.userAgent.indexOf("Firefox") >= 0);
window.dhx4.isIPad = (navigator.userAgent.search(/iPad/gi) >= 0);
};
if (typeof(window.dhx4.ajax) == "undefined") {
window.dhx4.ajax = {
// if false - dhxr param will added to prevent caching on client side (default),
// if true - do not add extra params
cache: false,
// default method for load/loadStruct, post/get allowed
// get - since 4.1.1, this should fix 412 error for macos safari
method: "get",
parse: function(data) {
if (typeof data !== "string") return data;
data = data.replace(/^[\s]+/,"");
if (window.DOMParser && !dhx4.isIE) { // ff,ie9
var obj = (new window.DOMParser()).parseFromString(data, "text/xml");
} else if (window.ActiveXObject !== window.undefined) {
var obj = new window.ActiveXObject("Microsoft.XMLDOM");
obj.async = "false";
obj.loadXML(data);
}
return obj;
},
xmltop: function(tagname, xhr, obj) {
if (typeof xhr.status == "undefined" || xhr.status < 400) {
var xml = (!xhr.responseXML) ? dhx4.ajax.parse(xhr.responseText || xhr) : (xhr.responseXML || xhr);
if (xml && xml.documentElement !== null && !xml.getElementsByTagName("parsererror").length) {
return xml.getElementsByTagName(tagname)[0];
}
}
if (obj !== -1) dhx4.callEvent("onLoadXMLError",["Incorrect XML", arguments[1], obj]);
return document.createElement("DIV");
},
xpath: function(xpathExp, docObj) {
if (!docObj.nodeName) docObj = docObj.responseXML || docObj;
if (dhx4.isIE) {
return docObj.selectNodes(xpathExp)||[];
} else {
var rows = [];
var first;
var col = (docObj.ownerDocument||docObj).evaluate(xpathExp, docObj, null, XPathResult.ANY_TYPE, null);
while (first = col.iterateNext()) rows.push(first);
return rows;
}
},
query: function(config) {
dhx4.ajax._call(
(config.method || "GET"),
config.url,
config.data || "",
(config.async || true),
config.callback,
null,
config.headers
);
},
get: function(url, onLoad) {
this._call("GET", url, null, true, onLoad);
},
getSync: function(url) {
return this._call("GET", url, null, false);
},
put: function(url, postData, onLoad) {
this._call("PUT", url, postData, true, onLoad);
},
del: function(url, postData, onLoad) {
this._call("DELETE", url, postData, true, onLoad);
},
post: function(url, postData, onLoad) {
if (arguments.length == 1) {
postData = "";
} else if (arguments.length == 2 && (typeof(postData) == "function" || typeof(window[postData]) == "function")) {
onLoad = postData;
postData = "";
} else {
postData = String(postData);
}
this._call("POST", url, postData, true, onLoad);
},
postSync: function(url, postData) {
postData = (postData == null ? "" : String(postData));
return this._call("POST", url, postData, false);
},
getLong: function(url, onLoad) {
this._call("GET", url, null, true, onLoad, {url:url});
},
postLong: function(url, postData, onLoad) {
if (arguments.length == 2 && (typeof(postData) == "function" || typeof(window[postData]))) {
onLoad = postData;
postData = "";
}
this._call("POST", url, postData, true, onLoad, {url:url, postData:postData});
},
_call: function(method, url, postData, async, onLoad, longParams, headers) {
var t = (window.XMLHttpRequest && !dhx4.isIE ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
var isQt = (navigator.userAgent.match(/AppleWebKit/) != null && navigator.userAgent.match(/Qt/) != null && navigator.userAgent.match(/Safari/) != null);
if (async == true) {
t.onreadystatechange = function() {
if ((t.readyState == 4) || (isQt == true && t.readyState == 3)) { // what for long response and status 404?
if (t.status != 200 || t.responseText == "")
if (!dhx4.callEvent("onAjaxError", [t])) return;
window.setTimeout(function(){
if (typeof(onLoad) == "function") {
onLoad.apply(window, [{xmlDoc:t}]); // dhtmlx-compat, response.xmlDoc.responseXML/responseText
}
if (longParams != null) {
if (typeof(longParams.postData) != "undefined") {
dhx4.ajax.postLong(longParams.url, longParams.postData, onLoad);
} else {
dhx4.ajax.getLong(longParams.url, onLoad);
}
}
onLoad = null;
t = null;
},1);
}
}
}
if (method == "GET" && this.cache != true) {
url += (url.indexOf("?")>=0?"&":"?")+"dhxr"+new Date().getTime()+"=1";
}
t.open(method, url, async);
if (headers){
for (var key in headers)
t.setRequestHeader(key, headers[key]);
} else if (method.toUpperCase() == "POST" || method == "PUT" || method == "DELETE") {
t.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
} else if (method == "GET") {
postData = null;
}
t.setRequestHeader("X-Requested-With", "XMLHttpRequest");
t.send(postData);
if (!async) return {xmlDoc:t}; // dhtmlx-compat, response.xmlDoc.responseXML/responseText
}
};
};
if (typeof(window.dhx4._eventable) == "undefined") {
window.dhx4._eventable = function(obj, mode) {
if (mode == "clear") {
obj.detachAllEvents();
obj.dhxevs = null;
obj.attachEvent = null;
obj.detachEvent = null;
obj.checkEvent = null;
obj.callEvent = null;
obj.detachAllEvents = null;
obj = null;
return;
}
obj.dhxevs = { data: {} };
obj.attachEvent = function(name, func) {
name = String(name).toLowerCase();
if (!this.dhxevs.data[name]) this.dhxevs.data[name] = {};
var eventId = window.dhx4.newId();
this.dhxevs.data[name][eventId] = func;
return eventId;
}
obj.detachEvent = function(eventId) {
for (var a in this.dhxevs.data) {
var k = 0;
for (var b in this.dhxevs.data[a]) {
if (b == eventId) {
this.dhxevs.data[a][b] = null;
delete this.dhxevs.data[a][b];
} else {
k++;
}
}
if (k == 0) {
this.dhxevs.data[a] = null;
delete this.dhxevs.data[a];
}
}
}
obj.checkEvent = function(name) {
name = String(name).toLowerCase();
return (this.dhxevs.data[name] != null);
}
obj.callEvent = function(name, params) {
name = String(name).toLowerCase();
if (this.dhxevs.data[name] == null) return true;
var r = true;
for (var a in this.dhxevs.data[name]) {
r = this.dhxevs.data[name][a].apply(this, params) && r;
}
return r;
}
obj.detachAllEvents = function() {
for (var a in this.dhxevs.data) {
for (var b in this.dhxevs.data[a]) {
this.dhxevs.data[a][b] = null;
delete this.dhxevs.data[a][b];
}
this.dhxevs.data[a] = null;
delete this.dhxevs.data[a];
}
}
obj = null;
};
dhx4._eventable(dhx4);
};
if (typeof(window.dhtmlx) == "undefined") {
window.dhtmlx={
extend:function(a, b){
for (var key in b)
if (!a[key])
a[key]=b[key];
return a;
},
extend_api:function(name,map,ext){
var t = window[name];
if (!t) return; //component not defined
window[name]=function(obj){
if (obj && typeof obj == "object" && !obj.tagName){
var 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
var that = t.apply(this,arguments);
if (map._patch) map._patch(this);
return that||this;
};
window[name].prototype=t.prototype;
if (ext)
dhtmlx.extend(window[name].prototype,ext);
},
url:function(str){
if (str.indexOf("?") != -1)
return "&";
else
return "?";
}
};
};
_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.documentMode > 7) &&
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])
}
if (typeof(window.dhtmlxEvent) == "undefined") {
function dhtmlxEvent(el, event, handler){
if (el.addEventListener)
el.addEventListener(event, handler, false);
else if (el.attachEvent)
el.attachEvent("on"+event, handler);
}
};
if (dhtmlxEvent.touchDelay == null) {
dhtmlxEvent.touchDelay = 2000;
};
if (typeof(dhtmlxEvent.initTouch) == "undefined") {
dhtmlxEvent.initTouch = function(){
var longtouch;
var target;
var tx, ty;
dhtmlxEvent(document.body, "touchstart", function(ev){
target = ev.touches[0].target;
tx = ev.touches[0].clientX;
ty = ev.touches[0].clientY;
longtouch = window.setTimeout(touch_event, dhtmlxEvent.touchDelay);
});
function touch_event(){
if (target){
var ev = document.createEvent("HTMLEvents"); // for chrome and firefox
ev.initEvent("dblclick", true, true);
target.dispatchEvent(ev);
longtouch = target = null;
}
};
dhtmlxEvent(document.body, "touchmove", function(ev){
if (longtouch){
if (Math.abs(ev.touches[0].clientX - tx) > 50 || Math.abs(ev.touches[0].clientY - ty) > 50 ){
window.clearTimeout(longtouch);
longtouch = target = false;
}
}
});
dhtmlxEvent(document.body, "touchend", function(ev){
if (longtouch){
window.clearTimeout(longtouch);
longtouch = target = false;
}
});
dhtmlxEvent.initTouch = function(){};
};
};
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:"3.2.0"
};
/*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, this.config.original_target]) === 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, this.config.original_target]);
} 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);
return false;
}, this);
this._click.gantt_open = dhtmlx.bind(function (e, id, trg) {
this.open(id);
return false;
}, this);
this._click.gantt_row = dhtmlx.bind(function (e, id, trg) {
if (id !== null) {
var task = this.getTask(id);
if(this.config.scroll_on_click)
this.showDate(task.start_date);
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.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 item = { };
this.createTask(item, id ? id : this.config.root_id);
return false;
}, this);
if(this._init_resize){
this._init_resize();
}
};
gantt._render_grid = function () {
if (this._is_grid_visible()) {
this._calc_grid_width();
this._render_grid_header();
}
};
gantt._calc_grid_width = function () {
var columns = this.getGridColumns();
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;
}
if (this.config.autofit || unknown.length) {
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];
}
}else{
this.config.grid_width = cols_width;
}
};
gantt._render_grid_header = function () {
var columns = this.getGridColumns();
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.getGridColumns();
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") {
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._has_children(item.id);
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.getChildren(this.getParent(id));
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.getParent(id);
tindex = this.getTaskIndex(id);
}
if(sid == parent){
return;
}
parent = parent || this.config.root_id;
var source = this.getTask(sid);
var source_pid = this.getParent(source.id);
var sbranch = this.getChildren(this.getParent(source.id));
var tbranch = this.getChildren(parent);
if (tindex == -1)
tindex = tbranch.length + 1;
if (source_pid == parent) {
var sindex = this.getTaskIndex(sid);
if (sindex == tindex) return;
}
/*
prevent moving to another sub-branch:
gantt.attachEvent("onBeforeTaskMove", function(id, parent, tindex){
var task = gantt.getTask(id);
if(task.parent != parent)
return false;
return true;
});
*/
if(this.callEvent("onBeforeTaskMove", [sid, parent, tindex]) === false)
return;
this._replace_branch_child(source_pid, sid);
tbranch = this.getChildren(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));
this.setParent(source, parent);
this._branches[parent] = tbranch;
var childTree = this._getTaskTree(sid);
for(var i = 0; i < childTree.length; i++){
var item = this._pull[childTree[i]];
if(item)
item.$level = this.calculateTaskLevel(item);
}
if(tindex*1 > 0){
if(id){
source.$drop_target = (this.getTaskIndex(sid) > this.getTaskIndex(id) ? "next:" : '') + id;
}else{
source.$drop_target = "next:" + gantt.getPrevSibling(sid);
}
}else if(tbranch[tindex*1 + 1]){
source.$drop_target = tbranch[tindex*1 + 1];
}else{
source.$drop_target = parent;
}
if(!this.callEvent("onAfterTaskMove", [sid, parent, tindex]))
return;
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);
var task = gantt.getTask(id);
if(gantt._is_readonly(task))
return false;
dnd.config.initial_open_state = task.$open;
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);
dnd.config.index = this.getTaskIndex(dnd.config.id);
dnd.config.parent = task.parent;
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._getGridPos = dhtmlx.bind( function(e){
var pos = this._get_position(this.$grid_data);
// row offset
var x = pos.x;
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;
pos.x = x;
pos.y = y;
return pos;
}, this);
dnd.attachEvent("onDragMove", dhtmlx.bind(function (obj, e) {
var dd = dnd.config;
var pos = dnd._getGridPos(e);
// setting position of row
dd.marker.style.left = pos.x + 10 + "px";
dd.marker.style.top = pos.y + "px";
//previous action might cause page scroll appear thus change position of the gantt, need to recalculate
pos = dnd._getGridPos(e);
var x = pos.x,
y = pos.y;
// 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]]; //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 && next.id != item.id) {
this.moveTask(item.id, -1, this.getParent(next.id));
return;
}
}
}
//if item is on different level, check the one before it
var index = this.getGlobalTaskIndex(over.id),
prev = this._pull[this._order[index-1]];
var shift = 1;
while((!prev || prev.id == over.id) && index - shift >= 0){
prev = this._pull[this._order[index-shift]];
shift++;
}
if (item.id == over.id) return;
//replacing item under cursor
if (over.$level == item.$level && item.id != over.id) {
this.moveTask(item.id, 0, 0, over.id);
}else if(over.$level == item.$level - 1 && !gantt.getChildren(over.id).length){
this.moveTask(item.id, 0, over.id);
} else if(prev && (prev.$level == item.$level) && (item.id != prev.id)){
this.moveTask(item.id, -1, this.getParent(prev.id));
}
}
return true;
}, this));
dnd.attachEvent("onDragEnd", dhtmlx.bind(function () {
var task = this.getTask(dnd.config.id);
if(this.callEvent("onBeforeRowDragEnd",[dnd.config.id, dnd.config.index, dnd.config.parent]) === false) {
this.moveTask(dnd.config.id, dnd.config.index, dnd.config.parent);
task.$drop_target = null;
}else{
this.callEvent("onRowDragEnd", [dnd.config.id, task.$drop_target]);
}
task.$transparent = false;
task.$open = dnd.config.initial_open_state;
this.refreshData();
}, this));
};
/* will be overwriten in order to provide hide/show column functionality in some editions */
gantt.getGridColumns = function () {
return this.config.columns;
};
gantt._has_children = function(id){
return this.getChildren(id).length > 0;
};
// --#include core/grid_resize.js
// --#include core/dynamic_loading.js
// --#include core/grid_column_api.js
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){
if(cellSize(a.unit, a.step) < cellSize(b.unit, b.step)){
return 1;
}else if(cellSize(a.unit, a.step) > cellSize(b.unit, b.step)){
return -1;
}else{
return 0;
}
});
},
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]);
}
for(var i = 0; i < configs.length; i++){
this.setPosSettings(configs[i]);
}
return configs;
},
setPosSettings: function(config){
for(var i = 0, len = config.trace_x.length; i < len; i++){
config.left.push((config.width[i - 1] || 0) + (config.left[i - 1] || 0));
}
},
_ignore_time_config : function(date){
if(this.config.skip_off_time){
return !this.isWorkTime(date);
}
return false;
},
//defined in an extension
processIgnores : function(config){
config.ignore_x = {};
config.display_count = config.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:[],
left:[],
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);
if(+curr >= +end){
end = gantt.date.add(curr, step, unit);
}
while(+curr < +end){
callback.call(this, new Date(curr));
curr = gantt.date.add(curr, step, unit);
curr = gantt.date[unit + "_start"](curr);
}
},
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);
}
}
};
// --#include core/scales_ignore.js
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.dateFromPos(coords_x.start + shift);
if(!ev.start_date){
ev.start_date = new Date(gantt.getState().min_date);
}
}else{
ev.end_date =gantt.dateFromPos(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.dateFromPos(coords_x.start + shift),
new_end = gantt.dateFromPos(coords_x.end + shift);
if(!new_start){
ev.start_date = new Date(gantt.getState().min_date);
ev.end_date = gantt.dateFromPos(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.dateFromPos(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.dateFromPos(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;
var id =gantt.locate(e);
var task = null;
if(gantt.isTaskExists(id)){
task = gantt.getTask(id);
}
if (gantt._is_readonly(task) || 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]){
id = gantt.locate(src);
task = dhtmlx.copy(gantt.getTask(id) || {});
if(gantt._is_readonly(task)){
this.clear_drag_state();
return false;
}
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;
}
function fixStart(task){
if(!gantt.isWorkTime(task.start_date))
task.start_date = gantt.calculateEndDate(task.start_date, -1, gantt.config.duration_unit);
}
function fixEnd(task){
if(!gantt.isWorkTime(new Date(task.end_date - 1)))
task.end_date = gantt.calculateEndDate(task.end_date, 1, gantt.config.duration_unit);
}
if(drag.mode == gantt.config.drag_mode.resize){
if(drag.left){
task.start_date = gantt.roundDate({date:task.start_date, unit:unit, step:step});
fixStart(task);
}else{
task.end_date = gantt.roundDate({date:task.end_date, unit:unit, step:step});
fixEnd(task);
}
}else if(drag.mode == gantt.config.drag_mode.move){
task.start_date = gantt.roundDate({date:task.start_date, unit:unit, step:step});
fixStart(task);
task.end_date = gantt.calculateEndDate(task.start_date, task.duration, gantt.config.duration_unit);
}
},
_fix_working_times:function(task, drag){
var drag = drag || {mode : gantt.config.drag_mode.move};
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){
gantt.correctTaskWorkTime(task);
}
}
},
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);
this._fireEvent("after_finish", drag.mode, [drag_id, drag.mode, e]);
this.clear_drag_state();
gantt.updateTask(ev.id);
}
}
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.roundTaskDates = function(task){
var drag_state = gantt._tasks_dnd.drag;
if(!drag_state){
drag_state = {mode:gantt.config.drag_mode.move};
}
gantt._tasks_dnd._fix_dnd_scale_time(task, drag_state);
};
gantt._render_link = function(id){
var link = this.getLink(id);
var renders = gantt._get_link_renderers();
for(var i = 0; i < renders.length; i++)
renders[i].render_item(link);
};
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";
if(link.color){
css += " gantt_link_inline_color";
}
var cssTemplate = this.templates.link_class ? this.templates.link_class(link) : "";
if(cssTemplate){
css += " " + cssTemplate;
}
if(this.config.highlight_critical_path && this.isCriticalLink){
if(this.isCriticalLink(link))
css += " gantt_critical_link";
}
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;
}
var el = drawer.render_line(lines[i], lines[i+1]);
if(link.color){
el.firstChild.style.backgroundColor = link.color;
}
div.appendChild(el);
}
var direction = lines[lines.length - 1].direction;
var endpoint = gantt._render_link_arrow(dots[dots.length - 1], direction);
if(link.color){
endpoint.style.borderColor = link.color;
}
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) {
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);
if(gantt._is_readonly(t)){
resetDndState();
return false;
}
var shift = 0;
if(gantt._get_safe_type(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 drag = getDndState();
if(drag.from && drag.to && drag.from != drag.to){
var type = gantt._get_link_type(drag.from_start, drag.to_start);
var link = {source : drag.from, target: drag.to, type:type};
if(link.type && gantt.isLinkAllowed(link))
gantt.addLink(link);
}
resetDndState();
if(drag.from)
gantt.refreshTask(drag.from, false);
if(drag.to)
gantt.refreshTask(drag.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 = "