cal_heatmap_rails 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +7 -0
- data/lib/cal_heatmap_rails/version.rb +2 -2
- data/vendor/assets/javascripts/cal-heatmap.js +446 -373
- data/vendor/assets/javascripts/cal-heatmap.min.js +3 -2
- metadata +2 -1
@@ -1,4 +1,4 @@
|
|
1
|
-
/*! cal-heatmap v3.0
|
1
|
+
/*! cal-heatmap v3.1.0 (Thu Aug 08 2013 01:26:26)
|
2
2
|
* ---------------------------------------------
|
3
3
|
* Cal-Heatmap is a javascript module to create calendar heatmap to visualize time series data, a la github contribution graph
|
4
4
|
* https://github.com/kamisama/cal-heatmap
|
@@ -198,7 +198,7 @@ var CalHeatMap = function() {
|
|
198
198
|
|
199
199
|
|
200
200
|
// ================================================
|
201
|
-
// CALLBACK
|
201
|
+
// EVENTS CALLBACK
|
202
202
|
// ================================================
|
203
203
|
|
204
204
|
// Callback when clicking on a time block
|
@@ -208,10 +208,10 @@ var CalHeatMap = function() {
|
|
208
208
|
afterLoad : null,
|
209
209
|
|
210
210
|
// Callback after loading the next domain in the calendar
|
211
|
-
afterLoadNextDomain :
|
211
|
+
afterLoadNextDomain : null,
|
212
212
|
|
213
213
|
// Callback after loading the previous domain in the calendar
|
214
|
-
afterLoadPreviousDomain :
|
214
|
+
afterLoadPreviousDomain : null,
|
215
215
|
|
216
216
|
// Callback after finishing all actions on the calendar
|
217
217
|
onComplete : null,
|
@@ -228,7 +228,7 @@ var CalHeatMap = function() {
|
|
228
228
|
//
|
229
229
|
// This callback is also executed once, after calling previous(),
|
230
230
|
// only when the max domain is reached
|
231
|
-
onMaxDomainReached:
|
231
|
+
onMaxDomainReached: null,
|
232
232
|
|
233
233
|
// Callback triggered after calling previous().
|
234
234
|
// The `status` argument is equal to true if there is no
|
@@ -236,17 +236,16 @@ var CalHeatMap = function() {
|
|
236
236
|
//
|
237
237
|
// This callback is also executed once, after calling next(),
|
238
238
|
// only when the min domain is reached
|
239
|
-
onMinDomainReached:
|
239
|
+
onMinDomainReached: null
|
240
240
|
};
|
241
241
|
|
242
242
|
|
243
|
-
|
244
243
|
this._domainType = {
|
245
244
|
"min" : {
|
246
245
|
name: "minute",
|
247
246
|
level: 10,
|
248
|
-
row: function(
|
249
|
-
column: function(
|
247
|
+
row: function() {return 10;},
|
248
|
+
column: function() { return 6; },
|
250
249
|
position: {
|
251
250
|
x : function(d) { return Math.floor(d.getMinutes() / self._domainType.min.row(d)); },
|
252
251
|
y : function(d) { return d.getMinutes() % self._domainType.min.row(d);}
|
@@ -256,12 +255,15 @@ var CalHeatMap = function() {
|
|
256
255
|
legend: "",
|
257
256
|
connector: "at"
|
258
257
|
},
|
259
|
-
extractUnit : function(d) {
|
258
|
+
extractUnit : function(d) {
|
259
|
+
var dt = new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes());
|
260
|
+
return dt.getTime();
|
261
|
+
}
|
260
262
|
},
|
261
263
|
"hour" : {
|
262
264
|
name: "hour",
|
263
265
|
level: 20,
|
264
|
-
row: function(
|
266
|
+
row: function() {return 6;},
|
265
267
|
column: function(d) {
|
266
268
|
switch(self.options.domain) {
|
267
269
|
case "day" : return 4;
|
@@ -286,14 +288,14 @@ var CalHeatMap = function() {
|
|
286
288
|
connector: "at"
|
287
289
|
},
|
288
290
|
extractUnit : function(d) {
|
289
|
-
var
|
290
|
-
return
|
291
|
+
var dt = new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours());
|
292
|
+
return dt.getTime();
|
291
293
|
}
|
292
294
|
},
|
293
295
|
"day" : {
|
294
296
|
name: "day",
|
295
297
|
level: 30,
|
296
|
-
row: function(
|
298
|
+
row: function() {return 7;},
|
297
299
|
column: function(d) {
|
298
300
|
d = new Date(d);
|
299
301
|
switch(self.options.domain) {
|
@@ -322,12 +324,15 @@ var CalHeatMap = function() {
|
|
322
324
|
legend: "%e %b",
|
323
325
|
connector: "on"
|
324
326
|
},
|
325
|
-
extractUnit : function(d) {
|
327
|
+
extractUnit : function(d) {
|
328
|
+
var dt = new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
329
|
+
return dt.getTime();
|
330
|
+
}
|
326
331
|
},
|
327
332
|
"week" : {
|
328
333
|
name: "week",
|
329
334
|
level: 40,
|
330
|
-
row: function(
|
335
|
+
row: function() {return 1;},
|
331
336
|
column: function(d) {
|
332
337
|
d = new Date(d);
|
333
338
|
switch(self.options.domain) {
|
@@ -343,7 +348,7 @@ var CalHeatMap = function() {
|
|
343
348
|
case "month" : return self.getWeekNumber(d) - self.getWeekNumber(new Date(d.getFullYear(), d.getMonth())) - 1;
|
344
349
|
}
|
345
350
|
},
|
346
|
-
y: function(
|
351
|
+
y: function() {
|
347
352
|
return 0;
|
348
353
|
}
|
349
354
|
},
|
@@ -352,13 +357,22 @@ var CalHeatMap = function() {
|
|
352
357
|
legend: "%B Week #%W",
|
353
358
|
connector: "on"
|
354
359
|
},
|
355
|
-
extractUnit : function(d) {
|
360
|
+
extractUnit : function(d) {
|
361
|
+
var dt = new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
362
|
+
// According to ISO-8601, week number computation are based on week starting on Monday
|
363
|
+
var weekDay = dt.getDay()-1;
|
364
|
+
if (weekDay < 0) {
|
365
|
+
weekDay = 6;
|
366
|
+
}
|
367
|
+
dt.setDate(dt.getDate() - weekDay);
|
368
|
+
return dt.getTime();
|
369
|
+
}
|
356
370
|
},
|
357
371
|
"month" : {
|
358
372
|
name: "month",
|
359
373
|
level: 50,
|
360
|
-
row: function(
|
361
|
-
column: function(
|
374
|
+
row: function() {return 1;},
|
375
|
+
column: function() {return 12;},
|
362
376
|
position: {
|
363
377
|
x : function(d) { return Math.floor(d.getMonth() / self._domainType.month.row(d)); },
|
364
378
|
y : function(d) { return d.getMonth() % self._domainType.month.row(d);}
|
@@ -368,13 +382,16 @@ var CalHeatMap = function() {
|
|
368
382
|
legend: "%B",
|
369
383
|
connector: "on"
|
370
384
|
},
|
371
|
-
extractUnit : function(d) {
|
385
|
+
extractUnit : function(d) {
|
386
|
+
var dt = new Date(d.getFullYear(), d.getMonth());
|
387
|
+
return dt.getTime();
|
388
|
+
}
|
372
389
|
},
|
373
390
|
"year" : {
|
374
391
|
name: "year",
|
375
392
|
level: 60,
|
376
|
-
row: function(
|
377
|
-
column: function(
|
393
|
+
row: function() {return 1;},
|
394
|
+
column: function() {return 12;},
|
378
395
|
position: {
|
379
396
|
x : function(d) { return Math.floor(d.getFullYear() / this._domainType.year.row(d)); },
|
380
397
|
y : function(d) { return d.getFullYear() % this._domainType.year.row(d);}
|
@@ -384,7 +401,10 @@ var CalHeatMap = function() {
|
|
384
401
|
legend: "%Y",
|
385
402
|
connector: "on"
|
386
403
|
},
|
387
|
-
extractUnit : function(d) {
|
404
|
+
extractUnit : function(d) {
|
405
|
+
var dt = new Date(d.getFullYear());
|
406
|
+
return dt.getTime();
|
407
|
+
}
|
388
408
|
}
|
389
409
|
};
|
390
410
|
|
@@ -423,7 +443,7 @@ var CalHeatMap = function() {
|
|
423
443
|
|
424
444
|
// Record all the valid domains
|
425
445
|
// Each domain value is a timestamp in milliseconds
|
426
|
-
this._domains =
|
446
|
+
this._domains = d3.map();
|
427
447
|
|
428
448
|
var graphDim = {
|
429
449
|
width: 0,
|
@@ -433,6 +453,11 @@ var CalHeatMap = function() {
|
|
433
453
|
this.NAVIGATE_LEFT = 1;
|
434
454
|
this.NAVIGATE_RIGHT = 2;
|
435
455
|
|
456
|
+
// Various update mode when using the update() API
|
457
|
+
this.RESET_ALL_ON_UPDATE = 0;
|
458
|
+
this.RESET_SINGLE_ON_UPDATE = 1;
|
459
|
+
this.APPEND_ON_UPDATE = 2;
|
460
|
+
|
436
461
|
this.root = null;
|
437
462
|
|
438
463
|
this._maxDomainReached = false;
|
@@ -446,7 +471,9 @@ var CalHeatMap = function() {
|
|
446
471
|
*/
|
447
472
|
function _init() {
|
448
473
|
|
449
|
-
self.
|
474
|
+
self.getDomain(self.options.start).map(function(d) { return d.getTime(); }).map(function(d) {
|
475
|
+
self._domains.set(d, self.getSubDomain(d).map(function(d) { return {t: self._domainType[self.options.subDomain].extractUnit(d), v: null}; }));
|
476
|
+
});
|
450
477
|
|
451
478
|
self.root = d3.select(self.options.itemSelector);
|
452
479
|
|
@@ -454,20 +481,35 @@ var CalHeatMap = function() {
|
|
454
481
|
|
455
482
|
if (self.options.paintOnLoad) {
|
456
483
|
|
484
|
+
self.verticalDomainLabel = (self.options.label.position === "top" || self.options.label.position === "bottom");
|
485
|
+
|
486
|
+
self.domainVerticalLabelHeight = Math.max(25, self.options.cellSize*2);
|
487
|
+
self.domainHorizontalLabelWidth = 0;
|
488
|
+
|
489
|
+
if (!self.verticalDomainLabel) {
|
490
|
+
self.domainVerticalLabelHeight = 0;
|
491
|
+
self.domainHorizontalLabelWidth = self.options.label.width;
|
492
|
+
}
|
493
|
+
|
494
|
+
// @todo : check validity
|
495
|
+
if (typeof self.options.domainMargin === "number") {
|
496
|
+
self.options.domainMargin = [self.options.domainMargin, self.options.domainMargin, self.options.domainMargin, self.options.domainMargin];
|
497
|
+
}
|
498
|
+
|
457
499
|
self.paint();
|
458
500
|
|
459
501
|
// =========================================================================//
|
460
502
|
// ATTACHING DOMAIN NAVIGATION EVENT //
|
461
503
|
// =========================================================================//
|
462
504
|
if (self.options.nextSelector !== false) {
|
463
|
-
d3.select(self.options.nextSelector).on("click." + self.options.itemNamespace, function(
|
505
|
+
d3.select(self.options.nextSelector).on("click." + self.options.itemNamespace, function() {
|
464
506
|
d3.event.preventDefault();
|
465
507
|
return self.loadNextDomain();
|
466
508
|
});
|
467
509
|
}
|
468
510
|
|
469
511
|
if (self.options.previousSelector !== false) {
|
470
|
-
d3.select(self.options.previousSelector).on("click." + self.options.itemNamespace, function(
|
512
|
+
d3.select(self.options.previousSelector).on("click." + self.options.itemNamespace, function() {
|
471
513
|
d3.event.preventDefault();
|
472
514
|
return self.loadPreviousDomain();
|
473
515
|
});
|
@@ -484,12 +526,14 @@ var CalHeatMap = function() {
|
|
484
526
|
|
485
527
|
// Fill the graph with some datas
|
486
528
|
if (self.options.loadOnInit) {
|
529
|
+
var domains = self._domains.keys().sort();
|
487
530
|
self.getDatas(
|
488
531
|
self.options.data,
|
489
|
-
new Date(
|
490
|
-
self.getSubDomain(
|
491
|
-
function(
|
492
|
-
self.fill(
|
532
|
+
new Date(parseInt(domains[0], 10)),
|
533
|
+
self.getSubDomain(parseInt(domains[domains.length-1], 10)).pop(),
|
534
|
+
function() {
|
535
|
+
self.fill();
|
536
|
+
self.onComplete();
|
493
537
|
}
|
494
538
|
);
|
495
539
|
} else {
|
@@ -500,6 +544,25 @@ var CalHeatMap = function() {
|
|
500
544
|
return true;
|
501
545
|
}
|
502
546
|
|
547
|
+
// Return the width of the domain block, without the domain gutter
|
548
|
+
// @param int d Domain start timestamp
|
549
|
+
function w(d, outer) {
|
550
|
+
var width = self.options.cellSize*self._domainType[self.options.subDomain].column(d) + self.options.cellPadding*self._domainType[self.options.subDomain].column(d);
|
551
|
+
if (typeof outer !== "undefined" && outer === true) {
|
552
|
+
return width += self.domainHorizontalLabelWidth + self.options.domainGutter + self.options.domainMargin[1] + self.options.domainMargin[3];
|
553
|
+
}
|
554
|
+
return width;
|
555
|
+
}
|
556
|
+
|
557
|
+
// Return the height of the domain block, without the domain gutter
|
558
|
+
function h(d, outer) {
|
559
|
+
var height = self.options.cellSize*self._domainType[self.options.subDomain].row(d) + self.options.cellPadding*self._domainType[self.options.subDomain].row(d);
|
560
|
+
if (typeof outer !== "undefined" && outer === true) {
|
561
|
+
height += self.options.domainGutter + self.domainVerticalLabelHeight + self.options.domainMargin[0] + self.options.domainMargin[2];
|
562
|
+
}
|
563
|
+
return height;
|
564
|
+
}
|
565
|
+
|
503
566
|
|
504
567
|
/**
|
505
568
|
*
|
@@ -512,44 +575,10 @@ var CalHeatMap = function() {
|
|
512
575
|
navigationDir = false;
|
513
576
|
}
|
514
577
|
|
515
|
-
var verticalDomainLabel = (self.options.label.position === "top" || self.options.label.position === "bottom");
|
516
|
-
|
517
|
-
var domainVerticalLabelHeight = Math.max(25, self.options.cellSize*2);
|
518
|
-
var domainHorizontalLabelWidth = 0;
|
519
|
-
|
520
|
-
if (!verticalDomainLabel) {
|
521
|
-
domainVerticalLabelHeight = 0;
|
522
|
-
domainHorizontalLabelWidth = self.options.label.width;
|
523
|
-
}
|
524
|
-
|
525
|
-
// @todo : check validity
|
526
|
-
if (typeof self.options.domainMargin === "number") {
|
527
|
-
self.options.domainMargin = [self.options.domainMargin, self.options.domainMargin, self.options.domainMargin, self.options.domainMargin];
|
528
|
-
}
|
529
|
-
|
530
|
-
// Return the width of the domain block, without the domain gutter
|
531
|
-
// @param int d Domain start timestamp
|
532
|
-
var w = function(d, outer) {
|
533
|
-
var width = self.options.cellSize*self._domainType[self.options.subDomain].column(d) + self.options.cellPadding*self._domainType[self.options.subDomain].column(d);
|
534
|
-
if (typeof outer !== "undefined" && outer === true) {
|
535
|
-
return width += domainHorizontalLabelWidth + self.options.domainGutter + self.options.domainMargin[1] + self.options.domainMargin[3];
|
536
|
-
}
|
537
|
-
return width;
|
538
|
-
};
|
539
|
-
|
540
|
-
// Return the height of the domain block, without the domain gutter
|
541
|
-
var h = function(d, outer) {
|
542
|
-
var height = self.options.cellSize*self._domainType[self.options.subDomain].row(d) + self.options.cellPadding*self._domainType[self.options.subDomain].row(d);
|
543
|
-
if (typeof outer !== "undefined" && outer === true) {
|
544
|
-
height += self.options.domainGutter + domainVerticalLabelHeight + self.options.domainMargin[0] + self.options.domainMargin[2];
|
545
|
-
}
|
546
|
-
return height;
|
547
|
-
};
|
548
|
-
|
549
578
|
// Painting all the domains
|
550
579
|
var domainSvg = self.root.select(".graph")
|
551
580
|
.selectAll(".graph-domain")
|
552
|
-
.data(self._domains, function(d) { return d;})
|
581
|
+
.data(self._domains.keys().map(function(d) { return parseInt(d, 10); }), function(d) { return d;})
|
553
582
|
;
|
554
583
|
|
555
584
|
var enteringDomainDim = 0;
|
@@ -560,26 +589,26 @@ var CalHeatMap = function() {
|
|
560
589
|
// PAINTING DOMAIN //
|
561
590
|
// =========================================================================//
|
562
591
|
|
563
|
-
|
592
|
+
self.svg = domainSvg
|
564
593
|
.enter()
|
565
594
|
.append("svg")
|
566
|
-
.attr("width", function(d
|
595
|
+
.attr("width", function(d) {
|
567
596
|
return w(d, true);
|
568
597
|
})
|
569
598
|
.attr("height", function(d) {
|
570
599
|
return h(d, true);
|
571
600
|
})
|
572
|
-
.attr("x", function(d
|
601
|
+
.attr("x", function(d) {
|
573
602
|
if (self.options.verticalOrientation) {
|
574
603
|
graphDim.width = w(d, true);
|
575
604
|
return 0;
|
576
605
|
} else {
|
577
|
-
return getDomainPosition(
|
606
|
+
return getDomainPosition(d, graphDim, "width", w(d, true));
|
578
607
|
}
|
579
608
|
})
|
580
|
-
.attr("y", function(d
|
609
|
+
.attr("y", function(d) {
|
581
610
|
if (self.options.verticalOrientation) {
|
582
|
-
return getDomainPosition(
|
611
|
+
return getDomainPosition(d, graphDim, "height", h(d, true));
|
583
612
|
} else {
|
584
613
|
graphDim.height = h(d, true);
|
585
614
|
return 0;
|
@@ -590,34 +619,38 @@ var CalHeatMap = function() {
|
|
590
619
|
var date = new Date(d);
|
591
620
|
switch(self.options.domain) {
|
592
621
|
case "hour" : classname += " h_" + date.getHours();
|
622
|
+
/* falls through */
|
593
623
|
case "day" : classname += " d_" + date.getDate() + " dy_" + date.getDay();
|
624
|
+
/* falls through */
|
594
625
|
case "week" : classname += " w_" + self.getWeekNumber(date);
|
626
|
+
/* falls through */
|
595
627
|
case "month" : classname += " m_" + (date.getMonth() + 1);
|
628
|
+
/* falls through */
|
596
629
|
case "year" : classname += " y_" + date.getFullYear();
|
597
630
|
}
|
598
631
|
return classname;
|
599
632
|
})
|
600
633
|
;
|
601
634
|
|
602
|
-
function getDomainPosition(
|
635
|
+
function getDomainPosition(domainIndex, graphDim, axis, domainDim) {
|
603
636
|
var tmp = 0;
|
604
637
|
switch(navigationDir) {
|
605
638
|
case false :
|
606
|
-
if (
|
639
|
+
//if (domainIndex > 0) {
|
607
640
|
tmp = graphDim[axis];
|
608
|
-
}
|
641
|
+
//}
|
609
642
|
|
610
643
|
graphDim[axis] += domainDim;
|
611
|
-
self.domainPosition.
|
644
|
+
self.domainPosition.setPosition(domainIndex, tmp);
|
612
645
|
return tmp;
|
613
646
|
|
614
647
|
case self.NAVIGATE_RIGHT :
|
615
|
-
self.domainPosition.
|
648
|
+
self.domainPosition.setPosition(domainIndex, graphDim[axis]);
|
616
649
|
|
617
650
|
enteringDomainDim = domainDim;
|
618
|
-
exitingDomainDim = self.domainPosition.
|
651
|
+
exitingDomainDim = self.domainPosition.getPositionFromIndex(1);
|
619
652
|
|
620
|
-
self.domainPosition.
|
653
|
+
self.domainPosition.shiftRightBy(exitingDomainDim);
|
621
654
|
return graphDim[axis];
|
622
655
|
|
623
656
|
case self.NAVIGATE_LEFT :
|
@@ -626,40 +659,42 @@ var CalHeatMap = function() {
|
|
626
659
|
enteringDomainDim = -tmp;
|
627
660
|
exitingDomainDim = graphDim[axis] - self.domainPosition.getLast();
|
628
661
|
|
629
|
-
self.domainPosition.
|
630
|
-
self.domainPosition.
|
662
|
+
self.domainPosition.setPosition(domainIndex, tmp);
|
663
|
+
self.domainPosition.shiftLeftBy(enteringDomainDim);
|
631
664
|
return tmp;
|
632
665
|
}
|
633
666
|
}
|
634
667
|
|
635
|
-
svg.append("rect")
|
636
|
-
.attr("width", function(d
|
637
|
-
.attr("height", function(d
|
668
|
+
self.svg.append("rect")
|
669
|
+
.attr("width", function(d) { return w(d, true) - self.options.domainGutter - self.options.cellPadding; })
|
670
|
+
.attr("height", function(d) { return h(d, true) - self.options.domainGutter - self.options.cellPadding; })
|
638
671
|
.attr("class", "domain-background")
|
639
|
-
|
672
|
+
;
|
640
673
|
|
641
674
|
// =========================================================================//
|
642
675
|
// PAINTING SUBDOMAINS //
|
643
676
|
// =========================================================================//
|
644
|
-
var subDomainSvgGroup = svg.append("svg")
|
645
|
-
.attr("x", function(
|
646
|
-
|
647
|
-
|
648
|
-
|
677
|
+
var subDomainSvgGroup = self.svg.append("svg")
|
678
|
+
.attr("x", function() {
|
679
|
+
if (self.options.label.position === "left") {
|
680
|
+
return self.domainHorizontalLabelWidth + self.options.domainMargin[3];
|
681
|
+
} else {
|
682
|
+
return self.options.domainMargin[3];
|
649
683
|
}
|
650
684
|
})
|
651
|
-
.attr("y", function(
|
652
|
-
|
653
|
-
|
654
|
-
|
685
|
+
.attr("y", function() {
|
686
|
+
if (self.options.label.position === "top") {
|
687
|
+
return self.domainVerticalLabelHeight + self.options.domainMargin[0];
|
688
|
+
} else {
|
689
|
+
return self.options.domainMargin[0];
|
655
690
|
}
|
656
691
|
})
|
657
692
|
.attr("class", "graph-subdomain-group")
|
658
693
|
;
|
659
694
|
|
660
695
|
var rect = subDomainSvgGroup
|
661
|
-
.selectAll("
|
662
|
-
.data(function(d) { return self.
|
696
|
+
.selectAll("g")
|
697
|
+
.data(function(d) { return self._domains.get(d); }, function(d) { return d.t; })
|
663
698
|
.enter()
|
664
699
|
.append("g")
|
665
700
|
;
|
@@ -667,41 +702,41 @@ var CalHeatMap = function() {
|
|
667
702
|
rect
|
668
703
|
.append("rect")
|
669
704
|
.attr("class", function(d) {
|
670
|
-
return "graph-rect" + self.getHighlightClassName(d) + (self.options.onClick !== null ? " hover_cursor" : "");
|
705
|
+
return "graph-rect" + self.getHighlightClassName(d.t) + (self.options.onClick !== null ? " hover_cursor" : "");
|
671
706
|
})
|
672
707
|
.attr("width", self.options.cellSize)
|
673
708
|
.attr("height", self.options.cellSize)
|
674
|
-
.attr("x", function(d) { return self.positionSubDomainX(d); })
|
675
|
-
.attr("y", function(d) { return self.positionSubDomainY(d); })
|
709
|
+
.attr("x", function(d) { return self.positionSubDomainX(d.t); })
|
710
|
+
.attr("y", function(d) { return self.positionSubDomainY(d.t); })
|
676
711
|
.on("click", function(d) {
|
677
712
|
if (self.options.onClick !== null) {
|
678
|
-
return self.onClick(d,
|
713
|
+
return self.onClick(new Date(d.t), d.v);
|
714
|
+
}
|
715
|
+
})
|
716
|
+
.call(function(selection) {
|
717
|
+
if (self.options.cellRadius > 0) {
|
718
|
+
selection
|
719
|
+
.attr("rx", self.options.cellRadius)
|
720
|
+
.attr("ry", self.options.cellRadius)
|
721
|
+
;
|
679
722
|
}
|
680
723
|
})
|
681
|
-
.call(radius)
|
682
724
|
;
|
683
725
|
|
684
|
-
|
685
|
-
|
686
|
-
selection
|
687
|
-
.attr("rx", self.options.cellRadius)
|
688
|
-
.attr("ry", self.options.cellRadius)
|
689
|
-
;
|
690
|
-
}
|
691
|
-
}
|
692
|
-
|
726
|
+
// Appending a title to each subdomain
|
727
|
+
rect.append("title").text(function(d){ return self.formatDate(new Date(d.t), self.options.subDomainDateFormat); });
|
693
728
|
|
694
729
|
|
695
730
|
// =========================================================================//
|
696
731
|
// PAINTING LABEL //
|
697
732
|
// =========================================================================//
|
698
|
-
svg.append("text")
|
733
|
+
self.svg.append("text")
|
699
734
|
.attr("class", "graph-label")
|
700
|
-
.attr("y", function(d
|
735
|
+
.attr("y", function(d) {
|
701
736
|
var y = self.options.domainMargin[0];
|
702
737
|
switch(self.options.label.position) {
|
703
|
-
case "top" : y += domainVerticalLabelHeight/2; break;
|
704
|
-
case "bottom" : y += h(d) + domainVerticalLabelHeight/2;
|
738
|
+
case "top" : y += self.domainVerticalLabelHeight/2; break;
|
739
|
+
case "bottom" : y += h(d) + self.domainVerticalLabelHeight/2;
|
705
740
|
}
|
706
741
|
|
707
742
|
return y + self.options.label.offset.y *
|
@@ -711,7 +746,7 @@ var CalHeatMap = function() {
|
|
711
746
|
-1 : 1
|
712
747
|
);
|
713
748
|
})
|
714
|
-
.attr("x", function(d
|
749
|
+
.attr("x", function(d){
|
715
750
|
var x = self.options.domainMargin[3];
|
716
751
|
switch(self.options.label.position) {
|
717
752
|
case "right" : x += w(d); break;
|
@@ -720,7 +755,7 @@ var CalHeatMap = function() {
|
|
720
755
|
}
|
721
756
|
|
722
757
|
if (self.options.label.align === "right") {
|
723
|
-
return x + domainHorizontalLabelWidth - self.options.label.offset.x *
|
758
|
+
return x + self.domainHorizontalLabelWidth - self.options.label.offset.x *
|
724
759
|
(self.options.label.rotate === "right" ? -1 : 1);
|
725
760
|
}
|
726
761
|
return x + self.options.label.offset.x;
|
@@ -735,8 +770,8 @@ var CalHeatMap = function() {
|
|
735
770
|
default : return "middle";
|
736
771
|
}
|
737
772
|
})
|
738
|
-
.attr("dominant-baseline", function() { return verticalDomainLabel ? "middle" : "top"; })
|
739
|
-
.text(function(d
|
773
|
+
.attr("dominant-baseline", function() { return self.verticalDomainLabel ? "middle" : "top"; })
|
774
|
+
.text(function(d) { return self.formatDate(new Date(d), self.options.domainLabelFormat); })
|
740
775
|
.call(domainRotate)
|
741
776
|
;
|
742
777
|
|
@@ -748,7 +783,7 @@ var CalHeatMap = function() {
|
|
748
783
|
var s = "rotate(90), ";
|
749
784
|
switch(self.options.label.position) {
|
750
785
|
case "right" : s += "translate(-" + w(d) + " , -" + w(d) + ")"; break;
|
751
|
-
case "left" : s += "translate(0, -" + domainHorizontalLabelWidth + ")"; break;
|
786
|
+
case "left" : s += "translate(0, -" + self.domainHorizontalLabelWidth + ")"; break;
|
752
787
|
}
|
753
788
|
|
754
789
|
return s;
|
@@ -759,8 +794,8 @@ var CalHeatMap = function() {
|
|
759
794
|
.attr("transform", function(d) {
|
760
795
|
var s = "rotate(270), ";
|
761
796
|
switch(self.options.label.position) {
|
762
|
-
case "right" : s += "translate(-" + (w(d) + domainHorizontalLabelWidth) + " , " + w(d) + ")"; break;
|
763
|
-
case "left" : s += "translate(-" + (domainHorizontalLabelWidth) + " , " + domainHorizontalLabelWidth + ")"; break;
|
797
|
+
case "right" : s += "translate(-" + (w(d) + self.domainHorizontalLabelWidth) + " , " + w(d) + ")"; break;
|
798
|
+
case "left" : s += "translate(-" + (self.domainHorizontalLabelWidth) + " , " + self.domainHorizontalLabelWidth + ")"; break;
|
764
799
|
}
|
765
800
|
|
766
801
|
return s;
|
@@ -770,8 +805,6 @@ var CalHeatMap = function() {
|
|
770
805
|
}
|
771
806
|
|
772
807
|
|
773
|
-
// Appending a title to each subdomain
|
774
|
-
rect.append("title").text(function(d){ return self.formatDate(d, self.options.subDomainDateFormat); });
|
775
808
|
|
776
809
|
|
777
810
|
// =========================================================================//
|
@@ -780,12 +813,12 @@ var CalHeatMap = function() {
|
|
780
813
|
if (self.options.subDomainTextFormat !== null) {
|
781
814
|
rect
|
782
815
|
.append("text")
|
783
|
-
.attr("class", function(d) { return "subdomain-text" + self.getHighlightClassName(d); })
|
784
|
-
.attr("x", function(d) { return self.positionSubDomainX(d) + self.options.cellSize/2; })
|
785
|
-
.attr("y", function(d) { return self.positionSubDomainY(d) + self.options.cellSize/2; })
|
816
|
+
.attr("class", function(d) { return "subdomain-text" + self.getHighlightClassName(d.t); })
|
817
|
+
.attr("x", function(d) { return self.positionSubDomainX(d.t) + self.options.cellSize/2; })
|
818
|
+
.attr("y", function(d) { return self.positionSubDomainY(d.t) + self.options.cellSize/2; })
|
786
819
|
.attr("text-anchor", "middle")
|
787
820
|
.attr("dominant-baseline", "central")
|
788
|
-
.text(function(d){ return self.formatDate(d, self.options.subDomainTextFormat); })
|
821
|
+
.text(function(d){ return self.formatDate(new Date(d.t), self.options.subDomainTextFormat); })
|
789
822
|
;
|
790
823
|
}
|
791
824
|
|
@@ -795,19 +828,11 @@ var CalHeatMap = function() {
|
|
795
828
|
|
796
829
|
if (navigationDir !== false) {
|
797
830
|
domainSvg.transition().duration(self.options.animationDuration)
|
798
|
-
.attr("x", function(d
|
799
|
-
|
800
|
-
return 0;
|
801
|
-
} else {
|
802
|
-
return self.domainPosition.getPosition(i);
|
803
|
-
}
|
831
|
+
.attr("x", function(d){
|
832
|
+
return self.options.verticalOrientation ? 0 : self.domainPosition.getPosition(d);
|
804
833
|
})
|
805
|
-
.attr("y", function(d
|
806
|
-
|
807
|
-
return self.domainPosition.getPosition(i);
|
808
|
-
} else {
|
809
|
-
return 0;
|
810
|
-
}
|
834
|
+
.attr("y", function(d){
|
835
|
+
return self.options.verticalOrientation? self.domainPosition.getPosition(d) : 0;
|
811
836
|
})
|
812
837
|
;
|
813
838
|
}
|
@@ -823,7 +848,7 @@ var CalHeatMap = function() {
|
|
823
848
|
|
824
849
|
// At the time of exit, domainsWidth and domainsHeight already automatically shifted
|
825
850
|
domainSvg.exit().transition().duration(self.options.animationDuration)
|
826
|
-
.attr("x", function(d
|
851
|
+
.attr("x", function(d){
|
827
852
|
if (self.options.verticalOrientation) {
|
828
853
|
return 0;
|
829
854
|
} else {
|
@@ -851,13 +876,56 @@ var CalHeatMap = function() {
|
|
851
876
|
.attr("width", function() { return graphDim.width - self.options.domainGutter - self.options.cellPadding; })
|
852
877
|
.attr("height", function() { return graphDim.height - self.options.domainGutter - self.options.cellPadding; })
|
853
878
|
;
|
879
|
+
};
|
854
880
|
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
881
|
+
this.fill = function() {
|
882
|
+
var rect = self.svg
|
883
|
+
.selectAll("svg").selectAll("g")
|
884
|
+
.data(function(d) { return self._domains.get(d); }, function(d) { return d.t; })
|
885
|
+
;
|
886
|
+
|
887
|
+
rect.transition().select("rect")
|
888
|
+
.attr("class", function(d) {
|
889
|
+
|
890
|
+
var htmlClass = "graph-rect" + self.getHighlightClassName(d.t);
|
891
|
+
|
892
|
+
if (d.v !== null) {
|
893
|
+
htmlClass += " " + self.legend(d.v);
|
894
|
+
} else if (self.options.considerMissingDataAsZero) {
|
895
|
+
htmlClass += " " + self.legend(0);
|
896
|
+
}
|
897
|
+
|
898
|
+
if (self.options.onClick !== null) {
|
899
|
+
htmlClass += " hover_cursor";
|
900
|
+
}
|
901
|
+
|
902
|
+
return htmlClass;
|
903
|
+
})
|
904
|
+
;
|
905
|
+
|
906
|
+
|
907
|
+
rect.transition().select("title")
|
908
|
+
.text(function(d) {
|
909
|
+
|
910
|
+
if (d.v === null && !self.options.considerMissingDataAsZero) {
|
911
|
+
return (self.options.subDomainTitleFormat.empty).format({
|
912
|
+
date: self.formatDate(new Date(d.t), self.options.subDomainDateFormat)
|
913
|
+
});
|
914
|
+
} else {
|
915
|
+
var value = d.v;
|
916
|
+
// Consider null as 0
|
917
|
+
if (value === null && self.options.considerMissingDataAsZero) {
|
918
|
+
value = 0;
|
919
|
+
}
|
920
|
+
|
921
|
+
return (self.options.subDomainTitleFormat.filled).format({
|
922
|
+
count: self.formatNumber(value),
|
923
|
+
name: self.options.itemName[(value !== 1 ? 1 : 0)],
|
924
|
+
connector: self._domainType[self.options.subDomain].format.connector,
|
925
|
+
date: self.formatDate(new Date(d.t), self.options.subDomainDateFormat)
|
926
|
+
});
|
927
|
+
}
|
928
|
+
});
|
861
929
|
};
|
862
930
|
|
863
931
|
|
@@ -1014,104 +1082,113 @@ var CalHeatMap = function() {
|
|
1014
1082
|
|
1015
1083
|
CalHeatMap.prototype = {
|
1016
1084
|
|
1017
|
-
|
1018
1085
|
// =========================================================================//
|
1019
|
-
// CALLBACK
|
1086
|
+
// EVENTS CALLBACK //
|
1020
1087
|
// =========================================================================//
|
1021
1088
|
|
1022
1089
|
/**
|
1023
|
-
*
|
1024
|
-
*
|
1025
|
-
* @param
|
1090
|
+
* Helper method for triggering event callback
|
1091
|
+
*
|
1092
|
+
* @param string eventName Name of the event to trigger
|
1093
|
+
* @param array successArgs List of argument to pass to the callback
|
1094
|
+
* @param boolean skip Whether to skip the event triggering
|
1095
|
+
* @return mixed True when the triggering was skipped, false on error, else the callback function
|
1026
1096
|
*/
|
1027
|
-
|
1028
|
-
if (
|
1029
|
-
return
|
1097
|
+
triggerEvent: function(eventName, successArgs, skip) {
|
1098
|
+
if ((arguments.length === 3 && skip) || this.options[eventName] === null) {
|
1099
|
+
return true;
|
1100
|
+
}
|
1101
|
+
|
1102
|
+
if (typeof this.options[eventName] === "function") {
|
1103
|
+
if (typeof successArgs === "function") {
|
1104
|
+
successArgs = successArgs();
|
1105
|
+
}
|
1106
|
+
return this.options[eventName].apply(this, successArgs);
|
1030
1107
|
} else {
|
1031
|
-
console.log("Provided callback for
|
1108
|
+
console.log("Provided callback for " + eventName + " is not a function.");
|
1032
1109
|
return false;
|
1033
1110
|
}
|
1034
1111
|
},
|
1035
1112
|
|
1036
1113
|
/**
|
1037
|
-
*
|
1114
|
+
* Event triggered on a mouse click on a subDomain cell
|
1115
|
+
*
|
1116
|
+
* @param Date d Date of the subdomain block
|
1117
|
+
* @param int itemNb Number of items in that date
|
1118
|
+
*/
|
1119
|
+
onClick : function(d, itemNb) {
|
1120
|
+
return this.triggerEvent("onClick", [d, itemNb]);
|
1121
|
+
},
|
1122
|
+
|
1123
|
+
/**
|
1124
|
+
* Event triggered after drawing the calendar, byt before filling it with data
|
1038
1125
|
*/
|
1039
1126
|
afterLoad : function() {
|
1040
|
-
|
1041
|
-
return this.options.afterLoad();
|
1042
|
-
} else {
|
1043
|
-
console.log("Provided callback for afterLoad is not a function.");
|
1044
|
-
return false;
|
1045
|
-
}
|
1127
|
+
return this.triggerEvent("afterLoad");
|
1046
1128
|
},
|
1047
1129
|
|
1048
1130
|
/**
|
1049
|
-
*
|
1131
|
+
* Event triggered after completing drawing and filling the calendar
|
1050
1132
|
*/
|
1051
1133
|
onComplete : function() {
|
1052
|
-
|
1053
|
-
return true;
|
1054
|
-
}
|
1055
|
-
|
1134
|
+
var response = this.triggerEvent("onComplete", [], this._completed);
|
1056
1135
|
this._completed = true;
|
1057
|
-
|
1058
|
-
return this.options.onComplete();
|
1059
|
-
} else {
|
1060
|
-
console.log("Provided callback for onComplete is not a function.");
|
1061
|
-
return false;
|
1062
|
-
}
|
1136
|
+
return response;
|
1063
1137
|
},
|
1064
1138
|
|
1065
1139
|
/**
|
1066
|
-
*
|
1140
|
+
* Event triggered after shifting the calendar one domain back
|
1141
|
+
*
|
1067
1142
|
* @param Date start Domain start date
|
1068
1143
|
* @param Date end Domain end date
|
1069
1144
|
*/
|
1070
1145
|
afterLoadPreviousDomain: function(start) {
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
return false;
|
1077
|
-
}
|
1146
|
+
var parent = this;
|
1147
|
+
return this.triggerEvent("afterLoadPreviousDomain", function() {
|
1148
|
+
var subDomain = parent.getSubDomain(start);
|
1149
|
+
return [subDomain.shift(), subDomain.pop()];
|
1150
|
+
});
|
1078
1151
|
},
|
1079
1152
|
|
1080
1153
|
/**
|
1081
|
-
*
|
1154
|
+
* Event triggered after shifting the calendar one domain above
|
1155
|
+
*
|
1082
1156
|
* @param Date start Domain start date
|
1083
1157
|
* @param Date end Domain end date
|
1084
1158
|
*/
|
1085
1159
|
afterLoadNextDomain: function(start) {
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
return false;
|
1092
|
-
}
|
1160
|
+
var parent = this;
|
1161
|
+
return this.triggerEvent("afterLoadNextDomain", function() {
|
1162
|
+
var subDomain = parent.getSubDomain(start);
|
1163
|
+
return [subDomain.shift(), subDomain.pop()];
|
1164
|
+
});
|
1093
1165
|
},
|
1094
1166
|
|
1167
|
+
/**
|
1168
|
+
* Event triggered after loading the leftmost domain allowed by minDate
|
1169
|
+
*
|
1170
|
+
* @param boolean reached True if the leftmost domain was reached
|
1171
|
+
*/
|
1095
1172
|
onMinDomainReached: function(reached) {
|
1096
1173
|
this._minDomainReached = reached;
|
1097
|
-
|
1098
|
-
return this.options.onMinDomainReached(reached);
|
1099
|
-
} else {
|
1100
|
-
console.log("Provided callback for onMinDomainReached is not a function.");
|
1101
|
-
return false;
|
1102
|
-
}
|
1174
|
+
return this.triggerEvent("onMinDomainReached", [reached]);
|
1103
1175
|
},
|
1104
1176
|
|
1177
|
+
/**
|
1178
|
+
* Event triggered after loading the rightmost domain allowed by maxDate
|
1179
|
+
*
|
1180
|
+
* @param boolean reached True if the rightmost domain was reached
|
1181
|
+
*/
|
1105
1182
|
onMaxDomainReached: function(reached) {
|
1106
1183
|
this._maxDomainReached = reached;
|
1107
|
-
|
1108
|
-
return this.options.onMaxDomainReached(reached);
|
1109
|
-
} else {
|
1110
|
-
console.log("Provided callback for onMaxDomainReached is not a function.");
|
1111
|
-
return false;
|
1112
|
-
}
|
1184
|
+
return this.triggerEvent("onMaxDomainReached", [reached]);
|
1113
1185
|
},
|
1114
1186
|
|
1187
|
+
|
1188
|
+
// =========================================================================//
|
1189
|
+
// FORMATTER //
|
1190
|
+
// =========================================================================//
|
1191
|
+
|
1115
1192
|
formatNumber: d3.format(",g"),
|
1116
1193
|
|
1117
1194
|
formatDate: function(d, format) {
|
@@ -1127,6 +1204,7 @@ CalHeatMap.prototype = {
|
|
1127
1204
|
}
|
1128
1205
|
},
|
1129
1206
|
|
1207
|
+
|
1130
1208
|
// =========================================================================//
|
1131
1209
|
// DOMAIN NAVIGATION //
|
1132
1210
|
// =========================================================================//
|
@@ -1147,28 +1225,35 @@ CalHeatMap.prototype = {
|
|
1147
1225
|
}
|
1148
1226
|
|
1149
1227
|
var parent = this;
|
1150
|
-
this._domains.
|
1151
|
-
|
1228
|
+
this._domains.set(
|
1229
|
+
nextDomainStartTimestamp,
|
1230
|
+
this.getSubDomain(nextDomainStartTimestamp).map(function(d) {
|
1231
|
+
return {t: parent._domainType[parent.options.subDomain].extractUnit(d), v: null};
|
1232
|
+
})
|
1233
|
+
);
|
1234
|
+
this._domains.remove(this._domains.keys().sort().shift());
|
1152
1235
|
|
1153
1236
|
this.paint(this.NAVIGATE_RIGHT);
|
1154
1237
|
|
1238
|
+
var domains = this._domains.keys().sort();
|
1239
|
+
|
1155
1240
|
this.getDatas(
|
1156
1241
|
this.options.data,
|
1157
|
-
new Date(
|
1158
|
-
this.getSubDomain(
|
1159
|
-
function(
|
1160
|
-
parent.fill(
|
1242
|
+
new Date(parseInt(domains[domains.length-1], 10)),
|
1243
|
+
this.getSubDomain(parseInt(domains[domains.length-1], 10)).pop(),
|
1244
|
+
function() {
|
1245
|
+
parent.fill();
|
1161
1246
|
}
|
1162
1247
|
);
|
1163
1248
|
|
1164
|
-
this.afterLoadNextDomain(new Date(
|
1249
|
+
this.afterLoadNextDomain(new Date(parseInt(domains[domains.length-1], 10)));
|
1165
1250
|
|
1166
1251
|
if (this.maxDomainIsReached(this.getNextDomain().getTime())) {
|
1167
1252
|
this.onMaxDomainReached(true);
|
1168
1253
|
}
|
1169
1254
|
|
1170
1255
|
// Try to "disengage" the min domain reached setting
|
1171
|
-
if (this._minDomainReached && !this.minDomainIsReached(
|
1256
|
+
if (this._minDomainReached && !this.minDomainIsReached(domains[0])) {
|
1172
1257
|
this.onMinDomainReached(false);
|
1173
1258
|
}
|
1174
1259
|
|
@@ -1190,28 +1275,35 @@ CalHeatMap.prototype = {
|
|
1190
1275
|
var previousDomainStartTimestamp = this.getPreviousDomain().getTime();
|
1191
1276
|
|
1192
1277
|
var parent = this;
|
1193
|
-
this._domains.
|
1194
|
-
|
1278
|
+
this._domains.set(
|
1279
|
+
previousDomainStartTimestamp,
|
1280
|
+
this.getSubDomain(previousDomainStartTimestamp).map(function(d) {
|
1281
|
+
return {t: parent._domainType[parent.options.subDomain].extractUnit(d), v: null};
|
1282
|
+
})
|
1283
|
+
);
|
1284
|
+
this._domains.remove(this._domains.keys().sort().pop());
|
1195
1285
|
|
1196
1286
|
this.paint(this.NAVIGATE_LEFT);
|
1197
1287
|
|
1288
|
+
var domains = this._domains.keys().sort();
|
1289
|
+
|
1198
1290
|
this.getDatas(
|
1199
1291
|
this.options.data,
|
1200
|
-
new Date(
|
1201
|
-
this.getSubDomain(
|
1202
|
-
function(
|
1203
|
-
parent.fill(
|
1292
|
+
new Date(parseInt(domains[0], 10)),
|
1293
|
+
this.getSubDomain(parseInt(domains[0], 10)).pop(),
|
1294
|
+
function() {
|
1295
|
+
parent.fill();
|
1204
1296
|
}
|
1205
1297
|
);
|
1206
1298
|
|
1207
|
-
this.afterLoadPreviousDomain(new Date(
|
1299
|
+
this.afterLoadPreviousDomain(new Date(parseInt(domains[0], 10)));
|
1208
1300
|
|
1209
1301
|
if (this.minDomainIsReached(previousDomainStartTimestamp)) {
|
1210
1302
|
this.onMinDomainReached(true);
|
1211
1303
|
}
|
1212
1304
|
|
1213
1305
|
// Try to "disengage" the max domain reached setting
|
1214
|
-
if (this._maxDomainReached && !this.maxDomainIsReached(
|
1306
|
+
if (this._maxDomainReached && !this.maxDomainIsReached(domains[domains.length-1])) {
|
1215
1307
|
this.onMaxDomainReached(false);
|
1216
1308
|
}
|
1217
1309
|
|
@@ -1236,6 +1328,7 @@ CalHeatMap.prototype = {
|
|
1236
1328
|
return (this.options.minDate !== null && (this.options.minDate.getTime() >= datetimestamp));
|
1237
1329
|
},
|
1238
1330
|
|
1331
|
+
|
1239
1332
|
// =========================================================================//
|
1240
1333
|
// PAINTING : LEGEND //
|
1241
1334
|
// =========================================================================//
|
@@ -1245,9 +1338,10 @@ CalHeatMap.prototype = {
|
|
1245
1338
|
var parent = this;
|
1246
1339
|
var legend = this.root;
|
1247
1340
|
|
1248
|
-
|
1249
|
-
|
1250
|
-
|
1341
|
+
if (this.options.legendVerticalPosition === "top") {
|
1342
|
+
legend = legend.insert("svg", ".graph");
|
1343
|
+
} else {
|
1344
|
+
legend = legend.append("svg");
|
1251
1345
|
}
|
1252
1346
|
|
1253
1347
|
var legendWidth =
|
@@ -1260,7 +1354,7 @@ CalHeatMap.prototype = {
|
|
1260
1354
|
.attr("height", this.options.legendCellSize + this.options.legendMargin[0] + this.options.legendMargin[2])
|
1261
1355
|
.attr("width", width)
|
1262
1356
|
.append("g")
|
1263
|
-
.attr("transform", function(
|
1357
|
+
.attr("transform", function() {
|
1264
1358
|
switch(parent.options.legendHorizontalPosition) {
|
1265
1359
|
case "right" : return "translate(" + (width - legendWidth) + ")";
|
1266
1360
|
case "middle" :
|
@@ -1289,7 +1383,6 @@ CalHeatMap.prototype = {
|
|
1289
1383
|
legendItem
|
1290
1384
|
.append("title")
|
1291
1385
|
.text(function(d) {
|
1292
|
-
var nextThreshold = parent.options.legend[d+1];
|
1293
1386
|
if (d === 0) {
|
1294
1387
|
return (parent.options.legendTitleFormat.lower).format({
|
1295
1388
|
min: parent.options.legend[d],
|
@@ -1309,105 +1402,30 @@ CalHeatMap.prototype = {
|
|
1309
1402
|
|
1310
1403
|
},
|
1311
1404
|
|
1312
|
-
// =========================================================================//
|
1313
|
-
// PAINTING : SUBDOMAIN FILLING //
|
1314
|
-
// =========================================================================//
|
1315
|
-
|
1316
|
-
/**
|
1317
|
-
* Colorize all rectangles according to their items count
|
1318
|
-
*
|
1319
|
-
* @param {[type]} data [description]
|
1320
|
-
*/
|
1321
|
-
display: function(data, domain) {
|
1322
|
-
var parent = this;
|
1323
|
-
|
1324
|
-
domain.each(function(domainUnit) {
|
1325
|
-
|
1326
|
-
if (data.hasOwnProperty(domainUnit) || parent.options.considerMissingDataAsZero) {
|
1327
|
-
d3.select(this).selectAll(".graph-subdomain-group rect")
|
1328
|
-
.attr("class", function(d) {
|
1329
|
-
var subDomainUnit = parent._domainType[parent.options.subDomain].extractUnit(d);
|
1330
|
-
|
1331
|
-
var htmlClass = "graph-rect" + parent.getHighlightClassName(d);
|
1332
|
-
|
1333
|
-
var value;
|
1334
|
-
|
1335
|
-
if (data.hasOwnProperty(domainUnit) && data[domainUnit].hasOwnProperty(subDomainUnit)) {
|
1336
|
-
htmlClass += " " + parent.legend(data[domainUnit][subDomainUnit]);
|
1337
|
-
} else if (parent.options.considerMissingDataAsZero) {
|
1338
|
-
htmlClass += " " + parent.legend(0);
|
1339
|
-
}
|
1340
|
-
|
1341
|
-
if (parent.options.onClick !== null) {
|
1342
|
-
htmlClass += " hover_cursor";
|
1343
|
-
}
|
1344
|
-
|
1345
|
-
return htmlClass;
|
1346
|
-
})
|
1347
|
-
.on("click", function(d) {
|
1348
|
-
if (parent.options.onClick !== null) {
|
1349
|
-
var subDomainUnit = parent._domainType[parent.options.subDomain].extractUnit(d);
|
1350
|
-
return parent.onClick(
|
1351
|
-
d,
|
1352
|
-
(data[domainUnit].hasOwnProperty(subDomainUnit) || parent.options.considerMissingDataAsZero ? data[domainUnit][subDomainUnit] : null)
|
1353
|
-
);
|
1354
|
-
}
|
1355
|
-
});
|
1356
|
-
|
1357
|
-
d3.select(this).selectAll(".graph-subdomain-group title")
|
1358
|
-
.text(function(d) {
|
1359
|
-
var subDomainUnit = parent._domainType[parent.options.subDomain].extractUnit(d);
|
1360
|
-
|
1361
|
-
if ((data.hasOwnProperty(domainUnit) && data[domainUnit].hasOwnProperty(subDomainUnit) && data[domainUnit][subDomainUnit] !== null) || parent.options.considerMissingDataAsZero){
|
1362
|
-
|
1363
|
-
if (data.hasOwnProperty(domainUnit) && data[domainUnit].hasOwnProperty(subDomainUnit)) {
|
1364
|
-
value = data[domainUnit][subDomainUnit];
|
1365
|
-
} else if (parent.options.considerMissingDataAsZero) {
|
1366
|
-
value = 0;
|
1367
|
-
}
|
1368
|
-
|
1369
|
-
return (parent.options.subDomainTitleFormat.filled).format({
|
1370
|
-
count: parent.formatNumber(value),
|
1371
|
-
name: parent.options.itemName[(value !== 1 ? 1 : 0)],
|
1372
|
-
connector: parent._domainType[parent.options.subDomain].format.connector,
|
1373
|
-
date: parent.formatDate(d, parent.options.subDomainDateFormat)
|
1374
|
-
});
|
1375
|
-
} else {
|
1376
|
-
return (parent.options.subDomainTitleFormat.empty).format({
|
1377
|
-
date: parent.formatDate(d, parent.options.subDomainDateFormat)
|
1378
|
-
});
|
1379
|
-
};
|
1380
|
-
});
|
1381
|
-
|
1382
|
-
|
1383
|
-
}
|
1384
|
-
}
|
1385
|
-
);
|
1386
|
-
return true;
|
1387
|
-
},
|
1388
|
-
|
1389
1405
|
// =========================================================================//
|
1390
1406
|
// POSITIONNING //
|
1391
1407
|
// =========================================================================//
|
1392
1408
|
|
1393
1409
|
positionSubDomainX: function(d) {
|
1394
|
-
var index = this._domainType[this.options.subDomain].position.x(d);
|
1410
|
+
var index = this._domainType[this.options.subDomain].position.x(new Date(d));
|
1395
1411
|
return index * this.options.cellSize + index * this.options.cellPadding;
|
1396
1412
|
},
|
1397
1413
|
|
1398
1414
|
positionSubDomainY: function(d) {
|
1399
|
-
var index = this._domainType[this.options.subDomain].position.y(d);
|
1415
|
+
var index = this._domainType[this.options.subDomain].position.y(new Date(d));
|
1400
1416
|
return index * this.options.cellSize + index * this.options.cellPadding;
|
1401
1417
|
},
|
1402
1418
|
|
1403
1419
|
/**
|
1404
1420
|
* Return a classname if the specified date should be highlighted
|
1405
1421
|
*
|
1406
|
-
* @param Date
|
1422
|
+
* @param timestamp date Date of the current subDomain
|
1407
1423
|
* @return String the highlight class
|
1408
1424
|
*/
|
1409
1425
|
getHighlightClassName: function(d)
|
1410
1426
|
{
|
1427
|
+
d = new Date(d);
|
1428
|
+
|
1411
1429
|
if (this.options.highlight.length > 0) {
|
1412
1430
|
for (var i in this.options.highlight) {
|
1413
1431
|
if (this.options.highlight[i] instanceof Date && this.dateIsEqual(this.options.highlight[i], d)) {
|
@@ -1468,6 +1486,7 @@ CalHeatMap.prototype = {
|
|
1468
1486
|
}
|
1469
1487
|
},
|
1470
1488
|
|
1489
|
+
|
1471
1490
|
// =========================================================================//
|
1472
1491
|
// DOMAIN COMPUTATION //
|
1473
1492
|
// =========================================================================//
|
@@ -1683,6 +1702,7 @@ CalHeatMap.prototype = {
|
|
1683
1702
|
case "hour" : return this.getHourDomain(date, computeHourSubDomainSize(date, this.options.domain));
|
1684
1703
|
case "x_day" :
|
1685
1704
|
case "day" : return this.getDayDomain(date, computeDaySubDomainSize(date, this.options.domain));
|
1705
|
+
case "x_week":
|
1686
1706
|
case "week" : return this.getWeekDomain(date, computeWeekSubDomainSize(date, this.options.domain));
|
1687
1707
|
case "x_month":
|
1688
1708
|
case "month" : return this.getMonthDomain(date, 12);
|
@@ -1690,11 +1710,11 @@ CalHeatMap.prototype = {
|
|
1690
1710
|
},
|
1691
1711
|
|
1692
1712
|
getNextDomain: function() {
|
1693
|
-
return this.getDomain(this._domains
|
1713
|
+
return this.getDomain(parseInt(this._domains.keys().sort().pop(), 10), 2).pop();
|
1694
1714
|
},
|
1695
1715
|
|
1696
1716
|
getPreviousDomain: function() {
|
1697
|
-
return this.getDomain(this._domains
|
1717
|
+
return this.getDomain(parseInt(this._domains.keys().sort().shift(), 10), -1)[0];
|
1698
1718
|
},
|
1699
1719
|
|
1700
1720
|
/**
|
@@ -1731,46 +1751,60 @@ CalHeatMap.prototype = {
|
|
1731
1751
|
// =========================================================================//
|
1732
1752
|
|
1733
1753
|
/**
|
1734
|
-
*
|
1754
|
+
* Fetch and interpret data from the datasource
|
1735
1755
|
*
|
1736
|
-
* @
|
1737
|
-
|
1738
|
-
|
1739
|
-
|
1740
|
-
|
1741
|
-
|
1742
|
-
},
|
1743
|
-
|
1744
|
-
/**
|
1745
|
-
* Interpret the data property
|
1756
|
+
* @param string|object source
|
1757
|
+
* @param Date startDate
|
1758
|
+
* @param Date endDate
|
1759
|
+
* @param function callback
|
1760
|
+
* @param function|boolean afterLoad function used to convert the data into a json object. Use true to use the afterLoad callback
|
1761
|
+
* @param updateMode
|
1746
1762
|
*
|
1747
1763
|
* @return mixed
|
1748
|
-
* - True if no data to load
|
1749
|
-
* - False if data
|
1750
|
-
* - json object
|
1764
|
+
* - True if there are no data to load
|
1765
|
+
* - False if data are loaded asynchronously
|
1751
1766
|
*/
|
1752
|
-
getDatas: function(source, startDate, endDate, callback) {
|
1753
|
-
var
|
1767
|
+
getDatas: function(source, startDate, endDate, callback, afterLoad, updateMode) {
|
1768
|
+
var self = this;
|
1769
|
+
if (arguments.length < 5) {
|
1770
|
+
afterLoad = true;
|
1771
|
+
}
|
1772
|
+
if (arguments.length < 6) {
|
1773
|
+
updateMode = this.APPEND_ON_UPDATE;
|
1774
|
+
}
|
1775
|
+
var _callback = function(data) {
|
1776
|
+
if (afterLoad !== false) {
|
1777
|
+
if (typeof afterLoad === "function") {
|
1778
|
+
data = afterLoad(data);
|
1779
|
+
} else if (typeof (self.options.afterLoadData) === "function") {
|
1780
|
+
data = self.options.afterLoadData(data);
|
1781
|
+
} else {
|
1782
|
+
console.log("Provided callback for afterLoadData is not a function.");
|
1783
|
+
return {};
|
1784
|
+
}
|
1785
|
+
}
|
1786
|
+
self.parseDatas(data, updateMode);
|
1787
|
+
callback();
|
1788
|
+
};
|
1754
1789
|
|
1755
1790
|
switch(typeof source) {
|
1756
1791
|
case "string" :
|
1757
1792
|
if (source === "") {
|
1758
|
-
|
1793
|
+
_callback({});
|
1759
1794
|
return true;
|
1760
1795
|
} else {
|
1761
|
-
|
1762
1796
|
switch(this.options.dataType) {
|
1763
1797
|
case "json" :
|
1764
|
-
d3.json(this.parseURI(source, startDate, endDate),
|
1798
|
+
d3.json(this.parseURI(source, startDate, endDate), _callback);
|
1765
1799
|
break;
|
1766
1800
|
case "csv" :
|
1767
|
-
d3.csv(this.parseURI(source, startDate, endDate),
|
1801
|
+
d3.csv(this.parseURI(source, startDate, endDate), _callback);
|
1768
1802
|
break;
|
1769
1803
|
case "tsv" :
|
1770
|
-
d3.tsv(this.parseURI(source, startDate, endDate),
|
1804
|
+
d3.tsv(this.parseURI(source, startDate, endDate), _callback);
|
1771
1805
|
break;
|
1772
1806
|
case "text" :
|
1773
|
-
d3.text(this.parseURI(source, startDate, endDate), "text/plain",
|
1807
|
+
d3.text(this.parseURI(source, startDate, endDate), "text/plain", _callback);
|
1774
1808
|
break;
|
1775
1809
|
}
|
1776
1810
|
|
@@ -1779,50 +1813,54 @@ CalHeatMap.prototype = {
|
|
1779
1813
|
break;
|
1780
1814
|
case "object" :
|
1781
1815
|
// @todo Check that it's a valid JSON object
|
1782
|
-
|
1816
|
+
_callback(source);
|
1783
1817
|
}
|
1784
1818
|
|
1785
1819
|
return true;
|
1786
1820
|
},
|
1787
1821
|
|
1788
1822
|
/**
|
1789
|
-
*
|
1823
|
+
* Populate the calendar internal data
|
1824
|
+
*
|
1825
|
+
* @param object data
|
1826
|
+
* @param constant updateMode
|
1790
1827
|
*
|
1791
|
-
* @
|
1792
|
-
* @return {[type]} [description]
|
1828
|
+
* @return void
|
1793
1829
|
*/
|
1794
|
-
parseDatas: function(data) {
|
1795
|
-
var stats = {};
|
1830
|
+
parseDatas: function(data, updateMode) {
|
1796
1831
|
|
1797
|
-
if (
|
1798
|
-
|
1799
|
-
|
1800
|
-
|
1801
|
-
|
1832
|
+
if (updateMode === this.RESET_ALL_ON_UPDATE) {
|
1833
|
+
this._domains.forEach(function(key, value) {
|
1834
|
+
value.forEach(function(element, index, array) {
|
1835
|
+
array[index].v = null;
|
1836
|
+
});
|
1837
|
+
});
|
1802
1838
|
}
|
1803
1839
|
|
1840
|
+
var domainKeys = this._domains.keys();
|
1841
|
+
var subDomainStep = this._domains.get(domainKeys[0])[1].t - this._domains.get(domainKeys[0])[0].t;
|
1842
|
+
|
1804
1843
|
for (var d in data) {
|
1805
1844
|
var date = new Date(d*1000);
|
1806
1845
|
var domainUnit = this.getDomain(date)[0].getTime();
|
1807
1846
|
|
1808
|
-
//
|
1809
|
-
if (this._domains.
|
1810
|
-
|
1811
|
-
|
1847
|
+
// Record only datas relevant to the current domain
|
1848
|
+
if (this._domains.has(domainUnit)) {
|
1849
|
+
var subDomainUnit = this._domainType[this.options.subDomain].extractUnit(date);
|
1850
|
+
var subDomainsData = this._domains.get(domainUnit);
|
1851
|
+
var index = Math.floor((subDomainUnit - domainUnit) / subDomainStep);
|
1812
1852
|
|
1813
|
-
|
1814
|
-
|
1815
|
-
|
1816
|
-
|
1817
|
-
|
1818
|
-
|
1819
|
-
|
1820
|
-
|
1821
|
-
|
1853
|
+
if (updateMode === this.RESET_SINGLE_ON_UPDATE) {
|
1854
|
+
subDomainsData[index].v = data[d];
|
1855
|
+
} else {
|
1856
|
+
if (!isNaN(subDomainsData[index].v)) {
|
1857
|
+
subDomainsData[index].v += data[d];
|
1858
|
+
} else {
|
1859
|
+
subDomainsData[index].v = data[d];
|
1860
|
+
}
|
1861
|
+
}
|
1822
1862
|
}
|
1823
1863
|
}
|
1824
|
-
|
1825
|
-
return stats;
|
1826
1864
|
},
|
1827
1865
|
|
1828
1866
|
parseURI: function(str, startDate, endDate) {
|
@@ -1849,6 +1887,35 @@ CalHeatMap.prototype = {
|
|
1849
1887
|
return this.loadPreviousDomain();
|
1850
1888
|
},
|
1851
1889
|
|
1890
|
+
/**
|
1891
|
+
* Update the calendar with new data
|
1892
|
+
*
|
1893
|
+
* @param object|string dataSource The calendar's datasource, same type as this.options.data
|
1894
|
+
* @param boolean|function afterLoad Whether to execute afterLoad() on the data. Pass directly a function
|
1895
|
+
* if you don't want to use the afterLoad() callback
|
1896
|
+
*/
|
1897
|
+
update: function(dataSource, afterLoad, updateMode) {
|
1898
|
+
if (arguments.length < 2) {
|
1899
|
+
afterLoad = true;
|
1900
|
+
}
|
1901
|
+
if (arguments.length < 3) {
|
1902
|
+
updateMode = this.RESET_ALL_ON_UPDATE;
|
1903
|
+
}
|
1904
|
+
|
1905
|
+
var domains = this._domains.keys().sort();
|
1906
|
+
var self = this;
|
1907
|
+
this.getDatas(
|
1908
|
+
dataSource,
|
1909
|
+
new Date(parseInt(domains[0], 10)),
|
1910
|
+
this.getSubDomain(parseInt(domains[domains.length-1], 10)).pop(),
|
1911
|
+
function() {
|
1912
|
+
self.fill();
|
1913
|
+
},
|
1914
|
+
afterLoad,
|
1915
|
+
updateMode
|
1916
|
+
);
|
1917
|
+
},
|
1918
|
+
|
1852
1919
|
getSVG: function() {
|
1853
1920
|
var styles = {
|
1854
1921
|
".graph": {},
|
@@ -1949,37 +2016,43 @@ CalHeatMap.prototype = {
|
|
1949
2016
|
};
|
1950
2017
|
|
1951
2018
|
var DomainPosition = function() {
|
1952
|
-
this.positions =
|
2019
|
+
this.positions = d3.map();
|
1953
2020
|
};
|
1954
2021
|
|
1955
|
-
DomainPosition.prototype.getPosition = function(
|
1956
|
-
return this.positions
|
2022
|
+
DomainPosition.prototype.getPosition = function(d) {
|
2023
|
+
return this.positions.get(d);
|
1957
2024
|
};
|
1958
2025
|
|
1959
|
-
DomainPosition.prototype.
|
1960
|
-
|
2026
|
+
DomainPosition.prototype.getPositionFromIndex = function(i) {
|
2027
|
+
var domains = this.positions.keys().sort();
|
2028
|
+
return this.positions.get(domains[i]);
|
1961
2029
|
};
|
1962
2030
|
|
1963
|
-
DomainPosition.prototype.
|
1964
|
-
this.positions.
|
2031
|
+
DomainPosition.prototype.getLast = function() {
|
2032
|
+
var domains = this.positions.keys().sort();
|
2033
|
+
return this.positions.get(domains[domains.length-1]);
|
1965
2034
|
};
|
1966
2035
|
|
1967
|
-
DomainPosition.prototype.
|
1968
|
-
this.positions.
|
2036
|
+
DomainPosition.prototype.setPosition = function(d, dim) {
|
2037
|
+
this.positions.set(d, dim);
|
1969
2038
|
};
|
1970
2039
|
|
1971
|
-
DomainPosition.prototype.
|
1972
|
-
|
1973
|
-
this.
|
1974
|
-
}
|
1975
|
-
|
2040
|
+
DomainPosition.prototype.shiftRightBy = function(exitingDomainDim) {
|
2041
|
+
this.positions.forEach(function(key, value) {
|
2042
|
+
this.set(key, value - exitingDomainDim);
|
2043
|
+
});
|
2044
|
+
|
2045
|
+
var domains = this.positions.keys().sort();
|
2046
|
+
this.positions.remove(domains[0]);
|
1976
2047
|
};
|
1977
2048
|
|
1978
|
-
DomainPosition.prototype.
|
1979
|
-
|
1980
|
-
this.
|
1981
|
-
}
|
1982
|
-
|
2049
|
+
DomainPosition.prototype.shiftLeftBy = function(enteringDomainDim) {
|
2050
|
+
this.positions.forEach(function(key, value) {
|
2051
|
+
this.set(key, value + enteringDomainDim);
|
2052
|
+
});
|
2053
|
+
|
2054
|
+
var domains = this.positions.keys().sort();
|
2055
|
+
this.positions.remove(domains[domains.length-1]);
|
1983
2056
|
};
|
1984
2057
|
|
1985
2058
|
|
@@ -2023,7 +2096,7 @@ function mergeRecursive(obj1, obj2) {
|
|
2023
2096
|
* AMD Loader
|
2024
2097
|
*/
|
2025
2098
|
if (typeof define === "function" && define.amd) {
|
2026
|
-
define(["d3"], function(
|
2099
|
+
define(["d3"], function() {
|
2027
2100
|
return CalHeatMap;
|
2028
2101
|
});
|
2029
2102
|
}
|