bezel-charts 1.6.0 → 1.8.0

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.
@@ -1,4 +1,4 @@
1
- /*! Bezel Charts v1.6.0 | MIT | github.com/ShazMoghaddam/bezel-charts */
1
+ /*! Bezel Charts v1.8.0 | MIT | github.com/ShazMoghaddam/bezel-charts */
2
2
 
3
3
  // src/core/util.js
4
4
  var SANS = "ui-sans-serif,system-ui,-apple-system,'Segoe UI',Roboto,Helvetica,Arial,sans-serif";
@@ -198,9 +198,9 @@ function spark(o, th) {
198
198
  mn = Math.min(mn, +o.goal);
199
199
  mx = Math.max(mx, +o.goal);
200
200
  }
201
- var pad = (mx - mn || Math.abs(mx) || 1) * 0.12;
202
- mn -= pad;
203
- mx += pad;
201
+ var pad2 = (mx - mn || Math.abs(mx) || 1) * 0.12;
202
+ mn -= pad2;
203
+ mx += pad2;
204
204
  var X = function(i) {
205
205
  return x0 + i * w / (pts.length - 1);
206
206
  };
@@ -268,7 +268,7 @@ var THEMES = {
268
268
  };
269
269
 
270
270
  // src/render.js
271
- var NAMES = ["spark", "line", "area", "bars", "histogram", "boxplot", "scatter", "ring", "donut", "gauge", "progress", "heatmap"];
271
+ var NAMES = ["spark", "line", "area", "bars", "combo", "histogram", "boxplot", "scatter", "ring", "donut", "gauge", "progress", "heatmap"];
272
272
  var TYPES = {};
