%PDF- %PDF-
| Direktori : /home/opti/workflow/src/public/app-assets/js/scripts/charts/d3/pie/ |
| Current File : /home/opti/workflow/src/public/app-assets/js/scripts/charts/d3/pie/pie-calendar.js |
/*=========================================================================================
File Name: line.js
Description: D3 simple line chart
----------------------------------------------------------------------------------------
Item Name: Modern Admin - Clean Bootstrap 4 Dashboard HTML Template
Version: 1.0
Author: PIXINVENT
Author URL: http://www.themeforest.net/user/pixinvent
==========================================================================================*/
// Line chart
// ------------------------------
$(window).on("load", function(){
// Revealing module pattern to store some global data that will be shared between different functions.
var d3CalendarGlobals = function() {
var ele = d3.select("#pie-calendar"),
// calendarWidth = ele.node().getBoundingClientRect().width,
calendarWidth = 1380,
calendarHeight = 820,
gridXTranslation = 10,
gridYTranslation = 40,
cellColorForCurrentMonth = '#EAEAEA',
cellColorForPreviousMonth = '#FFFFFF',
counter = 0, // Counter is used to keep track of the number of "back" and "forward" button presses and to calculate the month to display.
currentMonth = new Date().getMonth(),
monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
datesGroup;
function publicCalendarWidth() {
return calendarWidth;
}
function publicCalendarHeight() {
return calendarHeight;
}
function publicGridXTranslation() {
return gridXTranslation;
}
function publicGridYTranslation() {
return gridYTranslation;
}
function publicGridWidth() {
return calendarWidth - 10;
}
function publicGridHeight() {
return calendarHeight - 40;
}
function publicCellWidth() {
return publicGridWidth() / 7;
}
function publicCellHeight() {
return publicGridHeight() / 5;
}
function publicGetDatesGroup() {
return datesGroup;
}
function publicSetDatesGroup(value) {
datesGroup = value;
}
function publicIncrementCounter() {
counter = counter + 1;
}
function publicDecrementCounter() {
counter = counter - 1;
}
function publicMonthToDisplay() {
var dateToDisplay = new Date();
// We use the counter that keep tracks of "back" and "forward" presses to get the month to display.
dateToDisplay.setMonth(currentMonth + counter);
return dateToDisplay.getMonth();
}
function publicMonthToDisplayAsText() {
return monthNames[publicMonthToDisplay()];
}
function publicYearToDisplay() {
var dateToDisplay = new Date();
// We use the counter that keep tracks of "back" and "forward" presses to get the year to display.
dateToDisplay.setMonth(currentMonth + counter);
return dateToDisplay.getFullYear();
}
function publicGridCellPositions() {
// We store the top left positions of a 7 by 5 grid. These positions will be our reference points for drawing
// various objects such as the rectangular grids, the text indicating the date etc.
var cellPositions = [];
for (y = 0; y < 5; y++) {
for (x = 0; x < 7; x++) {
cellPositions.push([x * publicCellWidth(), y * publicCellHeight()]);
}
}
return cellPositions;
}
// This function generates all the days of the month. But since we have a 7 by 5 grid, we also need to get some of
// the days from the previous month and the next month. This way our grid will have all its cells filled. The days
// from the previous or the next month will have a different color though.
function publicDaysInMonth() {
var daysArray = [];
var firstDayOfTheWeek = new Date(publicYearToDisplay(), publicMonthToDisplay(), 1).getDay();
var daysInPreviousMonth = new Date(publicYearToDisplay(), publicMonthToDisplay(), 0).getDate();
// Lets say the first week of the current month is a Wednesday. Then we need to get 3 days from
// the end of the previous month. But we can't naively go from 29 - 31. We have to do it properly
// depending on whether the last month was one that had 31 days, 30 days or 28.
for (i = 1; i <= firstDayOfTheWeek; i++) {
daysArray.push([daysInPreviousMonth - firstDayOfTheWeek + i, cellColorForCurrentMonth]);
}
// These are all the days in the current month.
var daysInMonth = new Date(publicYearToDisplay(), publicMonthToDisplay() + 1, 0).getDate();
for (i = 1; i <= daysInMonth; i++) {
daysArray.push([i, cellColorForPreviousMonth]);
}
// Depending on how many days we have so far (from previous month and current), we will need
// to get some days from next month. We can do this naively though, since all months start on
// the 1st.
var daysRequiredFromNextMonth = 35 - daysArray.length;
for (i = 1; i <= daysRequiredFromNextMonth; i++) {
daysArray.push([i, cellColorForCurrentMonth]);
}
return daysArray.slice(0, 35);
}
return {
calendarWidth: publicCalendarWidth(),
calendarHeight: publicCalendarHeight(),
gridXTranslation: publicGridXTranslation(),
gridYTranslation: publicGridYTranslation(),
gridWidth: publicGridWidth(),
gridHeight: publicGridHeight(),
cellWidth: publicCellWidth(),
cellHeight: publicCellHeight(),
getDatesGroup: publicGetDatesGroup,
setDatesGroup: publicSetDatesGroup,
incrementCounter: publicIncrementCounter,
decrementCounter: publicDecrementCounter,
monthToDisplay: publicMonthToDisplay(),
monthToDisplayAsText: publicMonthToDisplayAsText,
yearToDisplay: publicYearToDisplay,
gridCellPositions: publicGridCellPositions(),
daysInMonth: publicDaysInMonth
}
}();
$(document).ready(function() {
renderCalendarGrid();
renderDaysOfMonth();
$('#back').on('click',displayPreviousMonth);
$('#forward').on('click',displayNextMonth);
});
function displayPreviousMonth() {
// We keep track of user's "back" and "forward" presses in this counter
d3CalendarGlobals.decrementCounter();
renderDaysOfMonth();
}
function displayNextMonth() {
// We keep track of user's "back" and "forward" presses in this counter
d3CalendarGlobals.incrementCounter();
renderDaysOfMonth();
}
// This function is responsible for rendering the days of the month in the grid.
function renderDaysOfMonth(month, year) {
$('#currentMonth').text(d3CalendarGlobals.monthToDisplayAsText() + ' ' + d3CalendarGlobals.yearToDisplay());
// We get the days for the month we need to display based on the number of times the user has pressed
// the forward or backward button.
var daysInMonthToDisplay = d3CalendarGlobals.daysInMonth();
var cellPositions = d3CalendarGlobals.gridCellPositions;
// All text elements representing the dates in the month are grouped together in the "datesGroup" element by the initalizing
// function below. The initializing function is also responsible for drawing the rectangles that make up the grid.
d3CalendarGlobals.datesGroup
.selectAll("text")
.data(daysInMonthToDisplay)
.attr("x", function(d, i) {
return cellPositions[i][0];
})
.attr("y", function(d, i) {
return cellPositions[i][1];
})
.attr("dx", 20) // right padding
.attr("dy", 20) // vertical alignment : middle
.attr("transform", "translate(" + d3CalendarGlobals.gridXTranslation + "," + d3CalendarGlobals.gridYTranslation + ")")
.text(function(d) {
return d[0];
}); // Render text for the day of the week
d3CalendarGlobals.calendar
.selectAll("rect")
.data(daysInMonthToDisplay)
// Here we change the color depending on whether the day is in the current month, the previous month or the next month.
// The function that generates the dates for any given month will also specify the colors for days that are not part of the
// current month. We just have to use it to fill the rectangle
.style("fill", function(d) {
return d[1];
});
drawGraphsForMonthlyData();
}
function drawGraphsForMonthlyData() {
// Get some random data
var data = getDataForMonth();
// Set up variables required to draw a pie chart
var outerRadius = d3CalendarGlobals.cellWidth / 3;
var innerRadius = 0;
var pie = d3.layout.pie();
// var color = d3.scale.category10();
var color = d3.scale.ordinal()
.range(["#99B898", "#FECEA8", "#FF847C", "#E84A5F", "#F8B195", "#F67280", "#C06C84"]);
var arc = d3.svg.arc().innerRadius(innerRadius).outerRadius(outerRadius);
// We need to index and group the pie charts and slices generated so that they can be rendered in
// the appropriate cells. To do that, we call D3's 'pie' function of each of the data elements.
var indexedPieData = [];
for (i = 0; i < data.length; i++) {
var pieSlices = pie(data[i]);
// This loop is to store an index (j) for each of the slices of a given pie chart. Two different charts
// on two different days will have the the same set of numbers for slices (eg: 0,1,2). This will help us
// pick the same colors for the slices for two independent charts. Otherwise, the colors of the slices
// will be different each day.
for (j = 0; j < pieSlices.length; j++) {
indexedPieData.push([pieSlices[j], i, j]);
}
}
var cellPositions = d3CalendarGlobals.gridCellPositions;
d3CalendarGlobals.chartsGroup
.selectAll("g.arc")
.remove();
var arcs = d3CalendarGlobals.chartsGroup.selectAll("g.arc")
// use the indexed data so that each pie chart can be draw in a different cell and therefore for a different day
.data(indexedPieData)
.enter()
.append("g")
.attr("class", "arc")
.attr("transform", function(d) {
// This is where we use the index here to translate the pie chart and rendere it in the appropriate cell.
// Normally, the chart would be squashed up against the top left of the cell, obscuring the text that shows the day of the month.
// We use the gridXTranslation and gridYTranslation and multiply it by a factor to move it to the center of the cell. There is probably
// a better way of doing this though.
var currentDataIndex = d[1];
return "translate(" + (outerRadius + d3CalendarGlobals.gridXTranslation * 5 + cellPositions[currentDataIndex][0]) + ", " + (outerRadius + d3CalendarGlobals.gridYTranslation * 1.25 + cellPositions[currentDataIndex][1]) + ")";
});
arcs.append("path")
.attr("fill", function(d, i) {
// The color is generated using the second index. Each slice of the pie is given a fixed number. This applies to all charts (see the indexing loop above).
// This way, by using the index we can generate teh same colors for each of the slices for different charts on different days.
return color(d[2]);
})
.attr("d", function(d, i) {
// Standard functions for drawing a pie charts in D3.
return arc(d[0]);
});
arcs.append("text")
.attr("transform", function(d, i) {
// Standard functions for drawing a pie charts in D3.
return "translate(" + arc.centroid(d[0]) + ")";
})
.attr("text-anchor", "middle")
.text(function(d, i) {
return d[0].value;
})
.style("fill", "#FFF");
}
// Generates some random data that can be used to draw pie charts.
function getDataForMonth() {
var randomData = [];
for (var i = 0; i < 35; i++) {
randomData.push([Math.floor(Math.random() * 100), Math.floor(Math.random() * 100), Math.floor(Math.random() * 100)]);
}
return randomData;
}
// This is the initializing function. It adds an svg element, draws a set of rectangles to form the calendar grid,
// puts text in each cell representing the date and does the initial rendering of the pie charts.
function renderCalendarGrid(month, year) {
// Add the svg element.
d3CalendarGlobals.calendar = d3.select("#pie-calendar")
.append("svg")
.attr("class", "calendar")
.attr("width", d3CalendarGlobals.calendarWidth)
.attr("height", d3CalendarGlobals.calendarHeight)
.append("g");
// Cell positions are generated and stored globally because they are used by other functions as a reference to render different things.
var cellPositions = d3CalendarGlobals.gridCellPositions;
// Draw rectangles at the appropriate postions, starting from the top left corner. Since we want to leave some room for the heading and buttons,
// use the gridXTranslation and gridYTranslation variables.
d3CalendarGlobals.calendar.selectAll("rect")
.data(cellPositions)
.enter()
.append("rect")
.attr("x", function(d) {
return d[0];
})
.attr("y", function(d) {
return d[1];
})
.attr("width", d3CalendarGlobals.cellWidth)
.attr("height", d3CalendarGlobals.cellHeight)
.style("stroke", "#555")
.style("fill", "white")
.attr("transform", "translate(" + d3CalendarGlobals.gridXTranslation + "," + d3CalendarGlobals.gridYTranslation + ")");
var daysOfTheWeek = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
// This adds the day of the week headings on top of the grid
d3CalendarGlobals.calendar.selectAll("headers")
.data([0, 1, 2, 3, 4, 5, 6])
.enter().append("text")
.attr("x", function(d) {
return cellPositions[d][0];
})
.attr("y", function(d) {
return cellPositions[d][1];
})
.attr("dx", d3CalendarGlobals.gridXTranslation + 5) // right padding
.attr("dy", 30) // vertical alignment : middle
.text(function(d) {
return daysOfTheWeek[d]
});
// The intial rendering of the dates for the current mont inside each of the cells in the grid. We create a named group ("datesGroup"),
// and add our dates to this group. This group is also stored globally. Later on, when the the user presses the back and forward buttons
// to navigate between the months, we clear and re add the new text elements to this group
d3CalendarGlobals.datesGroup = d3CalendarGlobals.calendar.append("svg:g");
var daysInMonthToDisplay = d3CalendarGlobals.daysInMonth();
d3CalendarGlobals.datesGroup
.selectAll("daysText")
.data(daysInMonthToDisplay)
.enter()
.append("text")
.attr("x", function(d, i) {
return cellPositions[i][0];
})
.attr("y", function(d, i) {
return cellPositions[i][1];
})
.attr("dx", 20) // right padding
.attr("dy", 20) // vertical alignment : middle
.attr("transform", "translate(" + d3CalendarGlobals.gridXTranslation + "," + d3CalendarGlobals.gridYTranslation + ")")
.text(function(d) {
return d[0];
});
// Create a new svg group to store the chart elements and store it globally. Again, as the user navigates through the months by pressing
// the "back" and "forward" buttons on the page, we clear the chart elements from this group and re add them again.
d3CalendarGlobals.chartsGroup = d3CalendarGlobals.calendar.append("svg:g");
// Call the function to draw the charts in the cells. This will be called again each time the user presses the forward or backward buttons.
drawGraphsForMonthlyData();
}
// Resize chart
// ------------------------------
// Call function on window resize
$(window).on('resize', resize);
// Call function on sidebar width change
$('.menu-toggle').on('click', resize);
// Resize function
// ------------------------------
function resize() {
width = ele.node().getBoundingClientRect().width - margin.left - margin.right;
// Main svg width
container.attr("width", width + margin.left + margin.right);
// Width of appended group
svg.attr("width", width + margin.left + margin.right);
// Axis
// -------------------------
x.range([0, width]);
svg.selectAll('.d3-xaxis').call(xAxis);
svg.selectAll('.d3-line').attr("d", line);
}
});