diff --git a/app/js/directives/timeline-overview.js b/app/js/directives/timeline-overview.js
index a4c8669..36635fd 100644
--- a/app/js/directives/timeline-overview.js
+++ b/app/js/directives/timeline-overview.js
@@ -9,6 +9,7 @@ function timelineOverview() {
var margin = timelineController.margin;
var height = 80;
var laneHeight = 10;
+ var loaded = false;
var x = timelineController.axes.x;
var y = d3.scale.linear();
@@ -49,6 +50,17 @@ function timelineOverview() {
.attr('stroke', 'rgba(100, 100, 100, 0.25)')
.attr('fill', function(d) {
return timelineController.statusColorMap[d.status];
+ })
+ .attr('class', function(d) {
+ if (timelineController.filterFunction) {
+ if (timelineController.filterFunction(d)) {
+ return 'filter-hit';
+ } else {
+ return 'filter-miss';
+ }
+ } else {
+ return null;
+ }
});
rects.exit().remove();
@@ -144,6 +156,8 @@ function timelineOverview() {
.attr('height', height - 1);
timelineController.setViewExtents(brush.extent());
+
+ loaded = true;
});
scope.$on('update', function() {
@@ -160,6 +174,13 @@ function timelineOverview() {
shiftViewport(selection.item);
}
});
+
+ scope.$on('filter', function() {
+ if (loaded) {
+ console.log('filtering');
+ updateItems(timelineController.data);
+ }
+ });
};
return {
diff --git a/app/js/directives/timeline-search.js b/app/js/directives/timeline-search.js
new file mode 100644
index 0000000..e60d748
--- /dev/null
+++ b/app/js/directives/timeline-search.js
@@ -0,0 +1,98 @@
+'use strict';
+
+var directivesModule = require('./_index.js');
+
+/**
+ * @ngInject
+ */
+function timelineSearch() {
+
+ /**
+ * @ngInject
+ */
+ var controller = function($scope, $element) {
+ var self = this;
+
+ this.open = false;
+ this.query = '';
+ this.showSuccess = true;
+ this.showSkip = true;
+ this.showFail = true;
+
+ this.results = [];
+
+ var doFilter = function(item) {
+ if ((item.status === 'success' && !self.showSuccess) ||
+ (item.status === 'skip' && !self.showSkip) ||
+ (item.status === 'fail' && !self.showFail)) {
+ return false;
+ }
+
+ if (item.name.toLowerCase().indexOf(self.query.toLowerCase()) < 0) {
+ return false;
+ }
+
+ return true;
+ };
+
+ this.updateResults = function() {
+ var timeline = $element.controller('timeline');
+ timeline.setFilterFunction(function(item) {
+ return doFilter(item);
+ });
+
+ var ret = [];
+ for (var i = 0; i < timeline.dataRaw.length; i++) {
+ var item = timeline.dataRaw[i];
+
+ if (!doFilter(item)) {
+ continue;
+ }
+
+ ret.push(timeline.dataRaw[i]);
+ if (ret.length > 25) {
+ break;
+ }
+ }
+
+ this.results = ret;
+ };
+
+ this.select = function(item) {
+ var timeline = $element.controller('timeline');
+ timeline.selectItem(item);
+ timeline.setFilterFunction(null);
+
+ self.query = '';
+ self.open = false;
+ };
+
+ var update = function(a, b) {
+ if (a === b) {
+ return;
+ }
+
+ self.updateResults();
+ };
+
+ $scope.$watch(function() { return self.query; }, update);
+ $scope.$watch(function() { return self.showSuccess; }, update);
+ $scope.$watch(function() { return self.showSkip; }, update);
+ $scope.$watch(function() { return self.showFail; }, update);
+
+ $scope.$on('dataLoaded', function() {
+ self.updateResults();
+ });
+ };
+
+ return {
+ restrict: 'EA',
+ require: ['^timelineSearch', '^timeline'],
+ scope: true,
+ controller: controller,
+ controllerAs: 'search',
+ templateUrl: 'directives/timeline-search.html'
+ };
+}
+
+directivesModule.directive('timelineSearch', timelineSearch);
diff --git a/app/js/directives/timeline-viewport.js b/app/js/directives/timeline-viewport.js
index 81cfae4..ea90738 100644
--- a/app/js/directives/timeline-viewport.js
+++ b/app/js/directives/timeline-viewport.js
@@ -202,6 +202,17 @@ function timelineViewport($document) {
return null;
}
})
+ .attr('class', function(d) {
+ if (timelineController.filterFunction) {
+ if (timelineController.filterFunction(d)) {
+ return 'filter-hit';
+ } else {
+ return 'filter-miss';
+ }
+ } else {
+ return null;
+ }
+ })
.on("mouseover", rectMouseOver)
.on('mouseout', rectMouseOut)
.on('click', rectClick);
@@ -311,6 +322,12 @@ function timelineViewport($document) {
select(null);
}
});
+
+ scope.$on('filter', function() {
+ if (loaded) {
+ update();
+ }
+ });
};
return {
diff --git a/app/js/directives/timeline.js b/app/js/directives/timeline.js
index cf51b1e..125ad46 100644
--- a/app/js/directives/timeline.js
+++ b/app/js/directives/timeline.js
@@ -56,6 +56,7 @@ function timeline($log, datasetService) {
self.selectionName = null;
self.selection = null;
self.hover = null;
+ self.filterFunction = null;
self.setViewExtents = function(extents) {
if (angular.isNumber(extents[0])) {
@@ -104,6 +105,12 @@ function timeline($log, datasetService) {
$scope.$broadcast('postSelect', self.selection);
};
+ self.setFilterFunction = function(fn) {
+ self.filterFunction = fn;
+
+ $scope.$broadcast('filter', fn);
+ };
+
self.selectItem = function(item) {
var workerItems = self.data[item.worker].values;
var index = -1;
@@ -249,9 +256,14 @@ function timeline($log, datasetService) {
var link = function(scope, el, attrs, ctrl) {
var updateWidth = function() {
- ctrl.width = el.parent()[0].clientWidth -
+ var body = el[0].querySelector('div.panel div.panel-body');
+ var style = getComputedStyle(body);
+
+ ctrl.width = body.clientWidth -
ctrl.margin.left -
- ctrl.margin.right;
+ ctrl.margin.right -
+ parseFloat(style.paddingLeft) -
+ parseFloat(style.paddingRight);
};
scope.$on('windowResize', updateWidth);
@@ -276,7 +288,7 @@ function timeline($log, datasetService) {
controllerAs: 'timeline',
restrict: 'EA',
transclude: true,
- template: '