273
273
  function register(type, fn) {
274
274
  TYPES[type] = fn;
@@ -351,7 +351,16 @@ var ATTRIBUTES = [
351
351
  "bin-width",
352
352
  "mark",
353
353
  "percent",
354
- "whiskers"
354
+ "whiskers",
355
+ "line",
356
+ "line-series",
357
+ "line-prefix",
358
+ "line-suffix",
359
+ "line-decimals",
360
+ "line-label",
361
+ "line-zero",
362
+ "dates",
363
+ "log"
355
364
  ];
356
365
  var HOST_CSS = `:host{display:block;position:relative;max-width:100%}:host([hidden]){display:none}svg{display:block;max-width:100%;height:auto;overflow:visible}.tip{position:absolute;left:0;top:0;pointer-events:none;background:#0b0b10;color:#f2f1f7;font:600 12px/1.5 ${MONO};padding:7px 10px;border-radius:10px;white-space:pre;opacity:0;transform:translate(-50%,calc(-100% - 12px));transition:opacity .12s;z-index:3;border:1px solid #2a2935;box-shadow:0 8px 24px rgba(0,0,0,.35)}.tip.on{opacity:1}.c{outline:none}.c:focus-visible{outline:2px solid #fff;outline-offset:3px;box-shadow:0 0 0 7px #000}.k{stroke:currentColor;stroke-width:2;stroke-opacity:1}.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}@media (forced-colors:active){.c:focus-visible{outline-color:Highlight}}`;
357
366
  var STEPS = { ArrowLeft: -1, ArrowUp: -1, ArrowRight: 1, ArrowDown: 1 };
@@ -660,9 +669,122 @@ function seriesTable(o, total2) {
660
669
  return { head: ["Label"].concat(head, total2 ? ["Total"] : []), rows };
661
670
  }
662
671
 
672
+ // src/core/time.js
673
+ var HOUR = 36e5;
674
+ var DAY = 864e5;
675
+ function parseDates(o, n) {
676
+ var d = list(o.dates);
677
+ if (!n || d.length < n) return null;
678
+ var out = [];
679
+ for (var i = 0; i < n; i++) {
680
+ var m = /^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2}))?)?$/.exec(String(d[i]).trim());
681
+ if (!m) return null;
682
+ out.push(Date.UTC(+m[1], +m[2] - 1, +m[3], +(m[4] || 0), +(m[5] || 0), +(m[6] || 0)));
683
+ }
684
+ return out;
685
+ }
686
+ function fmt2(lang, opt) {
687
+ try {
688
+ return new Intl.DateTimeFormat(lang, Object.assign({ timeZone: "UTC" }, opt));
689
+ } catch (e) {
690
+ return new Intl.DateTimeFormat("en-GB", Object.assign({ timeZone: "UTC" }, opt));
691
+ }
692
+ }
693
+ var pad = function(n) {
694
+ return (n < 10 ? "0" : "") + n;
695
+ };
696
+ var clock = function(t) {
697
+ var d = new Date(t);
698
+ return pad(d.getUTCHours()) + ":" + pad(d.getUTCMinutes());
699
+ };
700
+ function dateText(t, lang, withTime) {
701
+ return fmt2(lang, { day: "numeric", month: "short", year: "numeric" }).format(new Date(t)) + (withTime ? " " + clock(t) : "");
702
+ }
703
+ function hasTimes(T) {
704
+ return T.some(function(t) {
705
+ return t % DAY !== 0;
706
+ });
707
+ }
708
+ var STEPS2 = [
709
+ ["h", 1],
710
+ ["h", 3],
711
+ ["h", 6],
712
+ ["h", 12],
713
+ ["d", 1],
714
+ ["d", 2],
715
+ ["w", 1],
716
+ ["w", 2],
717
+ ["m", 1],
718
+ ["m", 2],
719
+ ["m", 3],
720
+ ["m", 6],
721
+ ["y", 1],
722
+ ["y", 2],
723
+ ["y", 5],
724
+ ["y", 10],
725
+ ["y", 20],
726
+ ["y", 50],
727
+ ["y", 100]
728
+ ];
729
+ var ROUGH = { h: HOUR, d: DAY, w: 7 * DAY, m: 30.44 * DAY, y: 365.25 * DAY };
730
+ function ticksFor(t0, t1, unit, k) {
731
+ var out = [], t;
732
+ if (unit === "h" || unit === "d" || unit === "w") {
733
+ var size = unit === "h" ? HOUR * k : DAY * (unit === "w" ? 7 * k : k);
734
+ var from = unit === "w" ? 4 * DAY : 0;
735
+ for (t = Math.ceil((t0 - from) / size) * size + from; t <= t1; t += size) out.push(t);
736
+ return out;
737
+ }
738
+ var d = new Date(t0), y = d.getUTCFullYear(), mo = unit === "m" ? d.getUTCMonth() : 0;
739
+ for (; ; ) {
740
+ t = Date.UTC(y, mo, 1);
741
+ if (t > t1) return out;
742
+ if (t >= t0 && (unit === "m" ? mo % k === 0 : y % k === 0)) out.push(t);
743
+ if (unit === "m") {
744
+ mo++;
745
+ if (mo === 12) {
746
+ mo = 0;
747
+ y++;
748
+ }
749
+ } else y++;
750
+ }
751
+ }
752
+ function dateTicks(t0, t1, room, lang) {
753
+ var most = Math.max(2, Math.floor(room / 64)), span = t1 - t0;
754
+ for (var i = 0; i < STEPS2.length; i++) {
755
+ var unit = STEPS2[i][0], k = STEPS2[i][1];
756
+ if (span / (ROUGH[unit] * k) > most + 2) continue;
757
+ var ticks = ticksFor(t0, t1, unit, k);
758
+ if (ticks.length > most) continue;
759
+ var dm = fmt2(lang, { day: "numeric", month: "short" }), my = fmt2(lang, { month: "short", year: "numeric" }), mm = fmt2(lang, { month: "short" });
760
+ return ticks.map(function(t, j) {
761
+ var d = new Date(t), text2;
762
+ if (unit === "h") text2 = t % DAY === 0 ? dm.format(d) : clock(t);
763
+ else if (unit === "d" || unit === "w") text2 = dm.format(d);
764
+ else if (unit === "m") text2 = d.getUTCMonth() === 0 || j === 0 ? my.format(d) : mm.format(d);
765
+ else text2 = String(d.getUTCFullYear());
766
+ return { t, text: text2 };
767
+ });
768
+ }
769
+ return [];
770
+ }
771
+ function pointName(labels, T, i, lang, timed) {
772
+ return labels[i] || (T ? dateText(T[i], lang, timed) : "#" + (i + 1));
773
+ }
774
+ function datedTable(t, o, n) {
775
+ var T = parseDates(o, n), labels = list(o.labels);
776
+ if (!T) return t;
777
+ var timed = hasTimes(T);
778
+ t.head[0] = "Date";
779
+ t.rows.forEach(function(r, i) {
780
+ if (!labels[i]) r[0] = dateText(T[i], o.lang || "en-GB", timed);
781
+ });
782
+ return t;
783
+ }
784
+
663
785
  // src/core/xy.js
