diff --git a/web/src/main/webapp/components/infiniteCircularScroll/InfiniteCircularScroll.js b/web/src/main/webapp/components/infiniteCircularScroll/InfiniteCircularScroll.js
new file mode 100644
index 000000000..864c2a285
--- /dev/null
+++ b/web/src/main/webapp/components/infiniteCircularScroll/InfiniteCircularScroll.js
@@ -0,0 +1,167 @@
+(function(window, $) {
+ 'use strict';
+ var ROW_SPARE_COUNT = 8;
+ window.InfiniteCircularScroll = $.Class({
+ $init: function(options) {
+ this.option(options);
+ this._init();
+ this._initVar();
+ this._initEvent();
+ },
+ _init: function() {
+ this.$wrapper = this.option("wrapper");
+ this._threshold = this.option("elementHeight") * (ROW_SPARE_COUNT / 4); // 2 * elementHeight
+ this._previousTop = 0;
+ this._previousTime = -1;
+ this._selectedRow = -1;
+ },
+ _initVar: function() {
+ this._elementArray = [];
+ this._elementArrayStartIndex = 0;
+ this._elementArrayEndIndex = 0;
+ this._elementArraySize = 0;
+ this._previousTop = 0;
+ },
+ _initEvent: function() {
+ var self = this;
+ this.option("scroller").on("scroll", function(event) {
+ self._scrollEventHandler(event, $(this));
+ });
+ },
+ _calculateElementStartCount: function() {
+ var temp = parseInt( this._viewAreaHeight / this.option("elementHeight") ) + ROW_SPARE_COUNT;
+ this._elementArraySize = temp > this._source.length ? this._source.length : temp;
+ this._elementArrayEndIndex = this._elementArraySize - 1;
+ },
+ _initElementArray: function() {
+ this.$wrapper.empty();
+ for( var i = 0 ; i < this._elementArraySize ; i++ ) {
+ var $element = $(this.option("template"));
+ this._renderElement($element, i);
+ this._elementArray.push($element);
+ this.$wrapper.append($element);
+ }
+ },
+ _renderElement: function($element, index) {
+ this._renderFunc.call( this, $element, index, this._source[index] );
+ $element.attr("data-index", index).css("top", (index * this.option("elementHeight")) + "px" );
+ },
+ _getDataIndex: function(index) {
+ return parseInt(this._elementArray[index].attr("data-index"));
+ },
+ _getTopByIndex: function(index) {
+ return parseInt(this._elementArray[index].css("top"));
+ },
+ _getNextArrayIndex: function(index) {
+ return index + 1 >= this._elementArraySize ? 0 : index + 1;
+ },
+ _getPreviousArrayIndex: function(index) {
+ return ( index - 1 < 0 ? this._elementArraySize : index ) - 1;
+ },
+ _scrollEventHandler: function(event, $targetElement) {
+ var top = $targetElement.scrollTop();
+ var mTime = new Date().valueOf();
+ if ( top == this._previousTop ) return;
+// if ( this._previousTime != -1 && ( mTime - this._previousTime ) < 16 ) return;
+
+ var bIsDown = top - this._previousTop > 0;
+ if ( bIsDown ) { // 아래로 스크롤 - top 값은 커진다.
+ this._scrollDown(top, this._getTopByIndex(this._elementArrayStartIndex));
+ } else { // 위로 스크롤 - top 값은 작아진다.
+ this._scrollUp(top, this._getTopByIndex(this._elementArrayEndIndex));
+ }
+ this._previousTop = top;
+ this._previousTime = mTime;
+ },
+ _scrollDown: function( top, firstElementTop ) {
+ var exceededDistance = top - firstElementTop - this._threshold;
+ if ( exceededDistance >= 0 ) {
+ var sourceLastIndex = this._getDataIndex(this._elementArrayEndIndex);
+ var nextIndex = this._elementArrayStartIndex;
+ var nextIndexStep = parseInt(exceededDistance / this.option("elementHeight"));
+
+ for( var i = 0 ; i < nextIndexStep ; i++ ) {
+ var sourceIndex = sourceLastIndex + i + 1;
+ if ( sourceIndex >= this._source.length ) {
+ break;
+ }
+ this._renderElement(this._elementArray[nextIndex], sourceIndex);
+ nextIndex = this._getNextArrayIndex(nextIndex);
+ }
+ this._elementArrayStartIndex = nextIndex;
+ this._elementArrayEndIndex = this._getPreviousArrayIndex(nextIndex);
+ }
+ },
+ _scrollUp: function( top, lastElementTop ) {
+ var exceededDistance = lastElementTop + this.option("elementHeight") - top - this._viewAreaHeight - this._threshold;
+ if ( exceededDistance >= 0 ) {
+ var sourceFirstIndex = this._getDataIndex(this._elementArrayStartIndex);
+ var previousIndex = this._elementArrayEndIndex;
+ var previousIndexStep = parseInt(exceededDistance / this.option("elementHeight"));
+
+ for( var i = 0 ; i < previousIndexStep ; i++ ) {
+ var sourceIndex = sourceFirstIndex - (i + 1);
+ if ( sourceIndex < 0 ) {
+ break;
+ }
+ this._renderElement(this._elementArray[previousIndex], sourceIndex);
+ previousIndex = this._getPreviousArrayIndex(previousIndex);
+ }
+ this._elementArrayEndIndex = previousIndex;
+ this._elementArrayStartIndex = this._getNextArrayIndex(previousIndex);
+ }
+ },
+ setSource: function( source ) {
+ this._source= source;
+ return this;
+ },
+ setViewAreaHeight: function( viewAreaHeight ) {
+ this._viewAreaHeight = viewAreaHeight;
+ return this;
+ },
+ setRenderFunc: function( fnRender ) {
+ this._renderFunc = fnRender;
+ return this;
+ },
+ reset: function() {
+ this._initVar();
+ this._calculateElementStartCount();
+ this._initElementArray();
+ return this;
+ },
+ getContentsAreaHeight: function() {
+ return this.option("elementHeight") * this._source.length;
+ },
+ destroy: function() {
+ this.$wrapper.parent().off("scroll");
+ },
+ resize: function( resizedViewAreaHeight ) {
+ var currentRow = parseInt(this.option("scroller").scrollTop() / this.option("elementHeight"));
+ this.setViewAreaHeight(resizedViewAreaHeight);
+ this.reset();
+ this.moveByRow( currentRow );
+ },
+ moveByRow: function( row ) { // from 0
+ var self = this;
+ setTimeout(function() {
+ self.option("scroller").scrollTop(row * self.option("elementHeight"));
+ },0);
+ },
+ searchRow: function( from, to, func) {
+ var resultRow = -1;
+ to = to == -1 ? this._source.length : to;
+ for( var i = from ; i < to ; i++ ) {
+ if ( func(this._source[i]) ) {
+ resultRow = i;
+ break;
+ }
+ }
+ return resultRow;
+ },
+ setSelectedRow: function(row, func) {
+ func.call(this, row);
+ this._selectedRow = row;
+ return this;
+ }
+ });
+})(window, jQuery);
diff --git a/web/src/main/webapp/features/distributedCallFlow/distributed-call-flow.directive.js b/web/src/main/webapp/features/distributedCallFlow/distributed-call-flow.directive.js
index b719f53e2..8cd483a17 100644
--- a/web/src/main/webapp/features/distributedCallFlow/distributed-call-flow.directive.js
+++ b/web/src/main/webapp/features/distributedCallFlow/distributed-call-flow.directive.js
@@ -424,13 +424,13 @@
if ( row == -1 ) {
if ( index > 0 ) {
selectRow( searchRowByTime(time, 0) );
- scope.$emit("transactionDetail.searchCallresult", "Loop" );
+ scope.$emit("transactionDetail.calltreeSearchCallResult", "Loop" );
} else {
- scope.$emit("transactionDetail.searchCallresult", "No call took longer than {time}ms." );
+ scope.$emit("transactionDetail.calltreeSearchCallResult", "No call took longer than {time}ms." );
}
} else {
selectRow(row);
- scope.$emit("transactionDetail.searchCallresult", "" );
+ scope.$emit("transactionDetail.calltreeSearchCallResult", "" );
}
});
searchRowByTime = function( time, index ) {
diff --git a/web/src/main/webapp/features/timeline/timeline.directive.js b/web/src/main/webapp/features/timeline/timeline.directive.js
index 787f189f6..804091ea3 100644
--- a/web/src/main/webapp/features/timeline/timeline.directive.js
+++ b/web/src/main/webapp/features/timeline/timeline.directive.js
@@ -1,163 +1,6 @@
(function( $ ) {
'use strict';
-
- function InfiniteCircularScroller(options) {
- this.options = options || {};
- this._init();
- this._initVar();
- this._initEvent();
- };
- InfiniteCircularScroller.prototype._init = function() {
- this.options.initPlusCount = 8;
- this.$wrapper = this.options.wrapper;
- this._threshold = this.options.elementHeight * (this.options.initPlusCount / 4);
- this._previousTop = 0;
- };
- InfiniteCircularScroller.prototype._initVar = function() {
- this._elementArray = [];
- this._elementArrayStartIndex = 0;
- this._elementArrayEndIndex = 0;
- this._elementArraySize = 0;
- this._previousTop = 0;
- };
- InfiniteCircularScroller.prototype._initEvent = function() {
- var self = this;
- this.options.scroller.on("scroll", function(event) {
- self._scrollEventHandler(event, $(this));
- });
- };
- InfiniteCircularScroller.prototype._calculateElementStartCount = function() {
- var temp = parseInt( this._viewAreaHeight / this.options.elementHeight ) + this.options.initPlusCount;
- this._elementArraySize = temp > this._source.length ? this._source.length : temp;
- this._elementArrayEndIndex = this._elementArraySize - 1;
- };
- InfiniteCircularScroller.prototype._initElementArray = function() {
- this.$wrapper.empty();
- for( var i = 0 ; i < this._elementArraySize ; i++ ) {
- var $element = $(this.options.template);
- this._renderElement($element, i);
- this._elementArray.push($element);
- this.$wrapper.append($element);
- }
- };
- InfiniteCircularScroller.prototype._renderElement = function($element, index) {
- this._renderFunc.call( this, $element, index, this._source[index] );
- $element.attr("data-index", index).css("top", (index * this.options.elementHeight) + "px" );
- };
- InfiniteCircularScroller.prototype._getDataIndex = function(index) {
- return parseInt(this._elementArray[index].attr("data-index"));
- };
- InfiniteCircularScroller.prototype._getTopByIndex = function(index) {
- return parseInt(this._elementArray[index].css("top"));
- };
- InfiniteCircularScroller.prototype._getNextArrayIndex = function(index) {
- return index + 1 >= this._elementArraySize ? 0 : index + 1;
- };
- InfiniteCircularScroller.prototype._getPreviousArrayIndex = function(index) {
- return ( index - 1 < 0 ? this._elementArraySize : index ) - 1;
- };
- InfiniteCircularScroller.prototype._scrollEventHandler = function(event, $targetElement) {
- var top = $targetElement.scrollTop();
- if ( top == this._previousTop ) return;
- if ( top < this._threshold ) return;
-
- //if ( top - this._previousTop > this._viewAreaHeight ) 스크롤 이벤트 입력값이 화면 보다 크게 오는 경우
- var bIsDown = top - this._previousTop > 0;
- if ( bIsDown ) { // 아래로 스크롤 - top 값은 커진다.
- this._scrollDown(top, this._getTopByIndex(this._elementArrayStartIndex));
- } else { // 위로 스크롤 - top 값은 작아진다.
- this._scrollUp(top, this._getTopByIndex(this._elementArrayEndIndex));
- }
- this._previousTop = top;
- };
- InfiniteCircularScroller.prototype._scrollDown = function( top, firstElementTop ) {
- var exceededDistance = top - firstElementTop - this._threshold;
- if ( exceededDistance >= 0 ) {
- var sourceLastIndex = this._getDataIndex(this._elementArrayEndIndex);
- var nextIndex = this._elementArrayStartIndex;
- var nextIndexStep = parseInt(exceededDistance / this.options.elementHeight);
-
- for( var i = 0 ; i < nextIndexStep ; i++ ) {
- var sourceIndex = sourceLastIndex + i + 1;
- if ( sourceIndex >= this._source.length ) {
- break;
- }
- this._renderElement(this._elementArray[nextIndex], sourceIndex);
- nextIndex = this._getNextArrayIndex(nextIndex);
- }
- this._elementArrayStartIndex = nextIndex;
- this._elementArrayEndIndex = this._getPreviousArrayIndex(nextIndex);
- }
- };
- InfiniteCircularScroller.prototype._scrollUp = function( top, lastElementTop ) {
- var exceededDistance = lastElementTop + this.options.elementHeight - top - this._viewAreaHeight - this._threshold;
- if ( exceededDistance >= 0 ) {
- var sourceFirstIndex = this._getDataIndex(this._elementArrayStartIndex);
- var previousIndex = this._elementArrayEndIndex;
- var previousIndexStep = parseInt(exceededDistance / this.options.elementHeight);
-
- for( var i = 0 ; i < previousIndexStep ; i++ ) {
- var sourceIndex = sourceFirstIndex - (i + 1);
- if ( sourceIndex < 0 ) {
- break;
- }
- this._renderElement(this._elementArray[previousIndex], sourceIndex);
- previousIndex = this._getPreviousArrayIndex(previousIndex);
- }
- this._elementArrayEndIndex = previousIndex;
- this._elementArrayStartIndex = this._getNextArrayIndex(previousIndex);
- }
- };
- InfiniteCircularScroller.prototype.setSource = function( source ) {
- this._source= source;
- return this;
- };
- InfiniteCircularScroller.prototype.setViewAreaHeight = function( viewAreaHeight ) {
- this._viewAreaHeight = viewAreaHeight;
- return this;
- };
- InfiniteCircularScroller.prototype.setRenderFunc = function( fnRender ) {
- this._renderFunc = fnRender;
- return this;
- };
- InfiniteCircularScroller.prototype.reset = function() {
- this._initVar();
- this._calculateElementStartCount();
- this._initElementArray();
- return this;
- };
- InfiniteCircularScroller.prototype.getContentsAreaHeight = function() {
- return this.options.elementHeight * this._source.length;
- };
- InfiniteCircularScroller.prototype.destroy = function() {
- this.$wrapper.parent().off("scroll");
- };
- InfiniteCircularScroller.prototype.resize = function( resizedViewAreaHeight ) {
- var currentRow = parseInt(this.options.scroller.scrollTop() / this.options.elementHeight);
- this.setViewAreaHeight(resizedViewAreaHeight);
- this.reset();
- this.moveByRow( currentRow );
- };
- InfiniteCircularScroller.prototype.moveByRow = function( row ) {
- var self = this;
- setTimeout(function() {
- self.options.scroller.scrollTop(0);
- },0);
- };
- InfiniteCircularScroller.prototype.moveByValue = function(from, func) {
- for( var i = from ; i < this._source.length ; i++ ) {
- if ( func(this._source[i]) ) {
- this.moveByRow(i);
- break;
- }
- }
- };
- /*
- * icscroller.moveByValue( 0, function( elementData ) {
- * return (elementData[scope.key.end] - elementData[scope.key.begin]) >= selfExecutionTime;
- * });
- */
-
+
pinpointApp.directive('timelineDirective', function () {
return {
restrict: 'EA',
@@ -166,13 +9,13 @@
link: function postLink(scope, element, attrs) {
// define private variables of methods
- var initialize, getColorByString, filterCallStacks, viewAreaHeight, initBarCount, renderCallStack;
+ var initialize, getColorByString, filterCallStacks, viewAreaHeight, initBarCount, renderCallStack, searchTime, searchStartIndex, searchSuccess;
var colorSet = [
"#66CCFF", "#FFCCFF", "#66CC00", "#FFCC33", "#669999", "#FF9999", "#6666FF", "#FF6633", "#66FFCC", "#006666",
"#FFFF00", "#66CCCC", "#FFCCCC", "#6699FF", "#FF99FF", "#669900", "#FF9933", "#66FFFF", "#996600", "#66FF00"
], colorSetIndex = [];
- var icscroller = new InfiniteCircularScroller({
+ var icscroller = new InfiniteCircularScroll({
scroller: $(element),
wrapper: $(element).find("div"),
elementHeight: 21,
@@ -192,7 +35,8 @@
});
icscroller.setRenderFunc(function( $element, index, elementData ) {
var marginLeft = getMarginLeft(elementData);
- $element.find("div.clickable-bar").css({
+ $element[ index == this._selectedRow ? "addClass" : "removeClass" ]("timeline-bar-selected")
+ .find("div.clickable-bar").css({
width : getWidth(elementData) + "px",
backgroundColor : getColorByString(elementData[scope.key.applicationName]),
marginLeft : marginLeft + "px"
@@ -224,8 +68,10 @@
scope.newCallStacks = filterCallStacks();
icscroller.setSource( scope.newCallStacks )
.setViewAreaHeight( $(element).parentsUntil("div.wrapper").height() - 70 ) // 70 is header area height
+ .setSelectedRow(-1, angular.noop)
.reset();
scope.maxHeight = icscroller.getContentsAreaHeight();
+ searchStartIndex = 0;
scope.$digest();
};
@@ -276,6 +122,39 @@
scope.$on('timelineDirective.resize', function (event) {
icscroller.resize($(element).parentsUntil("div.wrapper").height() - 70);
});
+ scope.$on("timelineDirective.searchCall", function( event, time, index ) {
+ var resultIndex = searchTime( searchStartIndex, -1, time );
+ if ( resultIndex == -1 ) {
+ if ( searchStartIndex == 0 ) {
+ scope.$emit("transactionDetail.timelineSearchCallResult", "No call took longer than {time}ms." );
+ } else {
+ resultIndex = searchTime( 0, searchStartIndex, time );
+ if ( resultIndex == -1 ) {
+ scope.$emit("transactionDetail.timelineSearchCallResult", "No call took longer than {time}ms." );
+ } else {
+ searchSuccess(resultIndex, "Loop");
+ }
+ }
+ } else {
+ searchSuccess(resultIndex, "");
+ }
+ });
+ searchTime = function( from, to, time ) {
+ return icscroller.searchRow(from, to, function( elementData ) {
+ if ( elementData[scope.key.end] - elementData[scope.key.begin] >= time ) {
+ return true;
+ }
+ return false;
+ });
+ };
+ searchSuccess = function(resultIndex, message) {
+ icscroller.setSelectedRow( resultIndex, function(newIndex) {
+ this.$wrapper.find("div[data-index=" + this._selectedRow + "]").removeClass("timeline-bar-selected");
+ this.$wrapper.find("div[data-index=" + newIndex + "]").addClass("timeline-bar-selected");
+ }).moveByRow( resultIndex );
+ searchStartIndex = resultIndex + 1;
+ scope.$emit("transactionDetail.timelineSearchCallResult", message );
+ }
}
};
});
diff --git a/web/src/main/webapp/index.html b/web/src/main/webapp/index.html
index d4d8cb305..49f1d3320 100644
--- a/web/src/main/webapp/index.html
+++ b/web/src/main/webapp/index.html
@@ -95,6 +95,7 @@
+
diff --git a/web/src/main/webapp/pages/transactionDetail/transaction-detail.controller.js b/web/src/main/webapp/pages/transactionDetail/transaction-detail.controller.js
index f538d3d0f..4f57b7bd7 100644
--- a/web/src/main/webapp/pages/transactionDetail/transaction-detail.controller.js
+++ b/web/src/main/webapp/pages/transactionDetail/transaction-detail.controller.js
@@ -86,19 +86,26 @@
}
};
initSearchVar = function() {
- $("#traceTabs li:nth-child(5)").hide();
+// $("#traceTabs li:nth-child(5)").hide();
$scope.searchMinTime = 1000;
- $scope.searchIndex = 0;
+ $scope.timelineSearchIndex = 0;
+ $scope.calltreeSearchIndex = 0;
$scope.searchMessage = "";
};
- $scope.searchIndex = 0;
+ $scope.calltreeSearchIndex = 0;
+ $scope.timelineSearchIndex = 0;
$scope.searchMinTime = 1000; // ms
$scope.searchMessage = "";
$scope.searchCall = function() {
- $scope.$broadcast('distributedCallFlowDirective.searchCall.forTransactionDetail', parseInt($scope.searchMinTime), parseInt($scope.searchIndex) );
+ if ( $("#CallStacks").is(":visible") ) {
+ $scope.$broadcast('distributedCallFlowDirective.searchCall.forTransactionDetail', parseInt($scope.searchMinTime), parseInt($scope.calltreeSearchIndex) );
+ } else {
+ $scope.$broadcast('timelineDirective.searchCall', parseInt($scope.searchMinTime), parseInt($scope.timelineSearchIndex) );
+ }
};
$scope.$watch( "searchMinTime", function( newVal ) {
- $scope.searchIndex = 0;
+ $scope.calltreeSearchIndex = 0;
+ $scope.timelineSearchIndex = 0;
});
$scope.openInNewWindow = function () {
@@ -122,13 +129,23 @@
$("#traceTabs li:nth-child(1) a").trigger("click");
$scope.$broadcast('distributedCallFlowDirective.selectRow.forTransactionDetail', rowId);
});
- $scope.$on("transactionDetail.searchCallresult", function(event, message) {
+ $scope.$on("transactionDetail.calltreeSearchCallResult", function(event, message) {
if ( message == "Loop" ) {
- $scope.searchIndex = 1;
+ $scope.calltreeSearchIndex = 1;
} else {
$scope.searchMessage = message.replace("{time}", $scope.searchMinTime);
if ( message == "" ) {
- $scope.searchIndex++;
+ $scope.calltreeSearchIndex++;
+ }
+ }
+ });
+ $scope.$on("transactionDetail.timelineSearchCallResult", function(event, message) {
+ if ( message == "Loop" ) {
+ $scope.timelineSearchIndex = 1;
+ } else {
+ $scope.searchMessage = message.replace("{time}", $scope.searchMinTime);
+ if ( message == "" ) {
+ $scope.timelineSearchIndex++;
}
}
});
@@ -137,7 +154,7 @@
$('#traceTabs li a[data-toggle="tab"]').on('shown.bs.tab', function(e) {
if ( e.target.href.indexOf( "#CallStacks") != -1 ) {
$at($at.CALLSTACK, $at.CLK_DISTRIBUTED_CALL_FLOW);
- $("#traceTabs li:nth-child(5)").show();
+// $("#traceTabs li:nth-child(5)").show();
}
});
// events binding
diff --git a/web/src/main/webapp/pages/transactionDetail/transactionDetail.html b/web/src/main/webapp/pages/transactionDetail/transactionDetail.html
index e47e46411..c4732e3e7 100644
--- a/web/src/main/webapp/pages/transactionDetail/transactionDetail.html
+++ b/web/src/main/webapp/pages/transactionDetail/transactionDetail.html
@@ -27,6 +27,9 @@
border-bottom:1px solid #D3D3D3;
font-family:Verdana;
}
+ .timeline-bar-selected {
+ background-color:orange;
+ }
.timeline-bar > div {
cursor:pointer;
margin-top:1px;