blob: 2fa6d85c142e96b9e8d3f915f8e1ac3462a41241 [file] [log] [blame]
/*
* Copyright (C) 2013 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
WebInspector.TimelineRuler = function()
{
WebInspector.Object.call(this);
this._element = document.createElement("div");
this._element.className = WebInspector.TimelineRuler.StyleClassName;
this._headerElement = document.createElement("div");
this._headerElement.className = WebInspector.TimelineRuler.HeaderElementStyleClassName;
this._element.appendChild(this._headerElement);
this._markersElement = document.createElement("div");
this._markersElement.className = WebInspector.TimelineRuler.MarkersElementStyleClassName;
this._element.appendChild(this._markersElement);
this._zeroTime = 0;
this._startTime = 0;
this._endTime = 0;
this._duration = NaN;
this._secondsPerPixel = 0;
this._selectionStartTime = 0;
this._selectionEndTime = Infinity;
this._endTimePinned = false;
this._allowsClippedLabels = false;
this._allowsTimeRangeSelection = false;
this._markerElementMap = new Map;
}
WebInspector.TimelineRuler.MinimumLeftDividerSpacing = 48;
WebInspector.TimelineRuler.MinimumDividerSpacing = 64;
WebInspector.TimelineRuler.StyleClassName = "timeline-ruler";
WebInspector.TimelineRuler.AllowsTimeRangeSelectionStyleClassName = "allows-time-range-selection";
WebInspector.TimelineRuler.HeaderElementStyleClassName = "header";
WebInspector.TimelineRuler.DividerElementStyleClassName = "divider";
WebInspector.TimelineRuler.DividerLabelElementStyleClassName = "label";
WebInspector.TimelineRuler.MarkersElementStyleClassName = "markers";
WebInspector.TimelineRuler.BaseMarkerElementStyleClassName = "marker";
WebInspector.TimelineRuler.ShadedAreaElementStyleClassName = "shaded-area";
WebInspector.TimelineRuler.SelectionDragElementStyleClassName = "selection-drag";
WebInspector.TimelineRuler.SelectionHandleElementStyleClassName = "selection-handle";
WebInspector.TimelineRuler.LeftSelectionElementStyleClassName = "left";
WebInspector.TimelineRuler.RightSelectionElementStyleClassName = "right";
WebInspector.TimelineRuler.MinimumSelectionTimeRange = 0.01;
WebInspector.TimelineRuler.Event = {
TimeRangeSelectionChanged: "time-ruler-time-range-selection-changed"
};
WebInspector.TimelineRuler.prototype = {
constructor: WebInspector.TimelineRuler,
// Public
get element()
{
return this._element;
},
get allowsClippedLabels()
{
return this._allowsClippedLabels
},
set allowsClippedLabels(x)
{
if (this._allowsClippedLabels === x)
return;
this._allowsClippedLabels = x || false;
this._needsLayout();
},
get allowsTimeRangeSelection()
{
return this._allowsTimeRangeSelection;
},
set allowsTimeRangeSelection(x)
{
if (this._allowsTimeRangeSelection === x)
return;
this._allowsTimeRangeSelection = x || false;
if (x) {
this._mouseDownEventListener = this._handleMouseDown.bind(this);
this._element.addEventListener("mousedown", this._mouseDownEventListener);
this._leftShadedAreaElement = document.createElement("div");
this._leftShadedAreaElement.classList.add(WebInspector.TimelineRuler.ShadedAreaElementStyleClassName);
this._leftShadedAreaElement.classList.add(WebInspector.TimelineRuler.LeftSelectionElementStyleClassName);
this._rightShadedAreaElement = document.createElement("div");
this._rightShadedAreaElement.classList.add(WebInspector.TimelineRuler.ShadedAreaElementStyleClassName);
this._rightShadedAreaElement.classList.add(WebInspector.TimelineRuler.RightSelectionElementStyleClassName);
this._leftSelectionHandleElement = document.createElement("div");
this._leftSelectionHandleElement.classList.add(WebInspector.TimelineRuler.SelectionHandleElementStyleClassName);
this._leftSelectionHandleElement.classList.add(WebInspector.TimelineRuler.LeftSelectionElementStyleClassName);
this._leftSelectionHandleElement.addEventListener("mousedown", this._handleSelectionHandleMouseDown.bind(this));
this._rightSelectionHandleElement = document.createElement("div");
this._rightSelectionHandleElement.classList.add(WebInspector.TimelineRuler.SelectionHandleElementStyleClassName);
this._rightSelectionHandleElement.classList.add(WebInspector.TimelineRuler.RightSelectionElementStyleClassName);
this._rightSelectionHandleElement.addEventListener("mousedown", this._handleSelectionHandleMouseDown.bind(this));
this._selectionDragElement = document.createElement("div");
this._selectionDragElement.classList.add(WebInspector.TimelineRuler.SelectionDragElementStyleClassName);
this._needsSelectionLayout();
} else {
this._element.removeEventListener("mousedown", this._mouseDownEventListener);
delete this._mouseDownEventListener;
this._leftShadedAreaElement.remove();
this._rightShadedAreaElement.remove();
this._leftSelectionHandleElement.remove();
this._rightSelectionHandleElement.remove();
this._selectionDragElement.remove();
delete this._leftShadedAreaElement;
delete this._rightShadedAreaElement;
delete this._leftSelectionHandleElement;
delete this._rightSelectionHandleElement;
delete this._selectionDragElement;
}
},
get zeroTime()
{
return this._zeroTime;
},
set zeroTime(x)
{
if (this._zeroTime === x)
return;
this._zeroTime = x || 0;
this._needsLayout();
},
get startTime()
{
return this._startTime;
},
set startTime(x)
{
if (this._startTime === x)
return;
this._startTime = x || 0;
if (!isNaN(this._duration))
this._endTime = this._startTime + this._duration;
this._needsLayout();
},
get duration()
{
if (!isNaN(this._duration))
return this._duration;
return this.endTime - this.startTime;
},
set duration(x)
{
if (this._duration === x)
return;
this._duration = x || NaN;
if (!isNaN(this._duration)) {
this._endTime = this._startTime + this._duration;
this._endTimePinned = true;
} else
this._endTimePinned = false;
this._needsLayout();
},
get endTime()
{
if (!this._endTimePinned && this._scheduledLayoutUpdateIdentifier)
this._recalculate();
return this._endTime;
},
set endTime(x)
{
if (this._endTime === x)
return;
this._endTime = x || 0;
this._endTimePinned = true;
this._needsLayout();
},
get secondsPerPixel()
{
if (this._scheduledLayoutUpdateIdentifier)
this._recalculate();
return this._secondsPerPixel;
},
set secondsPerPixel(x)
{
if (this._secondsPerPixel === x)
return;
this._secondsPerPixel = x || 0;
this._endTimePinned = false;
this._currentSliceTime = 0;
this._needsLayout();
},
get selectionStartTime()
{
return this._selectionStartTime;
},
set selectionStartTime(x)
{
if (this._selectionStartTime === x)
return;
this._selectionStartTime = x || 0;
this._timeRangeSelectionChanged = true;
this._needsSelectionLayout();
},
get selectionEndTime()
{
return this._selectionEndTime;
},
set selectionEndTime(x)
{
if (this._selectionEndTime === x)
return;
this._selectionEndTime = x || 0;
this._timeRangeSelectionChanged = true;
this._needsSelectionLayout();
},
addMarker: function(marker)
{
console.assert(marker instanceof WebInspector.TimelineMarker);
if (this._markerElementMap.has(marker))
return;
marker.addEventListener(WebInspector.TimelineMarker.Event.TimeChanged, this._timelineMarkerTimeChanged, this);
var markerElement = document.createElement("div");
markerElement.classList.add(WebInspector.TimelineRuler.BaseMarkerElementStyleClassName);
markerElement.classList.add(marker.type);
this._markerElementMap.set(marker, markerElement);
this._needsMarkerLayout();
},
elementForMarker: function(marker)
{
return this._markerElementMap.get(marker) || null;
},
updateLayout: function()
{
if (this._scheduledLayoutUpdateIdentifier) {
cancelAnimationFrame(this._scheduledLayoutUpdateIdentifier);
delete this._scheduledLayoutUpdateIdentifier;
}
var visibleWidth = this._recalculate();
if (visibleWidth <= 0)
return;
var duration = this.duration;
var pixelsPerSecond = visibleWidth / duration;
// Calculate a divider count based on the maximum allowed divider density.
var dividerCount = Math.round(visibleWidth / WebInspector.TimelineRuler.MinimumDividerSpacing);
if (this._endTimePinned || !this._currentSliceTime) {
// Calculate the slice time based on the rough divider count and the time span.
var sliceTime = duration / dividerCount;
// Snap the slice time to a nearest number (e.g. 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, etc.)
sliceTime = Math.pow(10, Math.ceil(Math.log(sliceTime) / Math.LN10));
if (sliceTime * pixelsPerSecond >= 5 * WebInspector.TimelineRuler.MinimumDividerSpacing)
sliceTime = sliceTime / 5;
if (sliceTime * pixelsPerSecond >= 2 * WebInspector.TimelineRuler.MinimumDividerSpacing)
sliceTime = sliceTime / 2;
this._currentSliceTime = sliceTime;
} else {
// Reuse the last slice time since the time duration does not scale to fit when the end time isn't pinned.
var sliceTime = this._currentSliceTime;
}
var firstDividerTime = (Math.ceil((this._startTime - this._zeroTime) / sliceTime) * sliceTime) + this._zeroTime;
var lastDividerTime = this._endTime;
// Calculate the divider count now based on the final slice time.
dividerCount = Math.ceil((lastDividerTime - firstDividerTime) / sliceTime);
// Make an extra divider in case the last one is partially visible.
if (!this._endTimePinned)
++dividerCount;
var markerDividers = this._markersElement.querySelectorAll("." + WebInspector.TimelineRuler.DividerElementStyleClassName);
var dividerElement = this._headerElement.firstChild;
for (var i = 0; i <= dividerCount; ++i) {
if (!dividerElement) {
dividerElement = document.createElement("div");
dividerElement.className = WebInspector.TimelineRuler.DividerElementStyleClassName;
this._headerElement.appendChild(dividerElement);
var labelElement = document.createElement("div");
labelElement.className = WebInspector.TimelineRuler.DividerLabelElementStyleClassName;
dividerElement._labelElement = labelElement;
dividerElement.appendChild(labelElement);
}
var markerDividerElement = markerDividers[i];
if (!markerDividerElement) {
markerDividerElement = document.createElement("div");
markerDividerElement.className = WebInspector.TimelineRuler.DividerElementStyleClassName;
this._markersElement.appendChild(markerDividerElement);
}
var dividerTime = firstDividerTime + (sliceTime * i);
var newLeftPosition = (dividerTime - this._startTime) / duration;
if (!this._allowsClippedLabels) {
// Don't allow dividers under 0% where they will be completely hidden.
if (newLeftPosition < 0)
continue;
// When over 100% it is time to stop making/updating dividers.
if (newLeftPosition > 1)
break;
// Don't allow the left-most divider spacing to be so tight it clips.
if ((newLeftPosition * visibleWidth) < WebInspector.TimelineRuler.MinimumLeftDividerSpacing)
continue;
}
this._updatePositionOfElement(dividerElement, newLeftPosition, visibleWidth);
this._updatePositionOfElement(markerDividerElement, newLeftPosition, visibleWidth);
dividerElement._labelElement.textContent = isNaN(dividerTime) ? "" : Number.secondsToString(dividerTime - this._zeroTime, true);
dividerElement = dividerElement.nextSibling;
}
// Remove extra dividers.
while (dividerElement) {
var nextDividerElement = dividerElement.nextSibling;
dividerElement.remove();
dividerElement = nextDividerElement;
}
for (; i < markerDividers.length; ++i)
markerDividers[i].remove();
this._updateMarkers(visibleWidth, duration);
this._updateSelection(visibleWidth, duration);
},
updateLayoutIfNeeded: function()
{
// If there is a main layout scheduled we can just update layout and return, since that
// will update markers and the selection at the same time.
if (this._scheduledLayoutUpdateIdentifier) {
this.updateLayout();
return;
}
var visibleWidth = this._element.clientWidth;
if (visibleWidth <= 0)
return;
if (this._scheduledMarkerLayoutUpdateIdentifier)
this._updateMarkers(visibleWidth, this.duration);
if (this._scheduledSelectionLayoutUpdateIdentifier)
this._updateSelection(visibleWidth, this.duration);
},
// Private
_needsLayout: function()
{
if (this._scheduledLayoutUpdateIdentifier)
return;
if (this._scheduledMarkerLayoutUpdateIdentifier) {
cancelAnimationFrame(this._scheduledMarkerLayoutUpdateIdentifier);
delete this._scheduledMarkerLayoutUpdateIdentifier;
}
if (this._scheduledSelectionLayoutUpdateIdentifier) {
cancelAnimationFrame(this._scheduledSelectionLayoutUpdateIdentifier);
delete this._scheduledSelectionLayoutUpdateIdentifier;
}
this._scheduledLayoutUpdateIdentifier = requestAnimationFrame(this.updateLayout.bind(this));
},
_needsMarkerLayout: function()
{
// If layout is scheduled, abort since markers will be updated when layout happens.
if (this._scheduledLayoutUpdateIdentifier)
return;
if (this._scheduledMarkerLayoutUpdateIdentifier)
return;
function update()
{
delete this._scheduledMarkerLayoutUpdateIdentifier;
var visibleWidth = this._element.clientWidth;
if (visibleWidth <= 0)
return;
this._updateMarkers(visibleWidth, this.duration);
}
this._scheduledMarkerLayoutUpdateIdentifier = requestAnimationFrame(update.bind(this));
},
_needsSelectionLayout: function()
{
if (!this._allowsTimeRangeSelection)
return;
// If layout is scheduled, abort since the selection will be updated when layout happens.
if (this._scheduledLayoutUpdateIdentifier)
return;
if (this._scheduledSelectionLayoutUpdateIdentifier)
return;
function update()
{
delete this._scheduledSelectionLayoutUpdateIdentifier;
var visibleWidth = this._element.clientWidth;
if (visibleWidth <= 0)
return;
this._updateSelection(visibleWidth, this.duration);
}
this._scheduledSelectionLayoutUpdateIdentifier = requestAnimationFrame(update.bind(this));
},
_recalculate: function()
{
var visibleWidth = this._element.clientWidth;
if (visibleWidth <= 0)
return 0;
if (this._endTimePinned)
var duration = this._endTime - this._startTime;
else
var duration = visibleWidth * this._secondsPerPixel;
this._secondsPerPixel = duration / visibleWidth;
if (!this._endTimePinned)
this._endTime = this._startTime + (visibleWidth * this._secondsPerPixel);
return visibleWidth;
},
_updatePositionOfElement: function(element, newPosition, visibleWidth, property)
{
property = property || "left";
newPosition *= this._endTimePinned ? 100 : visibleWidth;
newPosition = newPosition.toFixed(2);
var currentPosition = parseFloat(element.style[property]).toFixed(2);
if (currentPosition !== newPosition)
element.style[property] = newPosition + (this._endTimePinned ? "%" : "px");
},
_updateMarkers: function(visibleWidth, duration)
{
if (this._scheduledMarkerLayoutUpdateIdentifier) {
cancelAnimationFrame(this._scheduledMarkerLayoutUpdateIdentifier);
delete this._scheduledMarkerLayoutUpdateIdentifier;
}
this._markerElementMap.forEach(function(markerElement, marker) {
var newLeftPosition = (marker.time - this._startTime) / duration;
this._updatePositionOfElement(markerElement, newLeftPosition, visibleWidth);
if (!markerElement.parentNode)
this._markersElement.appendChild(markerElement);
}, this);
},
_updateSelection: function(visibleWidth, duration)
{
if (this._scheduledSelectionLayoutUpdateIdentifier) {
cancelAnimationFrame(this._scheduledSelectionLayoutUpdateIdentifier);
delete this._scheduledSelectionLayoutUpdateIdentifier;
}
this._element.classList.toggle(WebInspector.TimelineRuler.AllowsTimeRangeSelectionStyleClassName, this._allowsTimeRangeSelection);
if (!this._allowsTimeRangeSelection)
return;
var newLeftPosition = Math.max(0, (this._selectionStartTime - this._startTime) / duration);
this._updatePositionOfElement(this._leftShadedAreaElement, newLeftPosition, visibleWidth, "width");
this._updatePositionOfElement(this._leftSelectionHandleElement, newLeftPosition, visibleWidth, "left");
this._updatePositionOfElement(this._selectionDragElement, newLeftPosition, visibleWidth, "left");
var newRightPosition = 1 - Math.min((this._selectionEndTime - this._startTime) / duration, 1);
this._updatePositionOfElement(this._rightShadedAreaElement, newRightPosition, visibleWidth, "width");
this._updatePositionOfElement(this._rightSelectionHandleElement, newRightPosition, visibleWidth, "right");
this._updatePositionOfElement(this._selectionDragElement, newRightPosition, visibleWidth, "right");
if (!this._selectionDragElement.parentNode) {
this._element.appendChild(this._selectionDragElement);
this._element.appendChild(this._leftShadedAreaElement);
this._element.appendChild(this._leftSelectionHandleElement);
this._element.appendChild(this._rightShadedAreaElement);
this._element.appendChild(this._rightSelectionHandleElement);
}
if (this._timeRangeSelectionChanged)
this._dispatchTimeRangeSelectionChangedEvent();
},
_dispatchTimeRangeSelectionChangedEvent: function()
{
delete this._timeRangeSelectionChanged;
if (this._suppressTimeRangeSelectionChangedEvent)
return;
this.dispatchEventToListeners(WebInspector.TimelineRuler.Event.TimeRangeSelectionChanged);
},
_timelineMarkerTimeChanged: function()
{
this._needsMarkerLayout();
},
_handleMouseDown: function(event)
{
// Only handle left mouse clicks.
if (event.button !== 0 || event.ctrlKey)
return;
this._selectionIsMove = event.target === this._selectionDragElement;
this._suppressTimeRangeSelectionChangedEvent = !this._selectionIsMove;
if (this._selectionIsMove)
this._lastMousePosition = event.pageX;
else
this._mouseDownPosition = event.pageX - this._element.totalOffsetLeft;
this._mouseMoveEventListener = this._handleMouseMove.bind(this);
this._mouseUpEventListener = this._handleMouseUp.bind(this);
// Register these listeners on the document so we can track the mouse if it leaves the ruler.
document.addEventListener("mousemove", this._mouseMoveEventListener);
document.addEventListener("mouseup", this._mouseUpEventListener);
event.preventDefault();
event.stopPropagation();
},
_handleMouseMove: function(event)
{
console.assert(event.button === 0);
if (this._selectionIsMove) {
var currentMousePosition = event.pageX;
var offsetTime = (currentMousePosition - this._lastMousePosition) * this.secondsPerPixel;
var selectionDuration = this.selectionEndTime - this.selectionStartTime;
this.selectionStartTime = Math.max(this.startTime, Math.min(this.selectionStartTime + offsetTime, this.endTime - selectionDuration));
this.selectionEndTime = this.selectionStartTime + selectionDuration;
this._lastMousePosition = currentMousePosition;
} else {
var currentMousePosition = event.pageX - this._element.totalOffsetLeft;
this.selectionStartTime = Math.max(this.startTime, this.startTime + (Math.min(currentMousePosition, this._mouseDownPosition) * this.secondsPerPixel));
this.selectionEndTime = Math.min(this.startTime + (Math.max(currentMousePosition, this._mouseDownPosition) * this.secondsPerPixel), this.endTime);
}
this._updateSelection(this._element.clientWidth, this.duration);
event.preventDefault();
event.stopPropagation();
},
_handleMouseUp: function(event)
{
console.assert(event.button === 0);
if (!this._selectionIsMove && this.selectionEndTime - this.selectionStartTime < WebInspector.TimelineRuler.MinimumSelectionTimeRange) {
// The section is smaller than allowed, grow in the direction of the drag to meet the minumum.
var currentMousePosition = event.pageX - this._element.totalOffsetLeft;
if (currentMousePosition > this._mouseDownPosition) {
this.selectionEndTime = Math.min(this.selectionStartTime + WebInspector.TimelineRuler.MinimumSelectionTimeRange, this.endTime);
this.selectionStartTime = this.selectionEndTime - WebInspector.TimelineRuler.MinimumSelectionTimeRange;
} else {
this.selectionStartTime = Math.max(this.startTime, this.selectionEndTime - WebInspector.TimelineRuler.MinimumSelectionTimeRange);
this.selectionEndTime = this.selectionStartTime + WebInspector.TimelineRuler.MinimumSelectionTimeRange
}
}
delete this._suppressTimeRangeSelectionChangedEvent;
this._dispatchTimeRangeSelectionChangedEvent();
document.removeEventListener("mousemove", this._mouseMoveEventListener);
document.removeEventListener("mouseup", this._mouseUpEventListener);
delete this._mouseMovedEventListener;
delete this._mouseUpEventListener;
delete this._mouseDownPosition;
delete this._lastMousePosition;
delete this._selectionIsMove;
event.preventDefault();
event.stopPropagation();
},
_handleSelectionHandleMouseDown: function(event)
{
// Only handle left mouse clicks.
if (event.button !== 0 || event.ctrlKey)
return;
this._dragHandleIsStartTime = event.target === this._leftSelectionHandleElement;
this._mouseDownPosition = event.pageX - this._element.totalOffsetLeft;
this._selectionHandleMouseMoveEventListener = this._handleSelectionHandleMouseMove.bind(this);
this._selectionHandleMouseUpEventListener = this._handleSelectionHandleMouseUp.bind(this);
// Register these listeners on the document so we can track the mouse if it leaves the ruler.
document.addEventListener("mousemove", this._selectionHandleMouseMoveEventListener);
document.addEventListener("mouseup", this._selectionHandleMouseUpEventListener);
event.preventDefault();
event.stopPropagation();
},
_handleSelectionHandleMouseMove: function(event)
{
console.assert(event.button === 0);
var currentMousePosition = event.pageX - this._element.totalOffsetLeft;
var currentTime = this.startTime + (currentMousePosition * this.secondsPerPixel);
if (event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey) {
// Resize the selection on both sides when the Option keys is held down.
if (this._dragHandleIsStartTime) {
var timeDifference = currentTime - this.selectionStartTime;
this.selectionStartTime = Math.max(this.startTime, Math.min(currentTime, this.selectionEndTime - WebInspector.TimelineRuler.MinimumSelectionTimeRange));
this.selectionEndTime = Math.min(Math.max(this.selectionStartTime + WebInspector.TimelineRuler.MinimumSelectionTimeRange, this.selectionEndTime - timeDifference), this.endTime);
} else {
var timeDifference = currentTime - this.selectionEndTime;
this.selectionEndTime = Math.min(Math.max(this.selectionStartTime + WebInspector.TimelineRuler.MinimumSelectionTimeRange, currentTime), this.endTime);
this.selectionStartTime = Math.max(this.startTime, Math.min(this.selectionStartTime - timeDifference, this.selectionEndTime - WebInspector.TimelineRuler.MinimumSelectionTimeRange));
}
} else {
// Resize the selection on side being dragged.
if (this._dragHandleIsStartTime)
this.selectionStartTime = Math.max(this.startTime, Math.min(currentTime, this.selectionEndTime - WebInspector.TimelineRuler.MinimumSelectionTimeRange));
else
this.selectionEndTime = Math.min(Math.max(this.selectionStartTime + WebInspector.TimelineRuler.MinimumSelectionTimeRange, currentTime), this.endTime);
}
this._updateSelection(this._element.clientWidth, this.duration);
event.preventDefault();
event.stopPropagation();
},
_handleSelectionHandleMouseUp: function(event)
{
console.assert(event.button === 0);
document.removeEventListener("mousemove", this._selectionHandleMouseMoveEventListener);
document.removeEventListener("mouseup", this._selectionHandleMouseUpEventListener);
delete this._selectionHandleMouseMoveEventListener;
delete this._selectionHandleMouseUpEventListener;
delete this._dragHandleIsStartTime;
delete this._mouseDownPosition;
event.preventDefault();
event.stopPropagation();
}
}
WebInspector.TimelineRuler.prototype.__proto__ = WebInspector.Object.prototype;