664
786
  function xyFrame(o, th, c, x0, y0, w, h) {
665
- var s = "", top = y0, n = c.n, labels = c.labels;
787
+ var s = "", top = y0, n = c.n, labels = c.labels, T = c.dates;
666
788
  if (c.names) {
667
789
  var lg = legend(c.names, c.pal, th, x0, top, w, c.key, c.kw);
668
790
  s += lg.svg;
@@ -677,24 +799,42 @@ function xyFrame(o, th, c, x0, y0, w, h) {
677
799
  mn = Math.min(0, mn);
678
800
  mx = Math.max(0, mx);
679
801
  }
680
- var sc = niceScale(mn, mx, 4);
802
+ var hasMin = isFinite(c.min), hasMax = isFinite(c.max);
803
+ if (hasMin && hasMax && c.min >= c.max) hasMin = hasMax = false;
804
+ var sc = c.log ? logScale(hasMin && c.min > 0 ? c.min : c.posLo, hasMax && c.max > 0 ? c.max : c.posHi) : hasMin || hasMax ? fixedScale(hasMin ? c.min : mn, hasMax ? c.max : mx, hasMin, hasMax) : niceScale(mn, mx, 4);
681
805
  var tl = sc.ticks.map(c.tick);
682
806
  var axisW = Math.max.apply(null, tl.map(function(t) {
683
807
  return tw(t, 10, true);
684
808
  })) + 10;
685
- var bottom = labels.length ? 20 : 4;
809
+ var bottom = labels.length || T ? 20 : 4;
686
810
  var px = x0 + axisW, pw = w - axisW, ph = h - (top - y0) - bottom;
687
- var X = function(i2) {
811
+ var t0 = T ? Math.min.apply(null, T) : 0, span = T ? Math.max.apply(null, T) - t0 : 0;
812
+ var X = T ? function(i2) {
813
+ return span ? px + (T[i2] - t0) / span * pw : px + pw / 2;
814
+ } : function(i2) {
688
815
  return n === 1 ? px + pw / 2 : px + i2 * pw / (n - 1);
689
816
  };
690
- var Y = function(v) {
817
+ var Y = sc.log ? function(v) {
818
+ return top + ph - (Math.log10(v) - sc.a) / (sc.b - sc.a) * ph;
819
+ } : function(v) {
691
820
  return top + ph - (v - sc.lo) / (sc.hi - sc.lo) * ph;
692
821
  };
822
+ var clip = "";
823
+ if (hasMin || hasMax) {
824
+ var cid = newId("c");
825
+ s += '<defs><clipPath id="' + cid + '"><rect x="' + r2(px - 4) + '" y="' + r2(top - 4) + '" width="' + r2(pw + 8) + '" height="' + r2(ph + 8) + '"/></clipPath></defs>';
826
+ clip = ' clip-path="url(#' + cid + ')"';
827
+ }
693
828
  sc.ticks.forEach(function(t, i2) {
694
829
  var y = Y(t);
695
830
  s += '<line x1="' + r2(px) + '" x2="' + r2(px + pw) + '" y1="' + r2(y) + '" y2="' + r2(y) + '" stroke="' + th.grid + '"' + (t === 0 ? "" : ' stroke-dasharray="2 4"') + "/>" + text(px - 8, y + 3.5, tl[i2], small(th, 10, 'text-anchor="end"'));
696
831
  });
697
- if (labels.length) {
832
+ if (T) {
833
+ dateTicks(t0, t0 + span, pw, o.lang || "en-GB").forEach(function(d) {
834
+ var half = tw(d.text, 10, true) / 2, x = span ? px + (d.t - t0) / span * pw : px + pw / 2;
835
+ s += text(clamp(x, px + half, px + pw - half), top + ph + 15, d.text, small(th, 10, 'text-anchor="middle"'));
836
+ });
837
+ } else if (labels.length) {
698
838
  var every = Math.max(1, Math.ceil(n * (Math.max.apply(null, labels.map(function(l) {
699
839
  return tw(l, 10, true);
700
840
  })) + 10) / pw));
@@ -709,14 +849,44 @@ function xyFrame(o, th, c, x0, y0, w, h) {
709
849
  var x = X(nt.i);
710
850
  s += '<line x1="' + r2(x) + '" x2="' + r2(x) + '" y1="' + r2(top + 12) + '" y2="' + r2(top + ph) + '" stroke="' + th.fg + '" stroke-opacity=".4" stroke-dasharray="3 3"/>' + text(clamp(x, px + 30, px + pw - 30), top + 8, nt.text, small(th, 10, 'text-anchor="middle"', th.fg));
711
851
  });
712
- return { svg: s, X, Y, px, pw, top, ph };
852
+ return { svg: s, X, Y, px, pw, top, ph, clip, uneven: !!T };
853
+ }
854
+ function fixedScale(lo, hi, hasMin, hasMax) {
855
+ var nice = niceScale(lo, hi, 4), a = hasMin ? lo : nice.lo, b = hasMax ? hi : nice.hi, step = nice.ticks[1] - nice.ticks[0];
856
+ var ticks = nice.ticks.filter(function(t) {
857
+ return t >= a - step * 1e-9 && t <= b + step * 1e-9;
858
+ });
859
+ if (hasMin && (!ticks.length || ticks[0] - a > step * 0.4)) ticks.unshift(a);
860
+ if (hasMax && (!ticks.length || b - ticks[ticks.length - 1] > step * 0.4)) ticks.push(b);
861
+ return { lo: a, hi: b, ticks };
862
+ }
863
+ function logScale(lo, hi) {
864
+ var a = Math.floor(Math.log10(lo)), b = Math.ceil(Math.log10(hi)), ticks = [];
865
+ if (b === a) b++;
866
+ for (var e = a; e <= b; e++) {
867
+ var p = Number("1e" + e);
868
+ ticks.push(p);
869
+ if (b - a <= 2 && e < b) ticks.push(2 * p, 5 * p);
870
+ }
871
+ return { log: true, a, b, lo: ticks[0], hi: ticks[ticks.length - 1], ticks };
713
872
  }
714
873
  function xyFinish(o, th, f, n, tip) {
715
- var s = has(o.goal) ? goalLine(o, th, f.px, f.px + f.pw, f.Y(+o.goal)) : "";
716
- var step = n > 1 ? f.pw / (n - 1) : f.pw;
717
- for (var j = 0; j < n; j++) s += hit(f.X(j) - step / 2, f.top, step, f.ph, tip(j));
874
+ var g = has(o.goal) ? f.Y(+o.goal) : NaN;
875
+ var s = isFinite(g) ? goalLine(o, th, f.px, f.px + f.pw, g) : "";
876
+ var step = n > 1 ? f.pw / (n - 1) : f.pw, X = f.X;
877
+ for (var j = 0; j < n; j++) {
878
+ if (!f.uneven) {
879
+ s += hit(X(j) - step / 2, f.top, step, f.ph, tip(j));
880
+ continue;
881
+ }
882
+ var l = j ? (X(j - 1) + X(j)) / 2 : X(0) - (n > 1 ? (X(1) - X(0)) / 2 : f.pw / 2);
883
+ var r = j < n - 1 ? (X(j) + X(j + 1)) / 2 : X(j) + (n > 1 ? (X(j) - X(j - 1)) / 2 : f.pw / 2);
884
+ s += hit(l, f.top, Math.max(1, r - l), f.ph, tip(j));
885
+ }
718
886
  return s;
719
887
  }
888
+
889
+ // src/core/lines.js
720
890
  function lineKey(pal, dashOf, markOf, bg) {
721
891
  return function(k, x, y) {
722
892
  var c = pal[k % pal.length], dk = dashOf ? DASHES[dashOf(k)] : "", mk = markOf ? markOf(k) : "none";
@@ -729,6 +899,7 @@ function seriesLine(o, d, c, k, dk) {
729
899
  function pointMarks(o, mk, ys, X, Y, c, bg, k) {
730
900
  var s = "", every = Math.ceil(ys.length / 40);
731
901
  ys.forEach(function(y, i) {
902
+ if (!isFinite(Y(y))) return;
732
903
  if (i % every === 0 || i === ys.length - 1) s += marker(mk, X(i), Y(y), 3.5, ' fill="' + c + '" stroke="' + bg + '" stroke-width="1.5"' + anim(o, "bzc-a", 0.6 + k * 0.15));
733
904
  });
734
905
  return s;
@@ -748,9 +919,14 @@ function line(o, th) {
748
919
  var nameOf = function(k) {
749
920
  return names[k] || "Series " + (k + 1);
750
921
  };
922
+ var T = parseDates(o, n), timed = T && hasTimes(T), lang = o.lang || "en-GB";
923
+ var all = [].concat.apply([], S), pos = all.filter(function(v) {
924
+ return v > 0;
925
+ });
926
+ if (has(o.goal) && +o.goal > 0) pos.push(+o.goal);
927
+ var log = bool(o.log) && pos.length > 0;
751
928
  return tile(o, th, h, function(x0, y0, w, h2) {
752
929
  if (!n) return emptyState(th, x0, y0, w, h2);
753
- var all = [].concat.apply([], S);
754
930
  var f = xyFrame(o, th, {
755
931
  n,
756
932
  names: multi ? S.map(function(_, k) {
@@ -766,17 +942,34 @@ function line(o, th) {
766
942
  notes,
767
943
  tick: function(t) {
768
944
  return fmt(t, o, true);
769
- }
945
+ },
946
+ dates: T,
947
+ min: has(o.min) ? +o.min : NaN,
948
+ max: has(o.max) ? +o.max : NaN,
949
+ log,
950
+ posLo: log ? Math.min.apply(null, pos) : 0,
951
+ posHi: log ? Math.max.apply(null, pos) : 0
770
952
  }, x0, y0, w, h2);
771
- var s = f.svg, X = f.X, Y = f.Y, px = f.px, pw = f.pw, top = f.top, ph = f.ph;
953
+ var s = f.svg + (f.clip ? "<g" + f.clip + ">" : ""), X = f.X, Y = f.Y, px = f.px, pw = f.pw, top = f.top, ph = f.ph;
772
954
  S.forEach(function(v, k) {
773
955
  var c = pal[k % pal.length];
774
956
  var P = v.map(function(y, i) {
775
957
  return [X(i), Y(y)];
776
958
  });
777
959
  if (P.length === 1) P = [[px, P[0][1]], [px + pw, P[0][1]]];
778
- var d = pathOf(P, bool(o.straight));
779
- if (k === 0) {
960
+ var runs = log ? P.reduce(function(r, p) {
961
+ if (isFinite(p[1])) r[r.length - 1].push(p);
962
+ else if (r[r.length - 1].length) r.push([]);
963
+ return r;
964
+ }, [[]]).filter(function(r) {
965
+ return r.length;
966
+ }) : [P];
967
+ if (!runs.length) return;
968
+ var d = runs.map(function(r) {
969
+ return pathOf(r, bool(o.straight));
970
+ }).join("");
971
+ P = [].concat.apply([], runs);
972
+ if (k === 0 && !log) {
780
973
  var g = newId("g"), last = P[P.length - 1];
781
974
  s += '<defs><linearGradient id="' + g + '" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="' + c + '" stop-opacity=".26"/><stop offset="1" stop-color="' + c + '" stop-opacity="0"/></linearGradient></defs><path d="' + d + "L" + r2(last[0]) + "," + r2(top + ph) + "L" + r2(P[0][0]) + "," + r2(top + ph) + 'Z" fill="url(#' + g + ')"' + anim(o, "bzc-a") + "/>";
782
975
  }
@@ -789,11 +982,11 @@ function line(o, th) {
789
982
  s += '<circle cx="' + r2(e[0]) + '" cy="' + r2(e[1]) + '" r="4" fill="' + c + '" stroke="' + bg + '" stroke-width="2"/>';
790
983
  }
791
984
  if (bool(o.showValues)) v.forEach(function(y, i) {
792
- s += text(X(i), Y(y) - 8, fmt(y, o, true), small(th, 9, 'text-anchor="middle"'));
985
+ if (isFinite(Y(y))) s += text(X(i), Y(y) - 8, fmt(y, o, true), small(th, 9, 'text-anchor="middle"'));
793
986
  });
794
987
  });
795
- return s + xyFinish(o, th, f, n, function(j) {
796
- var tip = labels[j] || "#" + (j + 1);
988
+ return s + (f.clip ? "</g>" : "") + xyFinish(o, th, f, n, function(j) {
989
+ var tip = pointName(labels, T, j, lang, timed);
797
990
  S.forEach(function(v, k) {
798
991
  if (j < v.length) tip += "\n" + (multi ? nameOf(k) + ": " : "") + fmt(v[j], o);
799
992
  });
@@ -802,7 +995,8 @@ function line(o, th) {
802
995
  }, { stat: st, summary: S.length + " series over " + n + " points" });
803
996
  }
804
997
  line.table = function(o) {
805
- return seriesTable(o);
998
+ var t = seriesTable(o);
999
+ return datedTable(t, o, t.rows.length);
806
1000
  };
807
1001
 
808
1002
  // src/entries/line.js
@@ -864,6 +1058,7 @@ function area(o, th) {
864
1058
  var key = patOf ? function(j2, x, y) {
865
1059
  return '<rect x="' + r2(x) + '" y="' + r2(y + 1) + '" width="10" height="10" rx="2" fill="' + (fills[j2] || pal[j2 % pal.length]) + '"/>';
866
1060
  } : (dashOf || markOf) && lineKey(pal, dashOf, markOf, bg);
1061
+ var T = parseDates(o, n), timed = T && hasTimes(T), lang = o.lang || "en-GB";
867
1062
  var share = function(j2, i2) {
868
1063
  return pos[i2] ? Math.round(Math.max(0, val(j2, i2)) / pos[i2] * 100) : 0;
869
1064
  };
@@ -882,13 +1077,17 @@ function area(o, th) {
882
1077
  zero: true,
883
1078
  labels,
884
1079
  notes,
1080
+ dates: T,
1081
+ // Areas are measured from zero, so a min above zero would cut off their base: only max and a min at or below zero apply.
1082
+ min: has(o.min) && +o.min <= 0 ? +o.min : NaN,
1083
+ max: has(o.max) ? +o.max : NaN,
885
1084
  tick: mode === "percent" ? function(t2) {
886
1085
  return fmt(t2, { lang: o.lang }, true) + "%";
887
1086
  } : function(t2) {
888
1087
  return fmt(t2, o, true);
889
1088
  }
890
1089
  }, x0, y0, w, h2);
891
- var s = (defs ? "<defs>" + defs + "</defs>" : "") + f.svg, X = f.X, Y = f.Y;
1090
+ var s = (defs ? "<defs>" + defs + "</defs>" : "") + f.svg + (f.clip ? "<g" + f.clip + ">" : ""), X = f.X, Y = f.Y;
892
1091
  var pts = function(ys) {
893
1092
  var P = ys.map(function(y, i2) {
894
1093
  return [X(i2), Y(y)];
@@ -922,8 +1121,8 @@ function area(o, th) {
922
1121
  s += text(X(i2), Y(top) - 8, fmt(t2, o, true), small(th, 9, 'text-anchor="middle"'));
923
1122
  });
924
1123
  }
925
- return s + xyFinish(o, th, f, n, function(i2) {
926
- var tip = labels[i2] || "#" + (i2 + 1);
1124
+ return s + (f.clip ? "</g>" : "") + xyFinish(o, th, f, n, function(i2) {
1125
+ var tip = pointName(labels, T, i2, lang, timed);
927
1126
  S.forEach(function(ser, j2) {
928
1127
  if (i2 < ser.length) tip += "\n" + (multi ? nameOf(j2) + ": " : "") + fmt(ser[i2], o) + (mode === "percent" ? " (" + share(j2, i2) + "%)" : "");
929
1128
  });
@@ -935,8 +1134,8 @@ function area(o, th) {
935
1134
  });
936
1135
  }
937
1136
  area.table = function(o) {
938
- var S = series(o.values);
939
- return seriesTable(o, S.length > 1 && (o.stacked === "percent" || bool(o.stacked)));
1137
+ var S = series(o.values), t = seriesTable(o, S.length > 1 && (o.stacked === "percent" || bool(o.stacked)));
1138
+ return datedTable(t, o, t.rows.length);
940
1139
  };
941
1140
 
942
1141
  // src/entries/area.js
@@ -1169,6 +1368,224 @@ bars.table = function(o) {
1169
1368
  register("bars", bars);
1170
1369
  define("bars");
1171
1370
 
1371
+ // src/charts/combo.js
1372
+ function combo(o, th) {
1373
+ var B = series(o.values), L = series(o.line), pal = palette(o, th), k = B.length, m = L.length;
1374
+ var labels = list(o.labels), bnames = list(o.series), lnames = list(o.lineSeries), stacked = k > 1 && bool(o.stacked);
1375
+ var n = Math.max.apply(null, [0].concat(B.concat(L).map(function(s) {
1376
+ return s.length;
1377
+ })));
1378
+ var lo = lineOpts(o), h = num(o.height, 200);
1379
+ var barName = function(j) {
1380
+ return bnames[j] || (k === 1 ? "Bars" : "Bars " + (j + 1));
1381
+ };
1382
+ var lineName = function(j) {
1383
+ return lnames[j] || (m === 1 ? "Line" : "Line " + (j + 1));
1384
+ };
1385
+ var barCol = function(j) {
1386
+ return pal[j % pal.length];
1387
+ }, lineCol = function(j) {
1388
+ return pal[(k + j) % pal.length];
1389
+ };
1390
+ var val = function(s, i2) {
1391
+ return i2 < s.length ? s[i2] : 0;
1392
+ };
1393
+ var dashOf = styleOf(o.dash, Object.keys(DASHES), "solid"), markOf = styleOf(o.markers, SHAPES, "none");
1394
+ var patOf = k > 1 ? styleOf(o.patterns, PATTERNS, "solid") : null, bg = o.background || th.tile, defs = "", fills = [];
1395
+ for (var q = 0; patOf && q < k; q++) {
1396
+ if (patOf(q) === "solid") continue;
1397
+ var pid = newId("g");
1398
+ defs += pattern(pid, patOf(q), barCol(q), bg);
1399
+ fills[q] = "url(#" + pid + ")";
1400
+ }
1401
+ var key = function(e, x, y) {
1402
+ if (e < k) return '<rect x="' + r2(x) + '" y="' + r2(y + 1) + '" width="10" height="10" rx="2" fill="' + (fills[e] || barCol(e)) + '"/>';
1403
+ var j = e - k, c = lineCol(j), dk = dashOf ? DASHES[dashOf(j)] : "", mk = markOf ? markOf(j) : "none";
1404
+ return '<line x1="' + r2(x) + '" x2="' + r2(x + 18) + '" y1="' + r2(y + 6) + '" y2="' + r2(y + 6) + '" stroke="' + c + '" stroke-width="2.5" stroke-linecap="round"' + (dk ? ' stroke-dasharray="' + dk + '"' : "") + "/>" + (mk !== "none" ? marker(mk, x + 9, y + 6, 3.5, ' fill="' + c + '" stroke="' + bg + '" stroke-width="1.5"') : "");
1405
+ };
1406
+ var tops = [], bottoms = [];
1407
+ for (var i = 0; i < n; i++) {
1408
+ var up = 0, down = 0;
1409
+ B.forEach(function(s) {
1410
+ var v = val(s, i);
1411
+ if (stacked) {
1412
+ if (v >= 0) up += v;
1413
+ else down += v;
1414
+ } else {
1415
+ up = Math.max(up, v);
1416
+ down = Math.min(down, v);
1417
+ }
1418
+ });
1419
+ tops.push(up);
1420
+ bottoms.push(down);
1421
+ }
1422
+ return tile(o, th, h, function(x0, y0, w, h2) {
1423
+ if (!n || !k) return emptyState(th, x0, y0, w, h2);
1424
+ var s = defs ? "<defs>" + defs + "</defs>" : "", top = y0;
1425
+ var lg = legend(B.map(function(_, j) {
1426
+ return barName(j);
1427
+ }).concat(L.map(function(_, j) {
1428
+ return lineName(j);
1429
+ })), pal, th, x0, top, w, key, 24);
1430
+ s += lg.svg;
1431
+ top += lg.height;
1432
+ var titled = has(o.yLabel) || has(o.lineLabel);
1433
+ if (titled) top += 16;
1434
+ var sc = niceScale(Math.min.apply(null, bottoms), Math.max.apply(null, tops), 4), steps = sc.ticks.length - 1;
1435
+ var all = [].concat.apply([], L);
1436
+ var lmin = Math.min.apply(null, all), lmax = Math.max.apply(null, all);
1437
+ if (bool(o.lineZero)) {
1438
+ lmin = Math.min(0, lmin);
1439
+ lmax = Math.max(0, lmax);
1440
+ }
1441
+ var sc2 = m ? matched(lmin, lmax, steps) : null;
1442
+ var tl = sc.ticks.map(function(t2) {
1443
+ return fmt(t2, o, true);
1444
+ }), tr = sc2 ? sc2.ticks.map(function(t2) {
1445
+ return fmt(t2, lo, true);
1446
+ }) : [];
1447
+ var lw = Math.max.apply(null, tl.map(function(t2) {
1448
+ return tw(t2, 10, true);
1449
+ })) + 10;
1450
+ var rw = sc2 ? Math.max.apply(null, tr.map(function(t2) {
1451
+ return tw(t2, 10, true);
1452
+ })) + 10 : 0;
1453
+ var px = x0 + lw, pw = w - lw - rw, ph = h2 - (top - y0) - (labels.length ? 20 : 4), band = pw / n;
1454
+ var X = function(i2) {
1455
+ return px + band * (i2 + 0.5);
1456
+ };
1457
+ var Y = function(v) {
1458
+ return top + ph - (v - sc.lo) / (sc.hi - sc.lo) * ph;
1459
+ };
1460
+ var Y2 = function(v) {
1461
+ return top + ph - (v - sc2.lo) / (sc2.hi - sc2.lo) * ph;
1462
+ };
1463
+ sc.ticks.forEach(function(t2, i2) {
1464
+ var y = Y(t2);
1465
+ s += '<line x1="' + r2(px) + '" x2="' + r2(px + pw) + '" y1="' + r2(y) + '" y2="' + r2(y) + '" stroke="' + th.grid + '"' + (t2 === 0 ? "" : ' stroke-dasharray="2 4"') + "/>" + text(px - 8, y + 3.5, tl[i2], small(th, 10, 'text-anchor="end"')) + (sc2 ? text(px + pw + 8, y + 3.5, tr[i2], small(th, 10, 'text-anchor="start"')) : "");
1466
+ });
1467
+ var leftC = k === 1 ? barCol(0) : th.muted, rightC = m === 1 ? lineCol(0) : th.muted;
1468
+ s += '<line x1="' + r2(px) + '" x2="' + r2(px) + '" y1="' + r2(top) + '" y2="' + r2(top + ph) + '" stroke="' + leftC + '" stroke-width="2"/>';
1469
+ if (sc2) s += '<line x1="' + r2(px + pw) + '" x2="' + r2(px + pw) + '" y1="' + r2(top) + '" y2="' + r2(top + ph) + '" stroke="' + rightC + '" stroke-width="2"/>';
1470
+ if (has(o.yLabel)) s += text(x0, top - 8, o.yLabel, small(th, 10, "", k === 1 ? leftC : th.fg));
1471
+ if (has(o.lineLabel) && sc2) s += text(x0 + w, top - 8, o.lineLabel, small(th, 10, 'text-anchor="end"', m === 1 ? rightC : th.fg));
1472
+ if (labels.length) {
1473
+ var every = Math.max(1, Math.ceil((Math.max.apply(null, labels.map(function(l) {
1474
+ return tw(l, 10, true);
1475
+ })) + 10) / band));
1476
+ for (i = 0; i < n; i += every) if (labels[i] != null) s += text(X(i), top + ph + 15, labels[i], small(th, 10, 'text-anchor="middle"'));
1477
+ }
1478
+ var gw = band * 0.72, bw = stacked ? gw : gw / k;
1479
+ for (i = 0; i < n; i++) {
1480
+ var up2 = 0, down2 = 0;
1481
+ B.forEach(function(ser, j) {
1482
+ if (i >= ser.length) return;
1483
+ var v = ser[i], a, b, x = stacked ? X(i) - gw / 2 : X(i) - gw / 2 + j * bw;
1484
+ if (stacked) {
1485
+ if (v >= 0) {
1486
+ a = up2;
1487
+ up2 += v;
1488
+ b = up2;
1489
+ } else {
1490
+ a = down2;
1491
+ down2 += v;
1492
+ b = down2;
1493
+ }
1494
+ } else {
1495
+ a = 0;
1496
+ b = v;
1497
+ }
1498
+ var ya = Y(a), yb = Y(b);
1499
+ s += '<rect x="' + r2(x + 0.5) + '" y="' + r2(Math.min(ya, yb)) + '" width="' + r2(Math.max(1, bw - 1)) + '" height="' + r2(Math.abs(yb - ya)) + '" rx="2" fill="' + (fills[j] || barCol(j)) + '"' + anim(o, v < 0 ? "bzc-gn" : "bzc-g", Math.min(0.6, i * 0.04)) + "/>";
1500
+ });
1501
+ }
1502
+ L.forEach(function(ser, j) {
1503
+ var c = lineCol(j), P = ser.map(function(v, i2) {
1504
+ return [X(i2), Y2(v)];
1505
+ }), mk = markOf ? markOf(j) : "none";
1506
+ s += seriesLine(o, pathOf(P, bool(o.straight)), c, j, dashOf ? DASHES[dashOf(j)] : "");
1507
+ if (mk !== "none") s += pointMarks(o, mk, ser, X, Y2, c, bg, j);
1508
+ else s += '<circle cx="' + r2(P[P.length - 1][0]) + '" cy="' + r2(P[P.length - 1][1]) + '" r="4" fill="' + c + '" stroke="' + bg + '" stroke-width="2"/>';
1509
+ });
1510
+ for (i = 0; i < n; i++) {
1511
+ var tip = labels[i] || "#" + (i + 1), t = 0;
1512
+ B.forEach(function(ser, j) {
1513
+ if (i < ser.length) {
1514
+ tip += "\n" + barName(j) + ": " + fmt(ser[i], o);
1515
+ t += ser[i];
1516
+ }
1517
+ });
1518
+ if (stacked) tip += "\nTotal: " + fmt(t, o);
1519
+ L.forEach(function(ser, j) {
1520
+ if (i < ser.length) tip += "\n" + lineName(j) + ": " + fmt(ser[i], lo);
1521
+ });
1522
+ s += hit(X(i) - band / 2, top, band, ph, tip);
1523
+ }
1524
+ return s;
1525
+ }, { summary: k + " bar series" + (stacked ? " stacked" : "") + " and " + m + (m === 1 ? " line" : " lines") + " over " + n + " points" });
1526
+ }
1527
+ combo.table = function(o) {
1528
+ var B = series(o.values), L = series(o.line), labels = list(o.labels), bn = list(o.series), ln = list(o.lineSeries);
1529
+ var k = B.length, m = L.length, stacked = k > 1 && bool(o.stacked), lo = lineOpts(o), rows = [];
1530
+ var n = Math.max.apply(null, [0].concat(B.concat(L).map(function(s) {
1531
+ return s.length;
1532
+ })));
1533
+ for (var i = 0; i < n; i++) {
1534
+ var row = [labels[i] || "#" + (i + 1)], t = 0;
1535
+ B.forEach(function(s) {
1536
+ row.push(i < s.length ? fmt(s[i], o) : "");
1537
+ t += i < s.length ? s[i] : 0;
1538
+ });
1539
+ if (stacked) row.push(fmt(t, o));
1540
+ L.forEach(function(s) {
1541
+ row.push(i < s.length ? fmt(s[i], lo) : "");
1542
+ });
1543
+ rows.push(row);
1544
+ }
1545
+ return {
1546
+ head: ["Label"].concat(
1547
+ B.map(function(_, j) {
1548
+ return bn[j] || (k === 1 ? "Bars" : "Bars " + (j + 1));
1549
+ }),
1550
+ stacked ? ["Total"] : [],
1551
+ L.map(function(_, j) {
1552
+ return ln[j] || (m === 1 ? "Line" : "Line " + (j + 1));
1553
+ })
1554
+ ),
1555
+ rows
1556
+ };
1557
+ };
1558
+ function lineOpts(o) {
1559
+ return { prefix: o.linePrefix, suffix: o.lineSuffix, decimals: o.lineDecimals, compact: o.compact, lang: o.lang };
1560
+ }
1561
+ function matched(min, max, n) {
1562
+ if (min === max) {
1563
+ if (min === 0) max = 1;
1564
+ else {
1565
+ var p = Math.abs(min) * 0.1;
1566
+ min -= p;
1567
+ max += p;
1568
+ }
1569
+ }
1570
+ var mag = Math.pow(10, Math.floor(Math.log10((max - min) / n))), steps = [1, 2, 5, 10], i = 0, step, lo;
1571
+ for (; ; ) {
1572
+ step = steps[i] * mag;
1573
+ lo = Math.floor(min / step) * step;
1574
+ if (lo + n * step >= max - step * 1e-9) break;
1575
+ if (++i === 4) {
1576
+ i = 1;
1577
+ mag *= 10;
1578
+ }
1579
+ }
1580
+ var ticks = [];
1581
+ for (var t = 0; t <= n; t++) ticks.push(+(lo + t * step).toFixed(10));
1582
+ return { lo: ticks[0], hi: ticks[n], ticks };
1583
+ }
1584
+
1585
+ // src/entries/combo.js
1586
+ register("combo", combo);
1587
+ define("combo");
1588
+
1172
1589
  // src/charts/histogram.js
1173
1590
  function histogram(o, th) {
1174
1591
  var b = binned(o), names = list(o.series), pal = palette(o, th), h = num(o.height, 190);
@@ -1992,7 +2409,7 @@ register("heatmap", heatmap);
1992
2409
  define("heatmap");
1993
2410
 
1994
2411
  // src/index.js
1995
- var version = "1.6.0";
2412
+ var version = "1.8.0";
1996
2413
  var types = NAMES.slice();
1997
2414
  export {
1998
2415
  BezelChart,