|
|
@@ -15,16 +15,13 @@ var Gantt = Class.extend({ |
|
|
|
init: function(opts) { |
|
|
|
this.opts = opts; |
|
|
|
this.events = this.opts.events; |
|
|
|
this.tasks = []; |
|
|
|
this._bars = []; |
|
|
|
this._arrows = []; |
|
|
|
this.set_defaults(); |
|
|
|
this.groups = {}; |
|
|
|
this.make(); |
|
|
|
this.prepare(); |
|
|
|
this.render(); |
|
|
|
}, |
|
|
|
set_defaults: function() { |
|
|
|
var defaults = { |
|
|
|
label_width: 40, |
|
|
|
label_width: 38, |
|
|
|
header_height: 50, |
|
|
|
column_width: 30, |
|
|
|
step: 24, |
|
|
@@ -43,27 +40,48 @@ var Gantt = Class.extend({ |
|
|
|
}, |
|
|
|
view_mode: 'Day', |
|
|
|
padding: 18, |
|
|
|
date_format: 'YYYY-MM-DD' |
|
|
|
date_format: 'DD-MM-YYYY' |
|
|
|
}; |
|
|
|
for(var key in defaults) { |
|
|
|
if(defaults.hasOwnProperty(key)) { |
|
|
|
if(!this.opts[key]) this.opts[key] = defaults[key]; |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
make: function() { |
|
|
|
this.canvas = Snap(this.opts.parent_selector); |
|
|
|
this.canvas.addClass("gantt"); |
|
|
|
this.prepare_filters(); |
|
|
|
|
|
|
|
this._bars = []; |
|
|
|
this._arrows = []; |
|
|
|
this.groups = {}; |
|
|
|
|
|
|
|
//prepare tasks |
|
|
|
var me = this; |
|
|
|
this.tasks = this.opts.tasks.map(function(task, i) { |
|
|
|
// momentify |
|
|
|
task._start = moment(task.start, me.opts.date_format); |
|
|
|
task._end = moment(task.end, me.opts.date_format); |
|
|
|
//index |
|
|
|
task._index = i; |
|
|
|
//invalid dates |
|
|
|
if(!task.start || !task.end) { |
|
|
|
task._start = moment().startOf('day'); |
|
|
|
task._end = moment().startOf('day').add(2, 'days'); |
|
|
|
task.invalid = true; |
|
|
|
} |
|
|
|
return task; |
|
|
|
}); |
|
|
|
//default view mode |
|
|
|
this.set_scale(this.opts.view_mode); |
|
|
|
}, |
|
|
|
prepare: function() { |
|
|
|
//TODO: check for valid dates |
|
|
|
this.start = this.end = undefined; |
|
|
|
this.prepare_dates(); |
|
|
|
this.render_canvas(); |
|
|
|
}, |
|
|
|
render: function() { |
|
|
|
this.clear(); |
|
|
|
|
|
|
|
this.setup_groups(); |
|
|
|
this.make_grid(); |
|
|
|
this.make_dates(); |
|
|
|
// this.make_label(); |
|
|
|
this.make_bars(); |
|
|
|
this.make_arrows(); |
|
|
|
this.set_arrows_on_bars(); |
|
|
@@ -75,8 +93,9 @@ var Gantt = Class.extend({ |
|
|
|
bind: function() { |
|
|
|
this.bind_grid_click(); |
|
|
|
}, |
|
|
|
prepare_filters: function () { |
|
|
|
this.filters = {}; |
|
|
|
render_canvas: function() { |
|
|
|
this.canvas = Snap(this.opts.parent_selector); |
|
|
|
this.canvas.addClass("gantt"); |
|
|
|
}, |
|
|
|
clear: function () { |
|
|
|
this.canvas.clear(); |
|
|
@@ -86,10 +105,6 @@ var Gantt = Class.extend({ |
|
|
|
prepare_dates: function() { |
|
|
|
var me = this; |
|
|
|
this.tasks.forEach(function(task) { |
|
|
|
// momentify |
|
|
|
task._start = moment(task.start, me.opts.date_format); |
|
|
|
task._end = moment(task.end, me.opts.date_format); |
|
|
|
|
|
|
|
// set global start and end date |
|
|
|
if(!me.start || task._start < me.start) { |
|
|
|
me.start = task._start; |
|
|
@@ -98,19 +113,22 @@ var Gantt = Class.extend({ |
|
|
|
me.end = task._end; |
|
|
|
} |
|
|
|
}); |
|
|
|
if(me.view_mode === 'Quarter Day' || me.view_mode === 'Half Day') { |
|
|
|
me.start = me.start.clone().subtract(1, 'day'); |
|
|
|
me.end = me.end.clone().add(1, 'day'); |
|
|
|
} else if(me.view_mode === 'Month') { |
|
|
|
this.set_gantt_dates(); |
|
|
|
this.setup_dates(); |
|
|
|
}, |
|
|
|
set_gantt_dates: function() { |
|
|
|
var me = this; |
|
|
|
if(me.view_is(['Quarter Day','Half Day'])) { |
|
|
|
me.start = me.start.clone().subtract(7, 'day'); |
|
|
|
me.end = me.end.clone().add(7, 'day'); |
|
|
|
} else if(me.view_is('Month')) { |
|
|
|
me.start = me.start.clone().startOf('year'); |
|
|
|
me.end = me.end.clone().endOf('month').add(1, 'year'); |
|
|
|
} else { |
|
|
|
me.start = me.start.clone().startOf('month'); |
|
|
|
me.end = me.end.clone().endOf('month'); |
|
|
|
me.start = me.start.clone().startOf('month').subtract(1, 'month'); |
|
|
|
me.end = me.end.clone().endOf('month').add(1, 'month'); |
|
|
|
} |
|
|
|
this.setup_dates(); |
|
|
|
}, |
|
|
|
|
|
|
|
setup_dates: function() { |
|
|
|
this.dates = []; |
|
|
|
var cur_date = null; |
|
|
@@ -118,21 +136,33 @@ var Gantt = Class.extend({ |
|
|
|
if(!cur_date) { |
|
|
|
cur_date = this.start.clone(); |
|
|
|
} else { |
|
|
|
cur_date = (this.view_mode === 'Month') ? |
|
|
|
cur_date = this.view_is('Month') ? |
|
|
|
cur_date = cur_date.clone().add(1, 'month'): |
|
|
|
cur_date.clone().add(this.opts.step, 'hours'); |
|
|
|
} |
|
|
|
this.dates.push(cur_date); |
|
|
|
} |
|
|
|
}, |
|
|
|
setup_groups: function() { |
|
|
|
var me = this; |
|
|
|
// make group layers |
|
|
|
["grid", "date", "arrow", |
|
|
|
"progress", "bar", "details"].forEach(function(name) { |
|
|
|
me.groups[name] = me.canvas.group().attr({'id': name}); |
|
|
|
}); |
|
|
|
}, |
|
|
|
get_view_modes: function() { |
|
|
|
return this.opts.valid_view_modes || []; |
|
|
|
}, |
|
|
|
set_view_mode: function(mode) { |
|
|
|
this.set_scale(mode); |
|
|
|
this.start = this.end = undefined; |
|
|
|
this.prepare_dates(); |
|
|
|
this.prepare(); |
|
|
|
this.render(); |
|
|
|
}, |
|
|
|
set_scale: function (scale) { |
|
|
|
this.view_mode = scale; |
|
|
|
|
|
|
|
//fire viewmode_change event |
|
|
|
this.events.on_viewmode_change(scale); |
|
|
|
if(scale === 'Day') { |
|
|
|
this.opts.step = 24; |
|
|
@@ -160,14 +190,6 @@ var Gantt = Class.extend({ |
|
|
|
this.tasks.push(task); |
|
|
|
this.prepare_dates(); |
|
|
|
}, |
|
|
|
setup_groups: function() { |
|
|
|
var me = this; |
|
|
|
// make groups |
|
|
|
["grid", "controls", "label", "date", |
|
|
|
"arrow", "progress", "bar", "details"].forEach(function(name) { |
|
|
|
me.groups[name] = me.canvas.group().attr({'id': name}); |
|
|
|
}); |
|
|
|
}, |
|
|
|
set_width: function () { |
|
|
|
var cur_width = this.canvas.node.getBoundingClientRect().width; |
|
|
|
var actual_width = this.canvas.getBBox().width; |
|
|
@@ -401,15 +423,7 @@ var Gantt = Class.extend({ |
|
|
|
} |
|
|
|
}); |
|
|
|
}, |
|
|
|
get_task: function (id) { |
|
|
|
var result = null; |
|
|
|
this.tasks.forEach(function (task) { |
|
|
|
if (task.id === id){ |
|
|
|
result = task; |
|
|
|
} |
|
|
|
}); |
|
|
|
return result; |
|
|
|
}, |
|
|
|
|
|
|
|
make_label: function () { |
|
|
|
var me = this; |
|
|
|
var label_x = me.opts.label_width - me.opts.padding, |
|
|
@@ -471,6 +485,29 @@ var Gantt = Class.extend({ |
|
|
|
el.removeClass('active'); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}, |
|
|
|
view_is: function(modes) { |
|
|
|
var me = this; |
|
|
|
if (typeof modes === 'string') { |
|
|
|
return me.view_mode === modes; |
|
|
|
} else { |
|
|
|
modes.reduce(function(acc, curr) { |
|
|
|
return (me.view_mode === curr) || acc |
|
|
|
}, false); |
|
|
|
// for (var i = 0; i < modes.length; i++) { |
|
|
|
// if(me.gantt.view_mode === modes[i]) return true; |
|
|
|
// } |
|
|
|
// return false; |
|
|
|
} |
|
|
|
}, |
|
|
|
get_task: function (id) { |
|
|
|
var result = null; |
|
|
|
this.tasks.forEach(function (task) { |
|
|
|
if (task.id === id){ |
|
|
|
result = task; |
|
|
|
} |
|
|
|
}); |
|
|
|
return result; |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
@@ -501,12 +538,6 @@ var Bar = Class.extend({ |
|
|
|
var defaults = { |
|
|
|
height: 20, |
|
|
|
corner_radius: 3, |
|
|
|
color: { |
|
|
|
bar: "#a3a3ff", |
|
|
|
progress: "#7575ff", |
|
|
|
hover_bar: "#7575ff", |
|
|
|
hover_progress: "#4d4da8" |
|
|
|
}, |
|
|
|
events: {} |
|
|
|
}; |
|
|
|
for(var key in defaults) { |
|
|
@@ -519,6 +550,9 @@ var Bar = Class.extend({ |
|
|
|
this.prepare_plugins(); |
|
|
|
}, |
|
|
|
prepare_values: function() { |
|
|
|
if(!this.task.start || !this.task.end){ |
|
|
|
this.invalid = true; |
|
|
|
} |
|
|
|
this.x = this.compute_x(); |
|
|
|
this.y = this.compute_y(); |
|
|
|
this.duration = (this.task._end.diff(this.task._start, 'hours') + 24)/this.gantt.step; |
|
|
@@ -557,8 +591,12 @@ var Bar = Class.extend({ |
|
|
|
this.corner_radius, this.corner_radius) |
|
|
|
.addClass("bar") |
|
|
|
.appendTo(this.bar_group); |
|
|
|
if(this.invalid) { |
|
|
|
this.bar.addClass('bar-invalid'); |
|
|
|
} |
|
|
|
}, |
|
|
|
draw_progress_bar: function() { |
|
|
|
if(this.invalid) return; |
|
|
|
this.bar_progress = this.canvas.rect(this.x, this.y, |
|
|
|
this.progress_width, this.height, |
|
|
|
this.corner_radius, this.corner_radius) |
|
|
@@ -574,6 +612,7 @@ var Bar = Class.extend({ |
|
|
|
this.update_label_position(this); |
|
|
|
}, |
|
|
|
draw_resize_handles: function() { |
|
|
|
if(this.invalid) return; |
|
|
|
var bar = this.group.select('.bar'); |
|
|
|
this.canvas.rect(bar.getX() + bar.getWidth() - 9, bar.getY() + 1, |
|
|
|
8, this.height - 2, this.corner_radius, this.corner_radius) |
|
|
@@ -583,15 +622,37 @@ var Bar = Class.extend({ |
|
|
|
8, this.height - 2, this.corner_radius, this.corner_radius) |
|
|
|
.addClass('handle left') |
|
|
|
.appendTo(this.handle_group); |
|
|
|
this.canvas.polygon( |
|
|
|
|
|
|
|
if(this.task.progress && this.task.progress < 100) { |
|
|
|
this.canvas.polygon( |
|
|
|
bar.getX() + this.progress_width - 5, bar.getY() + bar.get("height"), |
|
|
|
bar.getX() + this.progress_width + 5, bar.getY() + bar.get("height"), |
|
|
|
bar.getX() + this.progress_width, bar.getY() + bar.get("height") - 8.66 |
|
|
|
) |
|
|
|
.addClass('handle progress') |
|
|
|
.appendTo(this.handle_group); |
|
|
|
.appendTo(this.handle_group) |
|
|
|
} |
|
|
|
}, |
|
|
|
draw_invalid_bar: function() { |
|
|
|
var x = this.gantt.offset + |
|
|
|
(moment().startOf('day').diff(this.gantt.start, 'hours') / |
|
|
|
this.gantt.step * |
|
|
|
this.gantt.unit_width); |
|
|
|
|
|
|
|
this.canvas.rect(x, this.y, |
|
|
|
this.gantt.unit_width*2, this.height, |
|
|
|
this.corner_radius, this.corner_radius) |
|
|
|
.addClass("bar-invalid") |
|
|
|
.appendTo(this.bar_group); |
|
|
|
//continue here |
|
|
|
this.canvas.text(x + this.gantt.unit_width, |
|
|
|
this.y + this.height/2, |
|
|
|
'Dates not set') |
|
|
|
.addClass("bar-label big") |
|
|
|
.appendTo(this.bar_group); |
|
|
|
}, |
|
|
|
bind: function () { |
|
|
|
if(this.invalid) return; |
|
|
|
// this.show_details(); |
|
|
|
this.bind_resize(); |
|
|
|
this.bind_drag(); |
|
|
@@ -728,11 +789,19 @@ var Bar = Class.extend({ |
|
|
|
}, |
|
|
|
bind_resize_progress: function() { |
|
|
|
var me = this; |
|
|
|
var bar = me.group.select('.bar'); |
|
|
|
var bar_progress = me.group.select('.bar-progress'); |
|
|
|
var handle = me.group.select('.handle.progress'); |
|
|
|
handle.drag(onmove, onstart, onstop); |
|
|
|
handle && handle.drag(onmove, onstart, onstop); |
|
|
|
|
|
|
|
function onmove(dx, dy) { |
|
|
|
if(dx > bar_progress.max_dx) { |
|
|
|
dx = bar_progress.max_dx; |
|
|
|
} |
|
|
|
if(dx < bar_progress.min_dx) { |
|
|
|
dx = bar_progress.min_dx; |
|
|
|
} |
|
|
|
|
|
|
|
bar_progress.attr("width", bar_progress.owidth + dx); |
|
|
|
handle.transform("t"+dx+",0"); |
|
|
|
bar_progress.finaldx = dx; |
|
|
@@ -743,8 +812,10 @@ var Bar = Class.extend({ |
|
|
|
me.set_action_completed(); |
|
|
|
} |
|
|
|
function onstart() { |
|
|
|
bar_progress.owidth = bar_progress.getWidth(); |
|
|
|
bar_progress.finaldx = 0; |
|
|
|
bar_progress.owidth = bar_progress.getWidth(); |
|
|
|
bar_progress.min_dx = -bar_progress.getWidth(); |
|
|
|
bar_progress.max_dx = bar.getWidth() - bar_progress.getWidth(); |
|
|
|
} |
|
|
|
}, |
|
|
|
view_is: function(modes) { |
|
|
|