var taskStatus = {'Stand':'Stand', 'Table ronde':'Table-ronde', 'Atelier':'Atelier', 'Concert':'Concert', 'Conference':'Conference', 'Repas':'Repas'}; var tickFormat = "%H:%M"; var margin = { top : 15, right : 0, bottom : 0, left : 50 }; var height = 500; var width = 730; var Keys = Array(); var Salles = Array(); var Areas = Array(); var Tasks = Array(); d3.json("le-prog-json", function(error, json) { if (error) return console.warn(error); var Keys = Object.keys(json["all"]); // Build set of data in memory for (var k = 0; k < Keys.length; k++) { Salles[Keys[k]] = Array(); Areas[Keys[k]] = Array(); Tasks[Keys[k]] = json["all"][Keys[k]]; // Fix Json to make date objects Tasks[Keys[k]].forEach(function(d) { d.startDate = new Date(d.startDate); d.endDate = new Date(d.endDate); }); // Build the list of Salles and Areas Tasks[Keys[k]].forEach(function(d) { Salles[Keys[k]].push( d.placeName ); Areas[Keys[k]].push( d.status ); } ); Salles[Keys[k]] = d3.set(Salles[Keys[k]]).values(); Areas[Keys[k]] = d3.set(Areas[Keys[k]]).values(); // Create Controls to Handle user selection Areas[Keys[k]].forEach(function(TypeArea) { d3.select("#Schedule_SVG_"+Keys[k]) .append("input") .attr("type", "checkbox") .attr("name", "OptTimetable-"+Keys[k]) .attr("id", TypeArea+"-"+Keys[k]) .attr("area", TypeArea) .style("float", "left") .style("margin-left", "20px") .style("margin-right", "3px") .property("checked",true); d3.select("#Schedule_SVG_"+Keys[k]) .append("label") .attr("for", TypeArea+"-"+Keys[k]) .style("float", "left") .style("margin-left", "5px") .text(TypeArea); }); d3.select("#Schedule_SVG_"+Keys[k]) .append("br"); // Create a dedicated SVG for it svg = d3.select("#Schedule_SVG_"+Keys[k]) .append("svg") .attr("id", "TimeTable-"+Keys[k]) .attr("width", "100%") //width + margin.right + margin.left) //.attr("height", height + margin.top + margin.bottom) .attr("viewBox", "0 0 " + (width + margin.right + margin.left) + " " + (height + margin.top + margin.bottom) ) .attr("preserveAspectRatio", "xMidYMid meet"); // Add some gradient header(svg); // Create axis svg.append("g") .attr("class", "xAxis axis"); svg.append("g") .attr("class", "yAxis axis"); var chart = svg.append("g") .attr("class", "timetable"); HandleEvents(Keys[k]); displayit(json["all"][Keys[k]], Salles[Keys[k]], Keys[k]); } }); function header(svg) { defs = svg.append("svg:defs"); conf = defs.append("svg:linearGradient") .attr("id", "BoxGradient-Conference") .attr("x1", "100%") .attr("y1", "0%") .attr("x2", "50%") .attr("y2", "100%") .attr("spreadMethod", "pad"); conf.append("svg:stop") .attr("offset", "0%") .attr("stop-color", "#EEE") .attr("stop-opacity", 0); conf.append("svg:stop") .attr("offset", "15%") .attr("stop-color", "#BDF86B") .attr("stop-opacity", .5); atlier = defs.append("svg:linearGradient") .attr("id", "BoxGradient-Atelier") .attr("x1", "100%") .attr("y1", "0%") .attr("x2", "50%") .attr("y2", "100%") .attr("spreadMethod", "pad"); atlier.append("svg:stop") .attr("offset", "0%") .attr("stop-color", "#EEE") .attr("stop-opacity", 0); atlier.append("svg:stop") .attr("offset", "15%") .attr("stop-color", "#FF856E") .attr("stop-opacity", .5); // append filter element var filter = defs.append( 'filter' ) .attr( 'id', 'dropshadow' ); /// !!! important - define id to reference it later // append gaussian blur to filter filter.append( 'feGaussianBlur' ) .attr( 'in', 'SourceAlpha' ) .attr( 'stdDeviation', 3 ) // !!! important parameter - blur .attr( 'result', 'blur' ); // append offset filter to result of gaussion blur filter filter.append( 'feOffset' ) .attr( 'in', 'blur' ) .attr( 'dx', 2 ) // !!! important parameter - x-offset .attr( 'dy', 2 ) // !!! important parameter - y-offset .attr( 'result', 'offsetBlur' ); filter.append( 'feComponentTransfer' ) .append( 'feFuncA' ) .attr( "type", "linear" ) .attr( "slope", "0.4" ); // merge result with original image var feMerge = filter.append( 'feMerge' ); // first layer result of blur and offset feMerge.append( 'feMergeNode' ) .attr( 'in", "offsetBlur' ); // original image on top feMerge.append( 'feMergeNode' ) .attr( 'in', 'SourceGraphic' ); // end filter stuff } var keyFunction = function(d) { return d.startDate + d.placeName + d.endDate; }; function HandleEvents(Ctrl) { d3.selectAll("input[name=OptTimetable-"+Ctrl+"]") .data(Areas[Ctrl]) .on("change", function(TypeData) { ArrayChoice = Array(); checked = d3.selectAll("input[name=OptTimetable-"+Ctrl+"]")[0] .forEach( function(v) { if (d3.select(v).node().checked) ArrayChoice.push(v.attributes['area'].value); } ); area_select = Array(); selection = Tasks[Ctrl].filter(function(v) { return ArrayChoice.indexOf(v.status)>=0; }); if (selection.length) { selection.forEach(function(d) { area_select.push( d.placeName ); } ); area_select = d3.set(area_select).values(); displayit(selection, area_select, Ctrl); d3.select("#TimeTable-"+Ctrl)[0][0].style.opacity = 1; } else { d3.select("#TimeTable-"+Ctrl)[0][0].style.opacity = 0; } }); } function displayit(Set_of_Task, Set_of_Area, key) { // Try to compute time range Set_of_Task.sort(function(a, b) { return a.startDate - b.startDate; }); timeDomainStart = Set_of_Task[0].startDate; Set_of_Task.sort(function(a, b) { return a.endDate - b.endDate; }); timeDomainEnd = Set_of_Task[Set_of_Task.length - 1].endDate; // Prepare scales xScale = d3.scale.ordinal() .domain(Set_of_Area) .rangeRoundBands([ 0, width ], .1); yScale = d3.time.scale() .domain([ timeDomainStart, timeDomainEnd ]) .range([ 0, height - margin.top - margin.bottom ]) .clamp(true); xAxis = d3.svg.axis() .scale(xScale) .orient("top") .tickSize(1); yAxis = d3.svg.axis() .scale(yScale) .orient("left") .tickFormat(d3.time.format(tickFormat)) .tickSubdivide(true) .tickSize(8) .tickPadding(8); var svg = d3.select("#TimeTable-"+key); var chart = svg.select(".timetable"); var rect = chart.selectAll("rect") .data(Set_of_Task, keyFunction); rect.enter() .insert("rect") .attr("rx", 5) .attr("ry", 5) .attr("filter", "url(#dropshadow)") .style("fill", function(d){ return "url(#BoxGradient-"+ taskStatus[d.status] +")"; }) .attr("class", function(d){ if(taskStatus[d.status] == null) { return "bar";} return taskStatus[d.status]; }) .transition() .duration(750); rect.transition() .duration(750) .attr("x", function(d){ return xScale(d.placeName || "unk"); }) .attr("y", function(d){ return yScale( d.startDate )+1; }) .attr("width", function(d) { return xScale.rangeBand(); }) .attr("height", function(d) { return (yScale( d.endDate ) - yScale( d.startDate )-2); }); rect.exit() .transition() .duration(300) .style("opacity", 1e-6) .remove(); var content = chart.selectAll("foreignObject") .data(Set_of_Task, keyFunction); content.enter() .insert("foreignObject") .append("xhtml:div") .attr("class", "SvgBody") .html(function(d) { return '
' + d.desc + '
'; }) .transition() .duration(750); content.transition() .duration(750) .attr("x", function(d){ return xScale(d.placeName || "unk"); }) .attr("y", function(d){ return yScale( d.startDate ); }) .attr("width", function(d) { return xScale.rangeBand(); }) .attr("height", function(d) { return (yScale( d.endDate ) - yScale( d.startDate )); }); // .attr("transform" ,"scale(1)"); content.exit() .transition() .duration(300) .style("opacity", 1e-6) .remove(); svg.select(".yAxis") .attr("transform", "translate("+ margin.left +","+ margin.top +")") .transition() .call(yAxis); svg.select(".xAxis") .attr("transform", "translate("+ margin.left +","+ margin.top +")") .transition() .call(xAxis); chart.attr("transform", "translate("+ margin.left +","+ margin.top +")"); }