| /* |
| * 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; |