1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 * @extends {WebInspector.HBox}
9 WebInspector
.FilmStripView = function()
11 WebInspector
.HBox
.call(this, true);
12 this.registerRequiredCSS("components_lazy/filmStripView.css");
13 this.contentElement
.classList
.add("film-strip-view");
14 this._statusLabel
= this.contentElement
.createChild("div", "label");
16 this.setMode(WebInspector
.FilmStripView
.Modes
.TimeBased
);
19 WebInspector
.FilmStripView
.Events
= {
20 FrameSelected
: "FrameSelected",
21 FrameEnter
: "FrameEnter",
22 FrameExit
: "FrameExit",
25 WebInspector
.FilmStripView
.Modes
= {
26 TimeBased
: "TimeBased",
27 FrameBased
: "FrameBased"
30 WebInspector
.FilmStripView
.prototype = {
32 * @param {string} mode
34 setMode: function(mode
)
37 this.contentElement
.classList
.toggle("time-based", mode
=== WebInspector
.FilmStripView
.Modes
.TimeBased
);
42 * @param {!WebInspector.FilmStripModel} filmStripModel
43 * @param {number} zeroTime
44 * @param {number} spanTime
46 setModel: function(filmStripModel
, zeroTime
, spanTime
)
48 this._model
= filmStripModel
;
49 this._zeroTime
= zeroTime
;
50 this._spanTime
= spanTime
;
51 var frames
= filmStripModel
.frames();
60 * @param {!WebInspector.FilmStripModel.Frame} frame
61 * @return {!Promise<!Element>}
63 createFrameElement: function(frame
)
65 var time
= frame
.timestamp
;
66 var element
= createElementWithClass("div", "frame");
67 element
.title
= WebInspector
.UIString("Doubleclick to zoom image. Click to view preceding requests.");
68 element
.createChild("div", "time").textContent
= Number
.millisToString(time
- this._zeroTime
);
69 var imageElement
= element
.createChild("div", "thumbnail").createChild("img");
70 element
.addEventListener("mousedown", this._onMouseEvent
.bind(this, WebInspector
.FilmStripView
.Events
.FrameSelected
, time
), false);
71 element
.addEventListener("mouseenter", this._onMouseEvent
.bind(this, WebInspector
.FilmStripView
.Events
.FrameEnter
, time
), false);
72 element
.addEventListener("mouseout", this._onMouseEvent
.bind(this, WebInspector
.FilmStripView
.Events
.FrameExit
, time
), false);
73 element
.addEventListener("dblclick", this._onDoubleClick
.bind(this, frame
), false);
75 return frame
.imageDataPromise().then(WebInspector
.FilmStripView
._setImageData
.bind(null, imageElement
)).then(returnElement
);
79 function returnElement()
86 * @param {number} time
87 * @return {!WebInspector.FilmStripModel.Frame}
89 frameByTime: function(time
)
92 * @param {number} time
93 * @param {!WebInspector.FilmStripModel.Frame} frame
96 function comparator(time
, frame
)
98 return time
- frame
.timestamp
;
100 // Using the first frame to fill the interval between recording start
101 // and a moment the frame is taken.
102 var frames
= this._model
.frames();
103 var index
= Math
.max(frames
.upperBound(time
, comparator
) - 1, 0);
104 return frames
[index
];
111 var frames
= this._model
.frames();
115 if (this._mode
=== WebInspector
.FilmStripView
.Modes
.FrameBased
) {
116 Promise
.all(frames
.map(this.createFrameElement
.bind(this))).then(appendElements
.bind(this));
120 var width
= this.contentElement
.clientWidth
;
121 var scale
= this._spanTime
/ width
;
122 this.createFrameElement(frames
[0]).then(continueWhenFrameImageLoaded
.bind(this)); // Calculate frame width basing on the first frame.
125 * @this {WebInspector.FilmStripView}
126 * @param {!Element} element0
128 function continueWhenFrameImageLoaded(element0
)
130 var frameWidth
= Math
.ceil(WebInspector
.measurePreferredSize(element0
, this.contentElement
).width
);
135 for (var pos
= frameWidth
; pos
< width
; pos
+= frameWidth
) {
136 var time
= pos
* scale
+ this._zeroTime
;
137 promises
.push(this.createFrameElement(this.frameByTime(time
)).then(fixWidth
));
139 Promise
.all(promises
).then(appendElements
.bind(this));
141 * @param {!Element} element
144 function fixWidth(element
)
146 element
.style
.width
= frameWidth
+ "px";
152 * @param {!Array.<!Element>} elements
153 * @this {WebInspector.FilmStripView}
155 function appendElements(elements
)
157 this.contentElement
.removeChildren();
158 for (var i
= 0; i
< elements
.length
; ++i
)
159 this.contentElement
.appendChild(elements
[i
]);
168 if (this._mode
=== WebInspector
.FilmStripView
.Modes
.FrameBased
)
174 * @param {string} eventName
175 * @param {number} timestamp
177 _onMouseEvent: function(eventName
, timestamp
)
179 this.dispatchEventToListeners(eventName
, timestamp
);
183 * @param {!WebInspector.FilmStripModel.Frame} filmStripFrame
185 _onDoubleClick: function(filmStripFrame
)
187 new WebInspector
.FilmStripView
.DialogDelegate(filmStripFrame
, this._zeroTime
);
193 this.contentElement
.removeChildren();
194 this.contentElement
.appendChild(this._statusLabel
);
198 * @param {string} text
200 setStatusText: function(text
)
202 this._statusLabel
.textContent
= text
;
205 __proto__
: WebInspector
.HBox
.prototype
209 * @param {!Element} imageElement
210 * @param {?string} data
212 WebInspector
.FilmStripView
._setImageData = function(imageElement
, data
)
215 imageElement
.src
= "data:image/jpg;base64," + data
;
220 * @extends {WebInspector.DialogDelegate}
221 * @param {!WebInspector.FilmStripModel.Frame} filmStripFrame
222 * @param {number=} zeroTime
224 WebInspector
.FilmStripView
.DialogDelegate = function(filmStripFrame
, zeroTime
)
226 WebInspector
.DialogDelegate
.call(this);
227 var shadowRoot
= WebInspector
.createShadowRootWithCoreStyles(this.element
);
228 shadowRoot
.appendChild(WebInspector
.Widget
.createStyleElement("components_lazy/filmStripDialog.css"));
229 this._contentElement
= shadowRoot
.createChild("div", "filmstrip-dialog");
230 this._contentElement
.tabIndex
= 0;
232 this._frames
= filmStripFrame
.model().frames();
233 this._index
= filmStripFrame
.index
;
234 this._zeroTime
= zeroTime
|| filmStripFrame
.model().zeroTime();
236 this._imageElement
= this._contentElement
.createChild("img");
237 var footerElement
= this._contentElement
.createChild("div", "filmstrip-dialog-footer");
238 footerElement
.createChild("div", "flex-auto");
239 var prevButton
= createTextButton("\u25C0", this._onPrevFrame
.bind(this), undefined, WebInspector
.UIString("Previous frame"));
240 footerElement
.appendChild(prevButton
);
241 this._timeLabel
= footerElement
.createChild("div", "filmstrip-dialog-label");
242 var nextButton
= createTextButton("\u25B6", this._onNextFrame
.bind(this), undefined, WebInspector
.UIString("Next frame"));
243 footerElement
.appendChild(nextButton
);
244 footerElement
.createChild("div", "flex-auto");
246 this._contentElement
.addEventListener("keydown", this._keyDown
.bind(this), false);
247 this._render().then(WebInspector
.Dialog
.show
.bind(null, null, this));
250 WebInspector
.FilmStripView
.DialogDelegate
.prototype = {
256 this._contentElement
.focus();
260 * @param {!Event} event
262 _keyDown: function(event
)
264 switch (event
.keyIdentifier
) {
266 if (WebInspector
.isMac() && event
.metaKey
)
267 this._onFirstFrame();
273 if (WebInspector
.isMac() && event
.metaKey
)
280 this._onFirstFrame();
289 _onPrevFrame: function()
296 _onNextFrame: function()
298 if (this._index
< this._frames
.length
- 1)
303 _onFirstFrame: function()
309 _onLastFrame: function()
311 this._index
= this._frames
.length
- 1;
316 * @return {!Promise<undefined>}
320 var frame
= this._frames
[this._index
];
321 this._timeLabel
.textContent
= Number
.millisToString(frame
.timestamp
- this._zeroTime
);
322 return frame
.imageDataPromise().then(WebInspector
.FilmStripView
._setImageData
.bind(null, this._imageElement
));
325 __proto__
: WebInspector
.DialogDelegate
.prototype