浏览代码

HUE-5049 [editor] Introduce the timeline bar/line chart

Enrico Berti 9 年之前
父节点
当前提交
1e991c6

+ 12 - 1
desktop/core/src/desktop/static/desktop/js/ko.charts.js

@@ -20,6 +20,7 @@
     TYPES: {
     TYPES: {
       LINECHART: "lines",
       LINECHART: "lines",
       BARCHART: "bars",
       BARCHART: "bars",
+      TIMELINECHART: "timeline",
       POINTCHART: "points",
       POINTCHART: "points",
       PIECHART: "pie",
       PIECHART: "pie",
       MAP: "map",
       MAP: "map",
@@ -312,6 +313,9 @@
           chartsNormalState();
           chartsNormalState();
         }, 0);
         }, 0);
       }
       }
+      else if (_datum.length > 0) {
+        ko.bindingHandlers.timelineChart.init(element, valueAccessor);
+      }
     }
     }
   };
   };
 
 
@@ -903,6 +907,8 @@
       $(element).find("svg").empty();
       $(element).find("svg").empty();
     }
     }
 
 
+    var _hideSelection = options.hideSelection != null ? options.hideSelection : false;
+
     if ($(element).is(":visible")) {
     if ($(element).is(":visible")) {
       nv.addGraph(function () {
       nv.addGraph(function () {
         var _chart = nv.models.lineWithBrushChart();
         var _chart = nv.models.lineWithBrushChart();
@@ -915,6 +921,9 @@
         if (_datum.length > 0 && _datum[0].values.length > 10 && enableSelection) {
         if (_datum.length > 0 && _datum[0].values.length > 10 && enableSelection) {
           _chart.enableSelection();
           _chart.enableSelection();
         }
         }
+        if (_hideSelection) {
+          _chart.hideSelection();
+        }
         if (options.showControls != null) {
         if (options.showControls != null) {
           _chart.showControls(false);
           _chart.showControls(false);
         }
         }
@@ -993,7 +1002,6 @@
 
 
     if ($(element).is(":visible")) {
     if ($(element).is(":visible")) {
       nv.addGraph(function () {
       nv.addGraph(function () {
-
         var _chart;
         var _chart;
         if (isTimeline) {
         if (isTimeline) {
           if ($(element).find("svg").length > 0 && $(element).find(".nv-discreteBarWithAxes").length > 0) {
           if ($(element).find("svg").length > 0 && $(element).find(".nv-discreteBarWithAxes").length > 0) {
@@ -1004,6 +1012,9 @@
           if (_datum.length > 0 && _datum[0].values.length > 10) {
           if (_datum.length > 0 && _datum[0].values.length > 10) {
             _chart.enableSelection();
             _chart.enableSelection();
           }
           }
+          if (_hideSelection) {
+            _chart.hideSelection();
+          }
           _chart.onSelectRange(function (from, to) {
           _chart.onSelectRange(function (from, to) {
             chartsUpdatingState();
             chartsUpdatingState();
             options.onSelectRange(from, to);
             options.onSelectRange(from, to);

+ 17 - 4
desktop/core/src/desktop/static/desktop/js/nv.d3.lineWithBrushChart.js

@@ -56,11 +56,12 @@ nv.models.lineWithBrushChart = function() {
     , defaultState = null
     , defaultState = null
     , noData = 'No Data Available.'
     , noData = 'No Data Available.'
     , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'brush')
     , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'brush')
-    , controlWidth = function() { return showControls ? 300 : 0 }
+    , controlWidth = function() { return showControls ? (selectionHidden ? 240 : 300) : 0 }
     , transitionDuration = 250
     , transitionDuration = 250
     , extent
     , extent
     , brushExtent = null
     , brushExtent = null
     , selectionEnabled = false
     , selectionEnabled = false
+    , selectionHidden = false
     , onSelectRange = null
     , onSelectRange = null
     , onStateChange = null
     , onStateChange = null
     , onChartUpdate = null
     , onChartUpdate = null
@@ -205,9 +206,10 @@ nv.models.lineWithBrushChart = function() {
       }
       }
 
 
       if (showControls) {
       if (showControls) {
-        var controlsData = [
-          { key: LABELS.SELECT, disabled: !selectionEnabled, checkbox: true }
-        ];
+        var controlsData = [];
+        if (! selectionHidden) {
+          controlsData.push({ key: LABELS.SELECT, disabled: !selectionEnabled, checkbox: true });
+        }
 
 
         controls.width(controlWidth()).color(['#444', '#444', '#444']);
         controls.width(controlWidth()).color(['#444', '#444', '#444']);
         g.select('.nv-controlsWrap')
         g.select('.nv-controlsWrap')
@@ -608,6 +610,17 @@ nv.models.lineWithBrushChart = function() {
     return chart;
     return chart;
   };
   };
 
 
+  chart.disableSelection = function() {
+    selectionEnabled = false;
+    return chart;
+  };
+
+  chart.hideSelection = function() {
+    selectionHidden = true;
+    selectionEnabled = false;
+    return chart;
+  }
+
   chart.onSelectRange = function(_) {
   chart.onSelectRange = function(_) {
     if (!arguments.length) return onSelectRange;
     if (!arguments.length) return onSelectRange;
     onSelectRange = _;
     onSelectRange = _;

+ 5 - 1
desktop/libs/notebook/src/notebook/static/notebook/js/notebook.ko.js

@@ -601,6 +601,7 @@ var EditorViewModel = (function() {
     self.chartScatterGroup = ko.observable(typeof snippet.chartScatterGroup != "undefined" && snippet.chartScatterGroup != null ? snippet.chartScatterGroup : null);
     self.chartScatterGroup = ko.observable(typeof snippet.chartScatterGroup != "undefined" && snippet.chartScatterGroup != null ? snippet.chartScatterGroup : null);
     self.chartScatterSize = ko.observable(typeof snippet.chartScatterSize != "undefined" && snippet.chartScatterSize != null ? snippet.chartScatterSize : null);
     self.chartScatterSize = ko.observable(typeof snippet.chartScatterSize != "undefined" && snippet.chartScatterSize != null ? snippet.chartScatterSize : null);
     self.chartScope = ko.observable(typeof snippet.chartScope != "undefined" && snippet.chartScope != null ? snippet.chartScope : "world");
     self.chartScope = ko.observable(typeof snippet.chartScope != "undefined" && snippet.chartScope != null ? snippet.chartScope : "world");
+    self.chartTimelineType = ko.observable(typeof snippet.chartTimelineType != "undefined" && snippet.chartTimelineType != null ? snippet.chartTimelineType : "bar");
     self.chartX = ko.observable(typeof snippet.chartX != "undefined" && snippet.chartX != null ? snippet.chartX : null);
     self.chartX = ko.observable(typeof snippet.chartX != "undefined" && snippet.chartX != null ? snippet.chartX : null);
     self.chartX.extend({notify: 'always'});
     self.chartX.extend({notify: 'always'});
     self.chartXPivot = ko.observable(typeof snippet.chartXPivot != "undefined" && snippet.chartXPivot != null ? snippet.chartXPivot : null);
     self.chartXPivot = ko.observable(typeof snippet.chartXPivot != "undefined" && snippet.chartXPivot != null ? snippet.chartXPivot : null);
@@ -612,7 +613,7 @@ var EditorViewModel = (function() {
     self.chartMapLabel = ko.observable(typeof snippet.chartMapLabel != "undefined" && snippet.chartMapLabel != null ? snippet.chartMapLabel : null);
     self.chartMapLabel = ko.observable(typeof snippet.chartMapLabel != "undefined" && snippet.chartMapLabel != null ? snippet.chartMapLabel : null);
 
 
     self.hasDataForChart = ko.computed(function () {
     self.hasDataForChart = ko.computed(function () {
-      if (self.chartType() == ko.HUE_CHARTS.TYPES.BARCHART || self.chartType() == ko.HUE_CHARTS.TYPES.LINECHART) {
+      if (self.chartType() == ko.HUE_CHARTS.TYPES.BARCHART || self.chartType() == ko.HUE_CHARTS.TYPES.LINECHART || self.chartType() == ko.HUE_CHARTS.TYPES.TIMELINECHART) {
         return typeof self.chartX() != "undefined" && self.chartX() != null && self.chartYMulti().length > 0;
         return typeof self.chartX() != "undefined" && self.chartX() != null && self.chartYMulti().length > 0;
       }
       }
       return typeof self.chartX() != "undefined" && self.chartX() != null && typeof self.chartYSingle() != "undefined" && self.chartYSingle() != null;
       return typeof self.chartX() != "undefined" && self.chartX() != null && typeof self.chartYSingle() != "undefined" && self.chartYSingle() != null;
@@ -669,6 +670,7 @@ var EditorViewModel = (function() {
       self.chartScatterGroup(self.previousChartOptions.chartScatterGroup);
       self.chartScatterGroup(self.previousChartOptions.chartScatterGroup);
       self.chartScatterSize(self.previousChartOptions.chartScatterSize);
       self.chartScatterSize(self.previousChartOptions.chartScatterSize);
       self.chartScope(self.previousChartOptions.chartScope);
       self.chartScope(self.previousChartOptions.chartScope);
+      self.chartTimelineType(self.previousChartOptions.chartTimelineType);
     });
     });
 
 
     self.isResultSettingsVisible = ko.observable(typeof snippet.isResultSettingsVisible != "undefined" && snippet.isResultSettingsVisible != null ? snippet.isResultSettingsVisible : false);
     self.isResultSettingsVisible = ko.observable(typeof snippet.isResultSettingsVisible != "undefined" && snippet.isResultSettingsVisible != null ? snippet.isResultSettingsVisible : false);
@@ -774,6 +776,7 @@ var EditorViewModel = (function() {
 
 
       self.previousChartOptions = {
       self.previousChartOptions = {
         chartScope: typeof self.chartScope() !== "undefined" ? self.chartScope() : self.previousChartOptions.chartScope,
         chartScope: typeof self.chartScope() !== "undefined" ? self.chartScope() : self.previousChartOptions.chartScope,
+        chartTimelineType: typeof self.chartTimelineType() !== "undefined" ? self.chartTimelineType() : self.previousChartOptions.chartTimelineType,
         chartX: typeof self.chartX() !== "undefined" ? self.chartX() : self.previousChartOptions.chartX,
         chartX: typeof self.chartX() !== "undefined" ? self.chartX() : self.previousChartOptions.chartX,
         chartXPivot: typeof self.chartXPivot() !== "undefined" ? self.chartXPivot() : self.previousChartOptions.chartXPivot,
         chartXPivot: typeof self.chartXPivot() !== "undefined" ? self.chartXPivot() : self.previousChartOptions.chartXPivot,
         chartYSingle: typeof self.chartYSingle() !== "undefined" ? self.chartYSingle() : self.previousChartOptions.chartYSingle,
         chartYSingle: typeof self.chartYSingle() !== "undefined" ? self.chartYSingle() : self.previousChartOptions.chartYSingle,
@@ -2135,6 +2138,7 @@ var EditorViewModel = (function() {
             chartMapLabel: typeof snippet.chartMapLabel() !== "undefined" ? snippet.chartMapLabel() : snippet.previousChartOptions.chartMapLabel,
             chartMapLabel: typeof snippet.chartMapLabel() !== "undefined" ? snippet.chartMapLabel() : snippet.previousChartOptions.chartMapLabel,
             chartYMulti: typeof snippet.chartYMulti() !== "undefined" ? snippet.chartYMulti() : snippet.previousChartOptions.chartYMulti,
             chartYMulti: typeof snippet.chartYMulti() !== "undefined" ? snippet.chartYMulti() : snippet.previousChartOptions.chartYMulti,
             chartScope: typeof snippet.chartScope() !== "undefined" ? snippet.chartScope() : snippet.previousChartOptions.chartScope,
             chartScope: typeof snippet.chartScope() !== "undefined" ? snippet.chartScope() : snippet.previousChartOptions.chartScope,
+            chartTimelineType: typeof snippet.chartTimelineType() !== "undefined" ? snippet.chartTimelineType() : snippet.previousChartOptions.chartTimelineType,
             chartSorting: typeof snippet.chartSorting() !== "undefined" ? snippet.chartSorting() : snippet.previousChartOptions.chartSorting,
             chartSorting: typeof snippet.chartSorting() !== "undefined" ? snippet.chartSorting() : snippet.previousChartOptions.chartSorting,
             chartScatterGroup: typeof snippet.chartScatterGroup() !== "undefined" ? snippet.chartScatterGroup() : snippet.previousChartOptions.chartScatterGroup,
             chartScatterGroup: typeof snippet.chartScatterGroup() !== "undefined" ? snippet.chartScatterGroup() : snippet.previousChartOptions.chartScatterGroup,
             chartScatterSize: typeof snippet.chartScatterSize() !== "undefined" ? snippet.chartScatterSize() : snippet.previousChartOptions.chartScatterSize
             chartScatterSize: typeof snippet.chartScatterSize() !== "undefined" ? snippet.chartScatterSize() : snippet.previousChartOptions.chartScatterSize

+ 81 - 2
desktop/libs/notebook/src/notebook/templates/editor_components.mako

@@ -1626,15 +1626,30 @@ ${ hueIcons.symbols() }
 
 
 <script type="text/html" id="snippet-chart-settings">
 <script type="text/html" id="snippet-chart-settings">
   <div>
   <div>
+    <!-- ko if: chartType() != '' && chartType() == ko.HUE_CHARTS.TYPES.TIMELINECHART -->
+    <ul class="nav nav-list" style="border: none; background-color: #FFF">
+      <li class="nav-header">${_('type')}</li>
+    </ul>
+    <div data-bind="visible: chartType() != ''">
+      <select data-bind="selectedOptions: chartTimelineType, optionsCaption: '${_ko('Choose a type...')}', select2: { width: '100%', placeholder: '${ _ko("Choose a type...") }', update: chartTimelineType, dropdownAutoWidth: true}">
+        <option value="bar">${ _("Bars") }</option>
+        <option value="line">${ _("Lines") }</option>
+      </select>
+    </div>
+    <!-- /ko -->
+
     <ul class="nav nav-list" style="border: none; background-color: #FFF" data-bind="visible: chartType() != ''">
     <ul class="nav nav-list" style="border: none; background-color: #FFF" data-bind="visible: chartType() != ''">
       <li data-bind="visible: [ko.HUE_CHARTS.TYPES.MAP, ko.HUE_CHARTS.TYPES.GRADIENTMAP, ko.HUE_CHARTS.TYPES.PIECHART].indexOf(chartType()) == -1" class="nav-header">${_('x-axis')}</li>
       <li data-bind="visible: [ko.HUE_CHARTS.TYPES.MAP, ko.HUE_CHARTS.TYPES.GRADIENTMAP, ko.HUE_CHARTS.TYPES.PIECHART].indexOf(chartType()) == -1" class="nav-header">${_('x-axis')}</li>
       <li data-bind="visible: chartType() == ko.HUE_CHARTS.TYPES.GRADIENTMAP" class="nav-header">${_('region')}</li>
       <li data-bind="visible: chartType() == ko.HUE_CHARTS.TYPES.GRADIENTMAP" class="nav-header">${_('region')}</li>
       <li data-bind="visible: chartType() == ko.HUE_CHARTS.TYPES.MAP" class="nav-header">${_('latitude')}</li>
       <li data-bind="visible: chartType() == ko.HUE_CHARTS.TYPES.MAP" class="nav-header">${_('latitude')}</li>
       <li data-bind="visible: chartType() == ko.HUE_CHARTS.TYPES.PIECHART" class="nav-header">${_('legend')}</li>
       <li data-bind="visible: chartType() == ko.HUE_CHARTS.TYPES.PIECHART" class="nav-header">${_('legend')}</li>
     </ul>
     </ul>
-    <div data-bind="visible: chartType() != ''">
+    <div data-bind="visible: chartType() != ko.HUE_CHARTS.TYPES.TIMELINECHART">
       <select data-bind="options: (chartType() == ko.HUE_CHARTS.TYPES.BARCHART || chartType() == ko.HUE_CHARTS.TYPES.PIECHART || chartType() == ko.HUE_CHARTS.TYPES.GRADIENTMAP) ? result.cleanedMeta : result.cleanedNumericMeta, value: chartX, optionsText: 'name', optionsValue: 'name', optionsCaption: '${_ko('Choose a column...')}', select2: { width: '100%', placeholder: '${ _ko("Choose a column...") }', update: chartX, dropdownAutoWidth: true}" class="input-medium"></select>
       <select data-bind="options: (chartType() == ko.HUE_CHARTS.TYPES.BARCHART || chartType() == ko.HUE_CHARTS.TYPES.PIECHART || chartType() == ko.HUE_CHARTS.TYPES.GRADIENTMAP) ? result.cleanedMeta : result.cleanedNumericMeta, value: chartX, optionsText: 'name', optionsValue: 'name', optionsCaption: '${_ko('Choose a column...')}', select2: { width: '100%', placeholder: '${ _ko("Choose a column...") }', update: chartX, dropdownAutoWidth: true}" class="input-medium"></select>
     </div>
     </div>
+    <div data-bind="visible: chartType() == ko.HUE_CHARTS.TYPES.TIMELINECHART">
+      <select data-bind="options: result.cleanedDateTimeMeta, value: chartX, optionsText: 'name', optionsValue: 'name', optionsCaption: '${_ko('Choose a column...')}', select2: { width: '100%', placeholder: '${ _ko("Choose a column...") }', update: chartX, dropdownAutoWidth: true}" class="input-medium"></select>
+    </div>
 
 
     <ul class="nav nav-list" style="border: none; background-color: #FFF" data-bind="visible: chartType() != ''">
     <ul class="nav nav-list" style="border: none; background-color: #FFF" data-bind="visible: chartType() != ''">
       <li data-bind="visible: [ko.HUE_CHARTS.TYPES.MAP, ko.HUE_CHARTS.TYPES.GRADIENTMAP, ko.HUE_CHARTS.TYPES.PIECHART].indexOf(chartType()) == -1" class="nav-header">${_('y-axis')}</li>
       <li data-bind="visible: [ko.HUE_CHARTS.TYPES.MAP, ko.HUE_CHARTS.TYPES.GRADIENTMAP, ko.HUE_CHARTS.TYPES.PIECHART].indexOf(chartType()) == -1" class="nav-header">${_('y-axis')}</li>
@@ -1643,7 +1658,7 @@ ${ hueIcons.symbols() }
       <li data-bind="visible: chartType() == ko.HUE_CHARTS.TYPES.PIECHART" class="nav-header">${_('value')}</li>
       <li data-bind="visible: chartType() == ko.HUE_CHARTS.TYPES.PIECHART" class="nav-header">${_('value')}</li>
     </ul>
     </ul>
 
 
-    <div style="overflow-y: auto; max-height: 220px" data-bind="visible: chartType() != '' && ((chartType() == ko.HUE_CHARTS.TYPES.BARCHART && !chartXPivot()) || chartType() == ko.HUE_CHARTS.TYPES.LINECHART)">
+    <div style="overflow-y: auto; max-height: 220px" data-bind="visible: chartType() != '' && ((chartType() == ko.HUE_CHARTS.TYPES.BARCHART && !chartXPivot()) || chartType() == ko.HUE_CHARTS.TYPES.LINECHART || chartType() == ko.HUE_CHARTS.TYPES.TIMELINECHART)">
       <ul class="unstyled" data-bind="foreach: result.cleanedNumericMeta">
       <ul class="unstyled" data-bind="foreach: result.cleanedNumericMeta">
         <li><label class="checkbox"><input type="checkbox" data-bind="checkedValue: name, checked: $parent.chartYMulti" /> <span data-bind="text: $data.name"></span></label></li>
         <li><label class="checkbox"><input type="checkbox" data-bind="checkedValue: name, checked: $parent.chartYMulti" /> <span data-bind="text: $data.name"></span></label></li>
       </ul>
       </ul>
@@ -1819,6 +1834,11 @@ ${ hueIcons.symbols() }
                       transformer: multiSerieDataTransformer, showControls: false, enableSelection: false }, visible: chartType() == ko.HUE_CHARTS.TYPES.LINECHART" class="chart"></div>
                       transformer: multiSerieDataTransformer, showControls: false, enableSelection: false }, visible: chartType() == ko.HUE_CHARTS.TYPES.LINECHART" class="chart"></div>
                 <!-- /ko -->
                 <!-- /ko -->
 
 
+                <!-- ko if: chartType() == ko.HUE_CHARTS.TYPES.TIMELINECHART -->
+                <div data-bind="attr:{'id': 'timelineChart_'+id()}, timelineChart: {type: chartTimelineType, skipWindowResize: true, datum: {counts: result.data, sorting: chartSorting(), snippet: $data}, fqs: ko.observableArray([]), hideSelection: true,
+                      transformer: timelineChartDataTransformer, stacked: false, showLegend: true}, hideSelection: true, visible: chartType() == ko.HUE_CHARTS.TYPES.TIMELINECHART" class="chart"></div>
+                <!-- /ko -->
+
                 <!-- ko if: chartType() == ko.HUE_CHARTS.TYPES.MAP -->
                 <!-- ko if: chartType() == ko.HUE_CHARTS.TYPES.MAP -->
                 <div data-bind="attr:{'id': 'leafletMapChart_'+id()}, leafletMapChart: {datum: {counts: result.data, sorting: chartSorting(), snippet: $data},
                 <div data-bind="attr:{'id': 'leafletMapChart_'+id()}, leafletMapChart: {datum: {counts: result.data, sorting: chartSorting(), snippet: $data},
                       transformer: leafletMapChartDataTransformer, showControls: false, height: 380, visible: chartType() == ko.HUE_CHARTS.TYPES.MAP, forceRedraw: true}" class="chart"></div>
                       transformer: leafletMapChartDataTransformer, showControls: false, height: 380, visible: chartType() == ko.HUE_CHARTS.TYPES.MAP, forceRedraw: true}" class="chart"></div>
@@ -2103,6 +2123,7 @@ ${ hueIcons.symbols() }
       <a class="snippet-side-btn" style="padding-right:0" href="javascript: void(0)" data-bind="css: {'active': $data.showChart }, click: function() { $data.showChart(true); }" title="${ _('Charts') }">
       <a class="snippet-side-btn" style="padding-right:0" href="javascript: void(0)" data-bind="css: {'active': $data.showChart }, click: function() { $data.showChart(true); }" title="${ _('Charts') }">
         <i class="hcha fa-fw hcha-bar-chart" data-bind="visible: chartType() == ko.HUE_CHARTS.TYPES.BARCHART"></i>
         <i class="hcha fa-fw hcha-bar-chart" data-bind="visible: chartType() == ko.HUE_CHARTS.TYPES.BARCHART"></i>
         <i class="hcha fa-fw hcha-line-chart" data-bind="visible: chartType() == ko.HUE_CHARTS.TYPES.LINECHART"></i>
         <i class="hcha fa-fw hcha-line-chart" data-bind="visible: chartType() == ko.HUE_CHARTS.TYPES.LINECHART"></i>
+        <i class="hcha fa-fw hcha-timeline-chart" data-bind="visible: chartType() == ko.HUE_CHARTS.TYPES.TIMELINECHART"></i>
         <i class="hcha fa-fw hcha-pie-chart" data-bind="visible: chartType() == ko.HUE_CHARTS.TYPES.PIECHART"></i>
         <i class="hcha fa-fw hcha-pie-chart" data-bind="visible: chartType() == ko.HUE_CHARTS.TYPES.PIECHART"></i>
         <i class="fa fa-fw fa-dot-circle-o" data-bind="visible: chartType() == ko.HUE_CHARTS.TYPES.SCATTERCHART"></i>
         <i class="fa fa-fw fa-dot-circle-o" data-bind="visible: chartType() == ko.HUE_CHARTS.TYPES.SCATTERCHART"></i>
         <i class="fa fa-fw fa-map-marker" data-bind="visible: chartType() == ko.HUE_CHARTS.TYPES.MAP"></i>
         <i class="fa fa-fw fa-map-marker" data-bind="visible: chartType() == ko.HUE_CHARTS.TYPES.MAP"></i>
@@ -2123,6 +2144,11 @@ ${ hueIcons.symbols() }
             <i class="hcha hcha-line-chart"></i> ${_('Lines')}
             <i class="hcha hcha-line-chart"></i> ${_('Lines')}
           </a>
           </a>
         </li>
         </li>
+        <li data-bind="visible: result.cleanedDateTimeMeta().length > 0">
+          <a href="javascript:void(0)" data-bind="css: {'active': chartType() == ko.HUE_CHARTS.TYPES.TIMELINECHART}, click: function(){ $data.showChart(true); chartType(ko.HUE_CHARTS.TYPES.TIMELINECHART); }">
+            <i class="hcha hcha-timeline-chart"></i> ${_('Time')}
+          </a>
+        </li>
         <li>
         <li>
           <a href="javascript:void(0)" data-bind="css: {'active': chartType() == ko.HUE_CHARTS.TYPES.PIECHART}, click: function(){ $data.showChart(true); chartType(ko.HUE_CHARTS.TYPES.PIECHART); }">
           <a href="javascript:void(0)" data-bind="css: {'active': chartType() == ko.HUE_CHARTS.TYPES.PIECHART}, click: function(){ $data.showChart(true); chartType(ko.HUE_CHARTS.TYPES.PIECHART); }">
             <i class="hcha hcha-pie-chart"></i> ${_('Pie')}
             <i class="hcha hcha-pie-chart"></i> ${_('Pie')}
@@ -2697,6 +2723,59 @@ ${ hueIcons.symbols() }
     return _data;
     return _data;
   }
   }
 
 
+
+  function timelineChartDataTransformer(rawDatum) {
+    var _datum = [];
+    var _plottedSerie = 0;
+
+    rawDatum.snippet.result.meta().forEach(function (meta) {
+      if (rawDatum.snippet.chartYMulti().indexOf(meta.name) > -1) {
+        var col = meta.name;
+        var _idxValue = -1;
+        var _idxLabel = -1;
+        rawDatum.snippet.result.meta().forEach(function (icol, idx) {
+          if (icol.name == rawDatum.snippet.chartX()) {
+            _idxLabel = idx;
+          }
+          if (icol.name == col) {
+            _idxValue = idx;
+          }
+        });
+
+        if (_idxValue > -1) {
+          var _data = [];
+          var colors = HueColors.cuiD3Scale();
+          $(rawDatum.counts()).each(function (cnt, item) {
+            _data.push({
+              series: _plottedSerie,
+              x: new Date(moment(hueUtils.html2text(item[_idxLabel]).valueOf())),
+              y: item[_idxValue] * 1,
+              color: colors[_plottedSerie % colors.length],
+              obj: item
+            });
+          });
+          if (rawDatum.sorting == "asc") {
+            _data.sort(function (a, b) {
+              return a.y - b.y
+            });
+          }
+          if (rawDatum.sorting == "desc") {
+            _data.sort(function (a, b) {
+              return b.y - a.y
+            });
+          }
+          _datum.push({
+            key: col,
+            values: _data
+          });
+          _plottedSerie++;
+        }
+      }
+    });
+
+    return _datum;
+  }
+
   function multiSerieDataTransformer(rawDatum) {
   function multiSerieDataTransformer(rawDatum) {
     var _datum = [];
     var _datum = [];