drag.js
var margin = {top:10, right: 10, bottom: 10, left: 50},
height = 500 - margin.top - margin.bottom,
height = 500 - margin.top - margin.bottom,
width = 500 - margin.left - margin.right;
var days = ['mon','tue','wed','thu','fri'];
var hours = ['08:00 AM','08:30 AM','09:00 AM','09:30 AM','10:00 AM','10:30 AM','11:00 AM','11:30 AM','12:00 PM','12:30 PM',
'01:00 PM','01:30 PM','02:00 PM','02:30 PM','03:00 PM','03:30 PM','04:00 PM','04:30 PM','05:00 PM','05:30 PM','06:00 PM',
'06:30 PM','07:00 PM','07:30 PM','08:00 PM','08:30 PM','09:00 PM','09:30 PM'];
var hourScaleRangeBand = height / hours.length;
function isColliding(course, trans, otherCourses) {
return _.chain(otherCourses).map(function(otherCourse) {
if (course.id != otherCourse.id) {
if (_.intersection(course.day, otherCourse.day).length > 0){
var otherStart = hourScale(format.parse(otherCourse.time.start));
var otherEnd = hourScale(format.parse(otherCourse.time.end));
var thisStart = trans[1];
var thisEnd = trans[1] + (hourScale(format.parse(course.time.end)) - hourScale(format.parse(course.time.start)));
return ((thisStart < otherStart && thisEnd > otherStart) || (thisStart > otherStart && thisStart < otherEnd));
} else {
return false;
}
} else {
return false;
}
}).contains(true).value();
}
courses = [
{
id: 0,
abbr: 'MATH243',
time: {
start: '09:00 AM',
end: '09:50 AM'
},
day: ['mon', 'tue', 'thu', 'fri']
},
{
id: 1,
abbr: 'CS332',
time: {
start: '10:30 AM',
end: '11:20 AM'
},
day: ['mon', 'wed', 'fri']
},
{
id: 2,
abbr: 'CS300',
time: {
start: '11:30 AM',
end: '12:20 PM'
},
day: ['mon','wed','fri']
},
{
id: 3,
abbr: 'PER181',
time: {
start: '10:30 AM',
end: '11:50 AM'
},
day: ['tue','thu']
},
{
id: 4,
abbr: 'CS384',
time: {
start: '06:30 PM',
end: '9:30 PM'
},
day: ['thu']
}
];
var week = [];
var format = d3.time.format("%I:%M %p");
var drag = d3.behavior.drag()
.origin(Object)
.on('drag', function (d, i) {
var t = d3.select(this);
var trans = d3.transform(t.attr('transform')).translate;
if (isColliding(d, trans, courses)) {
t.style('fill','red');
} else {
t.style('fill','black');
}
t.attr('transform', 'translate(0,' + Math.min(height - t.attr('height'), Math.max(0, +d3.event.dy + trans[1])) + ')');
})
.on('dragend', function (d, i) {
var t = d3.select(this);
var trans = d3.transform(t.attr('transform')).translate;
var hrs = hours[Math.floor(trans[1] / hourScaleRangeBand)];
if (isColliding(d, trans, courses)) {
t.attr('transform', function(d) { console.log(d.time.start); return 'translate(0,' + hourScale(format.parse(d.time.start)) + ')';});
t.style('fill','black');
} else {
t.attr('transform', function() { return 'translate(0,' + hourScale(format.parse(hrs)) + ')';});
var sectionDuration = (format.parse(courses[d.id].time.end) - format.parse(courses[d.id].time.start)) / 60000;
courses[d.id].time.start = hrs;
courses[d.id].time.end = format(d3.time.minute.offset(format.parse(hrs), sectionDuration));
updateCourseGroups();
};
});
var d3datehours = hours.map(format.parse);
days.forEach(function(day){
var hourObj = {};
hours.forEach(function(hour){
hourObj[hour] = {
occupied: false,
course: ''
};
});
week.push(hourObj.day = day);
});
var dayScale = d3.scale.ordinal()
.domain(days)
.rangeRoundBands([0, width], 0.01);
var hourScale = d3.time.scale()
.domain([d3datehours[0],d3datehours[27]])
.rangeRound([0, height]);
svg = d3.select('body').append('svg')
.attr('height', height + margin.top + margin.bottom)
.attr('width', width + margin.right + margin.left)
.append('g')
.attr('transform','translate(' + margin.left + ',' + margin.top + ')');
function makeHourAxis() {
return d3.svg.axis()
.scale(hourScale)
.orient('left')
}
function makeDayAxis() {
return d3.svg.axis()
.scale(dayScale)
.orient('top')
}
svg.append('g')
.attr('class', 'hour axis')
.attr('transform','translate(' + width + ',0)')
.attr('transform','translate(' + width + ',0)')
.call(makeHourAxis().ticks(hours.length).tickSize(width, 0, 0).tickFormat(""));
svg.append('g')
.attr('class', 'hour axis')
.call(makeHourAxis());
svg.append('g')
.attr('class', 'day axis')
.attr('transform', 'translate(' + (dayScale.rangeBand() / 2) + ',' + height + ')')
.call(makeDayAxis().tickSize(height, 0, 0).tickFormat(""));
svg.append('g')
.attr('class', 'day axis')
.call(makeDayAxis().tickSize(0));
var courseGroups = svg.selectAll('g.course')
.data(courses)
.enter().append('g')
.attr('class', 'course')
.attr('transform', function(d) {
return 'translate(' + 0 + ',' + hourScale(format.parse(d.time.start)) + ')';
})
.call(drag);
var sections = courseGroups.selectAll('g.section')
.data(function(d) {
return d.day.map(function(e){
return {day: e, time: d.time, abbr: d.abbr};
});
})
.enter().append('g')
.attr('class','section');
sections.append('rect')
.attr('class','section')
.attr('y', 0)
.attr('x', function(d){ return dayScale(d.day); })
.attr('width', dayScale.rangeBand())
.attr('height', function(d) {
return hourScale(format.parse(d.time.end)) - hourScale(format.parse(d.time.start));
});
sections.append('text')
.style('fill', 'white')
.attr('y', 10)
.attr('x', function(d){ return dayScale(d.day); })
.text(function(d){ return d.abbr; });
function updateCourseGroups() {
svg.selectAll('g.course')
.data(courses)
.attr('transform', function(d) {
return 'translate(' + 0 + ',' + hourScale(format.parse(d.time.start)) + ')';
});
}