| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | |
| 5 /** | 4 /** |
| 6 * @constructor | |
| 7 * @extends {WebInspector.Widget} | |
| 8 * @implements {WebInspector.TargetManager.Observer} | 5 * @implements {WebInspector.TargetManager.Observer} |
| 9 * @param {function():number} getWidthCallback | 6 * @unrestricted |
| 10 * @param {function(number)} setWidthCallback | |
| 11 */ | 7 */ |
| 12 WebInspector.MediaQueryInspector = function(getWidthCallback, setWidthCallback) | 8 WebInspector.MediaQueryInspector = class extends WebInspector.Widget { |
| 13 { | 9 /** |
| 14 WebInspector.Widget.call(this, true); | 10 * @param {function():number} getWidthCallback |
| 15 this.registerRequiredCSS("emulation/mediaQueryInspector.css"); | 11 * @param {function(number)} setWidthCallback |
| 16 this.contentElement.classList.add("media-inspector-view"); | 12 */ |
| 17 this.contentElement.addEventListener("click", this._onMediaQueryClicked.bind
(this), false); | 13 constructor(getWidthCallback, setWidthCallback) { |
| 18 this.contentElement.addEventListener("contextmenu", this._onContextMenu.bind
(this), false); | 14 super(true); |
| 15 this.registerRequiredCSS('emulation/mediaQueryInspector.css'); |
| 16 this.contentElement.classList.add('media-inspector-view'); |
| 17 this.contentElement.addEventListener('click', this._onMediaQueryClicked.bind
(this), false); |
| 18 this.contentElement.addEventListener('contextmenu', this._onContextMenu.bind
(this), false); |
| 19 this._mediaThrottler = new WebInspector.Throttler(0); | 19 this._mediaThrottler = new WebInspector.Throttler(0); |
| 20 | 20 |
| 21 this._getWidthCallback = getWidthCallback; | 21 this._getWidthCallback = getWidthCallback; |
| 22 this._setWidthCallback = setWidthCallback; | 22 this._setWidthCallback = setWidthCallback; |
| 23 this._scale = 1; | 23 this._scale = 1; |
| 24 | 24 |
| 25 WebInspector.targetManager.observeTargets(this); | 25 WebInspector.targetManager.observeTargets(this); |
| 26 WebInspector.zoomManager.addEventListener(WebInspector.ZoomManager.Events.Zo
omChanged, this._renderMediaQueries.bind(this), this); | 26 WebInspector.zoomManager.addEventListener( |
| 27 WebInspector.ZoomManager.Events.ZoomChanged, this._renderMediaQueries.bi
nd(this), this); |
| 28 } |
| 29 |
| 30 /** |
| 31 * @override |
| 32 * @param {!WebInspector.Target} target |
| 33 */ |
| 34 targetAdded(target) { |
| 35 // FIXME: adapt this to multiple targets. |
| 36 if (this._cssModel) |
| 37 return; |
| 38 this._cssModel = WebInspector.CSSModel.fromTarget(target); |
| 39 if (!this._cssModel) |
| 40 return; |
| 41 this._cssModel.addEventListener( |
| 42 WebInspector.CSSModel.Events.StyleSheetAdded, this._scheduleMediaQueries
Update, this); |
| 43 this._cssModel.addEventListener( |
| 44 WebInspector.CSSModel.Events.StyleSheetRemoved, this._scheduleMediaQueri
esUpdate, this); |
| 45 this._cssModel.addEventListener( |
| 46 WebInspector.CSSModel.Events.StyleSheetChanged, this._scheduleMediaQueri
esUpdate, this); |
| 47 this._cssModel.addEventListener( |
| 48 WebInspector.CSSModel.Events.MediaQueryResultChanged, this._scheduleMedi
aQueriesUpdate, this); |
| 49 } |
| 50 |
| 51 /** |
| 52 * @override |
| 53 * @param {!WebInspector.Target} target |
| 54 */ |
| 55 targetRemoved(target) { |
| 56 if (WebInspector.CSSModel.fromTarget(target) !== this._cssModel) |
| 57 return; |
| 58 this._cssModel.removeEventListener( |
| 59 WebInspector.CSSModel.Events.StyleSheetAdded, this._scheduleMediaQueries
Update, this); |
| 60 this._cssModel.removeEventListener( |
| 61 WebInspector.CSSModel.Events.StyleSheetRemoved, this._scheduleMediaQueri
esUpdate, this); |
| 62 this._cssModel.removeEventListener( |
| 63 WebInspector.CSSModel.Events.StyleSheetChanged, this._scheduleMediaQueri
esUpdate, this); |
| 64 this._cssModel.removeEventListener( |
| 65 WebInspector.CSSModel.Events.MediaQueryResultChanged, this._scheduleMedi
aQueriesUpdate, this); |
| 66 delete this._cssModel; |
| 67 } |
| 68 |
| 69 /** |
| 70 * @param {number} scale |
| 71 */ |
| 72 setAxisTransform(scale) { |
| 73 if (Math.abs(this._scale - scale) < 1e-8) |
| 74 return; |
| 75 this._scale = scale; |
| 76 this._renderMediaQueries(); |
| 77 } |
| 78 |
| 79 /** |
| 80 * @param {!Event} event |
| 81 */ |
| 82 _onMediaQueryClicked(event) { |
| 83 var mediaQueryMarker = event.target.enclosingNodeOrSelfWithClass('media-insp
ector-bar'); |
| 84 if (!mediaQueryMarker) |
| 85 return; |
| 86 |
| 87 var model = mediaQueryMarker._model; |
| 88 if (model.section() === WebInspector.MediaQueryInspector.Section.Max) { |
| 89 this._setWidthCallback(model.maxWidthExpression().computedLength()); |
| 90 return; |
| 91 } |
| 92 if (model.section() === WebInspector.MediaQueryInspector.Section.Min) { |
| 93 this._setWidthCallback(model.minWidthExpression().computedLength()); |
| 94 return; |
| 95 } |
| 96 var currentWidth = this._getWidthCallback(); |
| 97 if (currentWidth !== model.minWidthExpression().computedLength()) |
| 98 this._setWidthCallback(model.minWidthExpression().computedLength()); |
| 99 else |
| 100 this._setWidthCallback(model.maxWidthExpression().computedLength()); |
| 101 } |
| 102 |
| 103 /** |
| 104 * @param {!Event} event |
| 105 */ |
| 106 _onContextMenu(event) { |
| 107 if (!this._cssModel || !this._cssModel.isEnabled()) |
| 108 return; |
| 109 |
| 110 var mediaQueryMarker = event.target.enclosingNodeOrSelfWithClass('media-insp
ector-bar'); |
| 111 if (!mediaQueryMarker) |
| 112 return; |
| 113 |
| 114 var locations = mediaQueryMarker._locations; |
| 115 var uiLocations = new Map(); |
| 116 for (var i = 0; i < locations.length; ++i) { |
| 117 var uiLocation = WebInspector.cssWorkspaceBinding.rawLocationToUILocation(
locations[i]); |
| 118 if (!uiLocation) |
| 119 continue; |
| 120 var descriptor = String.sprintf( |
| 121 '%s:%d:%d', uiLocation.uiSourceCode.url(), uiLocation.lineNumber + 1,
uiLocation.columnNumber + 1); |
| 122 uiLocations.set(descriptor, uiLocation); |
| 123 } |
| 124 |
| 125 var contextMenuItems = uiLocations.keysArray().sort(); |
| 126 var contextMenu = new WebInspector.ContextMenu(event); |
| 127 var subMenuItem = contextMenu.appendSubMenuItem(WebInspector.UIString.capita
lize('Reveal in ^source ^code')); |
| 128 for (var i = 0; i < contextMenuItems.length; ++i) { |
| 129 var title = contextMenuItems[i]; |
| 130 subMenuItem.appendItem( |
| 131 title, |
| 132 this._revealSourceLocation.bind(this, /** @type {!WebInspector.UILocat
ion} */ (uiLocations.get(title)))); |
| 133 } |
| 134 contextMenu.show(); |
| 135 } |
| 136 |
| 137 /** |
| 138 * @param {!WebInspector.UILocation} location |
| 139 */ |
| 140 _revealSourceLocation(location) { |
| 141 WebInspector.Revealer.reveal(location); |
| 142 } |
| 143 |
| 144 _scheduleMediaQueriesUpdate() { |
| 145 if (!this.isShowing()) |
| 146 return; |
| 147 this._mediaThrottler.schedule(this._refetchMediaQueries.bind(this)); |
| 148 } |
| 149 |
| 150 _refetchMediaQueries() { |
| 151 if (!this.isShowing() || !this._cssModel) |
| 152 return Promise.resolve(); |
| 153 |
| 154 return this._cssModel.mediaQueriesPromise().then(this._rebuildMediaQueries.b
ind(this)); |
| 155 } |
| 156 |
| 157 /** |
| 158 * @param {!Array.<!WebInspector.MediaQueryInspector.MediaQueryUIModel>} model
s |
| 159 * @return {!Array.<!WebInspector.MediaQueryInspector.MediaQueryUIModel>} |
| 160 */ |
| 161 _squashAdjacentEqual(models) { |
| 162 var filtered = []; |
| 163 for (var i = 0; i < models.length; ++i) { |
| 164 var last = filtered.peekLast(); |
| 165 if (!last || !last.equals(models[i])) |
| 166 filtered.push(models[i]); |
| 167 } |
| 168 return filtered; |
| 169 } |
| 170 |
| 171 /** |
| 172 * @param {!Array.<!WebInspector.CSSMedia>} cssMedias |
| 173 */ |
| 174 _rebuildMediaQueries(cssMedias) { |
| 175 var queryModels = []; |
| 176 for (var i = 0; i < cssMedias.length; ++i) { |
| 177 var cssMedia = cssMedias[i]; |
| 178 if (!cssMedia.mediaList) |
| 179 continue; |
| 180 for (var j = 0; j < cssMedia.mediaList.length; ++j) { |
| 181 var mediaQuery = cssMedia.mediaList[j]; |
| 182 var queryModel = WebInspector.MediaQueryInspector.MediaQueryUIModel.crea
teFromMediaQuery(cssMedia, mediaQuery); |
| 183 if (queryModel && queryModel.rawLocation()) |
| 184 queryModels.push(queryModel); |
| 185 } |
| 186 } |
| 187 queryModels.sort(compareModels); |
| 188 queryModels = this._squashAdjacentEqual(queryModels); |
| 189 |
| 190 var allEqual = this._cachedQueryModels && this._cachedQueryModels.length ===
queryModels.length; |
| 191 for (var i = 0; allEqual && i < queryModels.length; ++i) |
| 192 allEqual = allEqual && this._cachedQueryModels[i].equals(queryModels[i]); |
| 193 if (allEqual) |
| 194 return; |
| 195 this._cachedQueryModels = queryModels; |
| 196 this._renderMediaQueries(); |
| 197 |
| 198 /** |
| 199 * @param {!WebInspector.MediaQueryInspector.MediaQueryUIModel} model1 |
| 200 * @param {!WebInspector.MediaQueryInspector.MediaQueryUIModel} model2 |
| 201 * @return {number} |
| 202 */ |
| 203 function compareModels(model1, model2) { |
| 204 return model1.compareTo(model2); |
| 205 } |
| 206 } |
| 207 |
| 208 _renderMediaQueries() { |
| 209 if (!this._cachedQueryModels || !this.isShowing()) |
| 210 return; |
| 211 |
| 212 var markers = []; |
| 213 var lastMarker = null; |
| 214 for (var i = 0; i < this._cachedQueryModels.length; ++i) { |
| 215 var model = this._cachedQueryModels[i]; |
| 216 if (lastMarker && lastMarker.model.dimensionsEqual(model)) { |
| 217 lastMarker.locations.push(model.rawLocation()); |
| 218 lastMarker.active = lastMarker.active || model.active(); |
| 219 } else { |
| 220 lastMarker = {active: model.active(), model: model, locations: [model.ra
wLocation()]}; |
| 221 markers.push(lastMarker); |
| 222 } |
| 223 } |
| 224 |
| 225 this.contentElement.removeChildren(); |
| 226 |
| 227 var container = null; |
| 228 for (var i = 0; i < markers.length; ++i) { |
| 229 if (!i || markers[i].model.section() !== markers[i - 1].model.section()) |
| 230 container = this.contentElement.createChild('div', 'media-inspector-mark
er-container'); |
| 231 var marker = markers[i]; |
| 232 var bar = this._createElementFromMediaQueryModel(marker.model); |
| 233 bar._model = marker.model; |
| 234 bar._locations = marker.locations; |
| 235 bar.classList.toggle('media-inspector-marker-inactive', !marker.active); |
| 236 container.appendChild(bar); |
| 237 } |
| 238 } |
| 239 |
| 240 /** |
| 241 * @return {number} |
| 242 */ |
| 243 _zoomFactor() { |
| 244 return WebInspector.zoomManager.zoomFactor() / this._scale; |
| 245 } |
| 246 |
| 247 /** |
| 248 * @override |
| 249 */ |
| 250 wasShown() { |
| 251 this._scheduleMediaQueriesUpdate(); |
| 252 } |
| 253 |
| 254 /** |
| 255 * @param {!WebInspector.MediaQueryInspector.MediaQueryUIModel} model |
| 256 * @return {!Element} |
| 257 */ |
| 258 _createElementFromMediaQueryModel(model) { |
| 259 var zoomFactor = this._zoomFactor(); |
| 260 var minWidthValue = model.minWidthExpression() ? model.minWidthExpression().
computedLength() / zoomFactor : 0; |
| 261 var maxWidthValue = model.maxWidthExpression() ? model.maxWidthExpression().
computedLength() / zoomFactor : 0; |
| 262 var result = createElementWithClass('div', 'media-inspector-bar'); |
| 263 |
| 264 if (model.section() === WebInspector.MediaQueryInspector.Section.Max) { |
| 265 result.createChild('div', 'media-inspector-marker-spacer'); |
| 266 var markerElement = result.createChild('div', 'media-inspector-marker medi
a-inspector-marker-max-width'); |
| 267 markerElement.style.width = maxWidthValue + 'px'; |
| 268 markerElement.title = model.mediaText(); |
| 269 appendLabel(markerElement, model.maxWidthExpression(), false, false); |
| 270 appendLabel(markerElement, model.maxWidthExpression(), true, true); |
| 271 result.createChild('div', 'media-inspector-marker-spacer'); |
| 272 } |
| 273 |
| 274 if (model.section() === WebInspector.MediaQueryInspector.Section.MinMax) { |
| 275 result.createChild('div', 'media-inspector-marker-spacer'); |
| 276 var leftElement = result.createChild('div', 'media-inspector-marker media-
inspector-marker-min-max-width'); |
| 277 leftElement.style.width = (maxWidthValue - minWidthValue) * 0.5 + 'px'; |
| 278 leftElement.title = model.mediaText(); |
| 279 appendLabel(leftElement, model.minWidthExpression(), true, false); |
| 280 appendLabel(leftElement, model.maxWidthExpression(), false, true); |
| 281 result.createChild('div', 'media-inspector-marker-spacer').style.flex = '0
0 ' + minWidthValue + 'px'; |
| 282 var rightElement = result.createChild('div', 'media-inspector-marker media
-inspector-marker-min-max-width'); |
| 283 rightElement.style.width = (maxWidthValue - minWidthValue) * 0.5 + 'px'; |
| 284 rightElement.title = model.mediaText(); |
| 285 appendLabel(rightElement, model.minWidthExpression(), true, false); |
| 286 appendLabel(rightElement, model.maxWidthExpression(), false, true); |
| 287 result.createChild('div', 'media-inspector-marker-spacer'); |
| 288 } |
| 289 |
| 290 if (model.section() === WebInspector.MediaQueryInspector.Section.Min) { |
| 291 var leftElement = result.createChild( |
| 292 'div', 'media-inspector-marker media-inspector-marker-min-width media-
inspector-marker-min-width-left'); |
| 293 leftElement.title = model.mediaText(); |
| 294 appendLabel(leftElement, model.minWidthExpression(), false, false); |
| 295 result.createChild('div', 'media-inspector-marker-spacer').style.flex = '0
0 ' + minWidthValue + 'px'; |
| 296 var rightElement = result.createChild( |
| 297 'div', 'media-inspector-marker media-inspector-marker-min-width media-
inspector-marker-min-width-right'); |
| 298 rightElement.title = model.mediaText(); |
| 299 appendLabel(rightElement, model.minWidthExpression(), true, true); |
| 300 } |
| 301 |
| 302 function appendLabel(marker, expression, atLeft, leftAlign) { |
| 303 marker |
| 304 .createChild( |
| 305 'div', |
| 306 'media-inspector-marker-label-container ' + (atLeft ? 'media-inspe
ctor-marker-label-container-left' : |
| 307 'media-inspe
ctor-marker-label-container-right')) |
| 308 .createChild( |
| 309 'span', 'media-inspector-marker-label ' + |
| 310 (leftAlign ? 'media-inspector-label-left' : 'media-inspector-l
abel-right')) |
| 311 .textContent = expression.value() + expression.unit(); |
| 312 } |
| 313 |
| 314 return result; |
| 315 } |
| 27 }; | 316 }; |
| 28 | 317 |
| 29 /** | 318 /** |
| 30 * @enum {number} | 319 * @enum {number} |
| 31 */ | 320 */ |
| 32 WebInspector.MediaQueryInspector.Section = { | 321 WebInspector.MediaQueryInspector.Section = { |
| 33 Max: 0, | 322 Max: 0, |
| 34 MinMax: 1, | 323 MinMax: 1, |
| 35 Min: 2 | 324 Min: 2 |
| 36 }; | 325 }; |
| 37 | 326 |
| 38 WebInspector.MediaQueryInspector.prototype = { | |
| 39 /** | |
| 40 * @override | |
| 41 * @param {!WebInspector.Target} target | |
| 42 */ | |
| 43 targetAdded: function(target) | |
| 44 { | |
| 45 // FIXME: adapt this to multiple targets. | |
| 46 if (this._cssModel) | |
| 47 return; | |
| 48 this._cssModel = WebInspector.CSSModel.fromTarget(target); | |
| 49 if (!this._cssModel) | |
| 50 return; | |
| 51 this._cssModel.addEventListener(WebInspector.CSSModel.Events.StyleSheetA
dded, this._scheduleMediaQueriesUpdate, this); | |
| 52 this._cssModel.addEventListener(WebInspector.CSSModel.Events.StyleSheetR
emoved, this._scheduleMediaQueriesUpdate, this); | |
| 53 this._cssModel.addEventListener(WebInspector.CSSModel.Events.StyleSheetC
hanged, this._scheduleMediaQueriesUpdate, this); | |
| 54 this._cssModel.addEventListener(WebInspector.CSSModel.Events.MediaQueryR
esultChanged, this._scheduleMediaQueriesUpdate, this); | |
| 55 }, | |
| 56 | |
| 57 /** | |
| 58 * @override | |
| 59 * @param {!WebInspector.Target} target | |
| 60 */ | |
| 61 targetRemoved: function(target) | |
| 62 { | |
| 63 if (WebInspector.CSSModel.fromTarget(target) !== this._cssModel) | |
| 64 return; | |
| 65 this._cssModel.removeEventListener(WebInspector.CSSModel.Events.StyleShe
etAdded, this._scheduleMediaQueriesUpdate, this); | |
| 66 this._cssModel.removeEventListener(WebInspector.CSSModel.Events.StyleShe
etRemoved, this._scheduleMediaQueriesUpdate, this); | |
| 67 this._cssModel.removeEventListener(WebInspector.CSSModel.Events.StyleShe
etChanged, this._scheduleMediaQueriesUpdate, this); | |
| 68 this._cssModel.removeEventListener(WebInspector.CSSModel.Events.MediaQue
ryResultChanged, this._scheduleMediaQueriesUpdate, this); | |
| 69 delete this._cssModel; | |
| 70 }, | |
| 71 | |
| 72 /** | |
| 73 * @param {number} scale | |
| 74 */ | |
| 75 setAxisTransform: function(scale) | |
| 76 { | |
| 77 if (Math.abs(this._scale - scale) < 1e-8) | |
| 78 return; | |
| 79 this._scale = scale; | |
| 80 this._renderMediaQueries(); | |
| 81 }, | |
| 82 | |
| 83 /** | |
| 84 * @param {!Event} event | |
| 85 */ | |
| 86 _onMediaQueryClicked: function(event) | |
| 87 { | |
| 88 var mediaQueryMarker = event.target.enclosingNodeOrSelfWithClass("media-
inspector-bar"); | |
| 89 if (!mediaQueryMarker) | |
| 90 return; | |
| 91 | |
| 92 var model = mediaQueryMarker._model; | |
| 93 if (model.section() === WebInspector.MediaQueryInspector.Section.Max) { | |
| 94 this._setWidthCallback(model.maxWidthExpression().computedLength()); | |
| 95 return; | |
| 96 } | |
| 97 if (model.section() === WebInspector.MediaQueryInspector.Section.Min) { | |
| 98 this._setWidthCallback(model.minWidthExpression().computedLength()); | |
| 99 return; | |
| 100 } | |
| 101 var currentWidth = this._getWidthCallback(); | |
| 102 if (currentWidth !== model.minWidthExpression().computedLength()) | |
| 103 this._setWidthCallback(model.minWidthExpression().computedLength()); | |
| 104 else | |
| 105 this._setWidthCallback(model.maxWidthExpression().computedLength()); | |
| 106 }, | |
| 107 | |
| 108 /** | |
| 109 * @param {!Event} event | |
| 110 */ | |
| 111 _onContextMenu: function(event) | |
| 112 { | |
| 113 if (!this._cssModel || !this._cssModel.isEnabled()) | |
| 114 return; | |
| 115 | |
| 116 var mediaQueryMarker = event.target.enclosingNodeOrSelfWithClass("media-
inspector-bar"); | |
| 117 if (!mediaQueryMarker) | |
| 118 return; | |
| 119 | |
| 120 var locations = mediaQueryMarker._locations; | |
| 121 var uiLocations = new Map(); | |
| 122 for (var i = 0; i < locations.length; ++i) { | |
| 123 var uiLocation = WebInspector.cssWorkspaceBinding.rawLocationToUILoc
ation(locations[i]); | |
| 124 if (!uiLocation) | |
| 125 continue; | |
| 126 var descriptor = String.sprintf("%s:%d:%d", uiLocation.uiSourceCode.
url(), uiLocation.lineNumber + 1, uiLocation.columnNumber + 1); | |
| 127 uiLocations.set(descriptor, uiLocation); | |
| 128 } | |
| 129 | |
| 130 var contextMenuItems = uiLocations.keysArray().sort(); | |
| 131 var contextMenu = new WebInspector.ContextMenu(event); | |
| 132 var subMenuItem = contextMenu.appendSubMenuItem(WebInspector.UIString.ca
pitalize("Reveal in ^source ^code")); | |
| 133 for (var i = 0; i < contextMenuItems.length; ++i) { | |
| 134 var title = contextMenuItems[i]; | |
| 135 subMenuItem.appendItem(title, this._revealSourceLocation.bind(this,
/** @type {!WebInspector.UILocation} */(uiLocations.get(title)))); | |
| 136 } | |
| 137 contextMenu.show(); | |
| 138 }, | |
| 139 | |
| 140 /** | |
| 141 * @param {!WebInspector.UILocation} location | |
| 142 */ | |
| 143 _revealSourceLocation: function(location) | |
| 144 { | |
| 145 WebInspector.Revealer.reveal(location); | |
| 146 }, | |
| 147 | |
| 148 _scheduleMediaQueriesUpdate: function() | |
| 149 { | |
| 150 if (!this.isShowing()) | |
| 151 return; | |
| 152 this._mediaThrottler.schedule(this._refetchMediaQueries.bind(this)); | |
| 153 }, | |
| 154 | |
| 155 _refetchMediaQueries: function() | |
| 156 { | |
| 157 if (!this.isShowing() || !this._cssModel) | |
| 158 return Promise.resolve(); | |
| 159 | |
| 160 return this._cssModel.mediaQueriesPromise() | |
| 161 .then(this._rebuildMediaQueries.bind(this)); | |
| 162 }, | |
| 163 | |
| 164 /** | |
| 165 * @param {!Array.<!WebInspector.MediaQueryInspector.MediaQueryUIModel>} mod
els | |
| 166 * @return {!Array.<!WebInspector.MediaQueryInspector.MediaQueryUIModel>} | |
| 167 */ | |
| 168 _squashAdjacentEqual: function(models) | |
| 169 { | |
| 170 var filtered = []; | |
| 171 for (var i = 0; i < models.length; ++i) { | |
| 172 var last = filtered.peekLast(); | |
| 173 if (!last || !last.equals(models[i])) | |
| 174 filtered.push(models[i]); | |
| 175 } | |
| 176 return filtered; | |
| 177 }, | |
| 178 | |
| 179 /** | |
| 180 * @param {!Array.<!WebInspector.CSSMedia>} cssMedias | |
| 181 */ | |
| 182 _rebuildMediaQueries: function(cssMedias) | |
| 183 { | |
| 184 var queryModels = []; | |
| 185 for (var i = 0; i < cssMedias.length; ++i) { | |
| 186 var cssMedia = cssMedias[i]; | |
| 187 if (!cssMedia.mediaList) | |
| 188 continue; | |
| 189 for (var j = 0; j < cssMedia.mediaList.length; ++j) { | |
| 190 var mediaQuery = cssMedia.mediaList[j]; | |
| 191 var queryModel = WebInspector.MediaQueryInspector.MediaQueryUIMo
del.createFromMediaQuery(cssMedia, mediaQuery); | |
| 192 if (queryModel && queryModel.rawLocation()) | |
| 193 queryModels.push(queryModel); | |
| 194 } | |
| 195 } | |
| 196 queryModels.sort(compareModels); | |
| 197 queryModels = this._squashAdjacentEqual(queryModels); | |
| 198 | |
| 199 var allEqual = this._cachedQueryModels && this._cachedQueryModels.length
=== queryModels.length; | |
| 200 for (var i = 0; allEqual && i < queryModels.length; ++i) | |
| 201 allEqual = allEqual && this._cachedQueryModels[i].equals(queryModels
[i]); | |
| 202 if (allEqual) | |
| 203 return; | |
| 204 this._cachedQueryModels = queryModels; | |
| 205 this._renderMediaQueries(); | |
| 206 | |
| 207 /** | |
| 208 * @param {!WebInspector.MediaQueryInspector.MediaQueryUIModel} model1 | |
| 209 * @param {!WebInspector.MediaQueryInspector.MediaQueryUIModel} model2 | |
| 210 * @return {number} | |
| 211 */ | |
| 212 function compareModels(model1, model2) | |
| 213 { | |
| 214 return model1.compareTo(model2); | |
| 215 } | |
| 216 }, | |
| 217 | |
| 218 _renderMediaQueries: function() | |
| 219 { | |
| 220 if (!this._cachedQueryModels || !this.isShowing()) | |
| 221 return; | |
| 222 | |
| 223 var markers = []; | |
| 224 var lastMarker = null; | |
| 225 for (var i = 0; i < this._cachedQueryModels.length; ++i) { | |
| 226 var model = this._cachedQueryModels[i]; | |
| 227 if (lastMarker && lastMarker.model.dimensionsEqual(model)) { | |
| 228 lastMarker.locations.push(model.rawLocation()); | |
| 229 lastMarker.active = lastMarker.active || model.active(); | |
| 230 } else { | |
| 231 lastMarker = { | |
| 232 active: model.active(), | |
| 233 model: model, | |
| 234 locations: [ model.rawLocation() ] | |
| 235 }; | |
| 236 markers.push(lastMarker); | |
| 237 } | |
| 238 } | |
| 239 | |
| 240 this.contentElement.removeChildren(); | |
| 241 | |
| 242 var container = null; | |
| 243 for (var i = 0; i < markers.length; ++i) { | |
| 244 if (!i || markers[i].model.section() !== markers[i - 1].model.sectio
n()) | |
| 245 container = this.contentElement.createChild("div", "media-inspec
tor-marker-container"); | |
| 246 var marker = markers[i]; | |
| 247 var bar = this._createElementFromMediaQueryModel(marker.model); | |
| 248 bar._model = marker.model; | |
| 249 bar._locations = marker.locations; | |
| 250 bar.classList.toggle("media-inspector-marker-inactive", !marker.acti
ve); | |
| 251 container.appendChild(bar); | |
| 252 } | |
| 253 }, | |
| 254 | |
| 255 /** | |
| 256 * @return {number} | |
| 257 */ | |
| 258 _zoomFactor: function() | |
| 259 { | |
| 260 return WebInspector.zoomManager.zoomFactor() / this._scale; | |
| 261 }, | |
| 262 | |
| 263 wasShown: function() | |
| 264 { | |
| 265 this._scheduleMediaQueriesUpdate(); | |
| 266 }, | |
| 267 | |
| 268 /** | |
| 269 * @param {!WebInspector.MediaQueryInspector.MediaQueryUIModel} model | |
| 270 * @return {!Element} | |
| 271 */ | |
| 272 _createElementFromMediaQueryModel: function(model) | |
| 273 { | |
| 274 var zoomFactor = this._zoomFactor(); | |
| 275 var minWidthValue = model.minWidthExpression() ? model.minWidthExpressio
n().computedLength() / zoomFactor : 0; | |
| 276 var maxWidthValue = model.maxWidthExpression() ? model.maxWidthExpressio
n().computedLength() / zoomFactor : 0; | |
| 277 var result = createElementWithClass("div", "media-inspector-bar"); | |
| 278 | |
| 279 if (model.section() === WebInspector.MediaQueryInspector.Section.Max) { | |
| 280 result.createChild("div", "media-inspector-marker-spacer"); | |
| 281 var markerElement = result.createChild("div", "media-inspector-marke
r media-inspector-marker-max-width"); | |
| 282 markerElement.style.width = maxWidthValue + "px"; | |
| 283 markerElement.title = model.mediaText(); | |
| 284 appendLabel(markerElement, model.maxWidthExpression(), false, false)
; | |
| 285 appendLabel(markerElement, model.maxWidthExpression(), true, true); | |
| 286 result.createChild("div", "media-inspector-marker-spacer"); | |
| 287 } | |
| 288 | |
| 289 if (model.section() === WebInspector.MediaQueryInspector.Section.MinMax)
{ | |
| 290 result.createChild("div", "media-inspector-marker-spacer"); | |
| 291 var leftElement = result.createChild("div", "media-inspector-marker
media-inspector-marker-min-max-width"); | |
| 292 leftElement.style.width = (maxWidthValue - minWidthValue) * 0.5 + "p
x"; | |
| 293 leftElement.title = model.mediaText(); | |
| 294 appendLabel(leftElement, model.minWidthExpression(), true, false); | |
| 295 appendLabel(leftElement, model.maxWidthExpression(), false, true); | |
| 296 result.createChild("div", "media-inspector-marker-spacer").style.fle
x = "0 0 " + minWidthValue + "px"; | |
| 297 var rightElement = result.createChild("div", "media-inspector-marker
media-inspector-marker-min-max-width"); | |
| 298 rightElement.style.width = (maxWidthValue - minWidthValue) * 0.5 +
"px"; | |
| 299 rightElement.title = model.mediaText(); | |
| 300 appendLabel(rightElement, model.minWidthExpression(), true, false); | |
| 301 appendLabel(rightElement, model.maxWidthExpression(), false, true); | |
| 302 result.createChild("div", "media-inspector-marker-spacer"); | |
| 303 } | |
| 304 | |
| 305 if (model.section() === WebInspector.MediaQueryInspector.Section.Min) { | |
| 306 var leftElement = result.createChild("div", "media-inspector-marker
media-inspector-marker-min-width media-inspector-marker-min-width-left"); | |
| 307 leftElement.title = model.mediaText(); | |
| 308 appendLabel(leftElement, model.minWidthExpression(), false, false); | |
| 309 result.createChild("div", "media-inspector-marker-spacer").style.fle
x = "0 0 " + minWidthValue + "px"; | |
| 310 var rightElement = result.createChild("div", "media-inspector-marker
media-inspector-marker-min-width media-inspector-marker-min-width-right"); | |
| 311 rightElement.title = model.mediaText(); | |
| 312 appendLabel(rightElement, model.minWidthExpression(), true, true); | |
| 313 } | |
| 314 | |
| 315 function appendLabel(marker, expression, atLeft, leftAlign) | |
| 316 { | |
| 317 marker.createChild("div", "media-inspector-marker-label-container "
+ (atLeft ? "media-inspector-marker-label-container-left" : "media-inspector-mar
ker-label-container-right")) | |
| 318 .createChild("span", "media-inspector-marker-label " + (leftAlig
n ? "media-inspector-label-left" : "media-inspector-label-right")) | |
| 319 .textContent = expression.value() + expression.unit(); | |
| 320 } | |
| 321 | |
| 322 return result; | |
| 323 }, | |
| 324 | |
| 325 __proto__: WebInspector.Widget.prototype | |
| 326 }; | |
| 327 | |
| 328 /** | 327 /** |
| 329 * @constructor | 328 * @unrestricted |
| 330 * @param {!WebInspector.CSSMedia} cssMedia | |
| 331 * @param {?WebInspector.CSSMediaQueryExpression} minWidthExpression | |
| 332 * @param {?WebInspector.CSSMediaQueryExpression} maxWidthExpression | |
| 333 * @param {boolean} active | |
| 334 */ | 329 */ |
| 335 WebInspector.MediaQueryInspector.MediaQueryUIModel = function(cssMedia, minWidth
Expression, maxWidthExpression, active) | 330 WebInspector.MediaQueryInspector.MediaQueryUIModel = class { |
| 336 { | 331 /** |
| 332 * @param {!WebInspector.CSSMedia} cssMedia |
| 333 * @param {?WebInspector.CSSMediaQueryExpression} minWidthExpression |
| 334 * @param {?WebInspector.CSSMediaQueryExpression} maxWidthExpression |
| 335 * @param {boolean} active |
| 336 */ |
| 337 constructor(cssMedia, minWidthExpression, maxWidthExpression, active) { |
| 337 this._cssMedia = cssMedia; | 338 this._cssMedia = cssMedia; |
| 338 this._minWidthExpression = minWidthExpression; | 339 this._minWidthExpression = minWidthExpression; |
| 339 this._maxWidthExpression = maxWidthExpression; | 340 this._maxWidthExpression = maxWidthExpression; |
| 340 this._active = active; | 341 this._active = active; |
| 341 if (maxWidthExpression && !minWidthExpression) | 342 if (maxWidthExpression && !minWidthExpression) |
| 342 this._section = WebInspector.MediaQueryInspector.Section.Max; | 343 this._section = WebInspector.MediaQueryInspector.Section.Max; |
| 343 else if (minWidthExpression && maxWidthExpression) | 344 else if (minWidthExpression && maxWidthExpression) |
| 344 this._section = WebInspector.MediaQueryInspector.Section.MinMax; | 345 this._section = WebInspector.MediaQueryInspector.Section.MinMax; |
| 345 else | 346 else |
| 346 this._section = WebInspector.MediaQueryInspector.Section.Min; | 347 this._section = WebInspector.MediaQueryInspector.Section.Min; |
| 347 }; | 348 } |
| 348 | 349 |
| 349 /** | 350 /** |
| 350 * @param {!WebInspector.CSSMedia} cssMedia | 351 * @param {!WebInspector.CSSMedia} cssMedia |
| 351 * @param {!WebInspector.CSSMediaQuery} mediaQuery | 352 * @param {!WebInspector.CSSMediaQuery} mediaQuery |
| 352 * @return {?WebInspector.MediaQueryInspector.MediaQueryUIModel} | 353 * @return {?WebInspector.MediaQueryInspector.MediaQueryUIModel} |
| 353 */ | 354 */ |
| 354 WebInspector.MediaQueryInspector.MediaQueryUIModel.createFromMediaQuery = functi
on(cssMedia, mediaQuery) | 355 static createFromMediaQuery(cssMedia, mediaQuery) { |
| 355 { | |
| 356 var maxWidthExpression = null; | 356 var maxWidthExpression = null; |
| 357 var maxWidthPixels = Number.MAX_VALUE; | 357 var maxWidthPixels = Number.MAX_VALUE; |
| 358 var minWidthExpression = null; | 358 var minWidthExpression = null; |
| 359 var minWidthPixels = Number.MIN_VALUE; | 359 var minWidthPixels = Number.MIN_VALUE; |
| 360 var expressions = mediaQuery.expressions(); | 360 var expressions = mediaQuery.expressions(); |
| 361 for (var i = 0; i < expressions.length; ++i) { | 361 for (var i = 0; i < expressions.length; ++i) { |
| 362 var expression = expressions[i]; | 362 var expression = expressions[i]; |
| 363 var feature = expression.feature(); | 363 var feature = expression.feature(); |
| 364 if (feature.indexOf("width") === -1) | 364 if (feature.indexOf('width') === -1) |
| 365 continue; | 365 continue; |
| 366 var pixels = expression.computedLength(); | 366 var pixels = expression.computedLength(); |
| 367 if (feature.startsWith("max-") && pixels < maxWidthPixels) { | 367 if (feature.startsWith('max-') && pixels < maxWidthPixels) { |
| 368 maxWidthExpression = expression; | 368 maxWidthExpression = expression; |
| 369 maxWidthPixels = pixels; | 369 maxWidthPixels = pixels; |
| 370 } else if (feature.startsWith("min-") && pixels > minWidthPixels) { | 370 } else if (feature.startsWith('min-') && pixels > minWidthPixels) { |
| 371 minWidthExpression = expression; | 371 minWidthExpression = expression; |
| 372 minWidthPixels = pixels; | 372 minWidthPixels = pixels; |
| 373 } | 373 } |
| 374 } | 374 } |
| 375 if (minWidthPixels > maxWidthPixels || (!maxWidthExpression && !minWidthExpr
ession)) | 375 if (minWidthPixels > maxWidthPixels || (!maxWidthExpression && !minWidthExpr
ession)) |
| 376 return null; | 376 return null; |
| 377 | 377 |
| 378 return new WebInspector.MediaQueryInspector.MediaQueryUIModel(cssMedia, minW
idthExpression, maxWidthExpression, mediaQuery.active()); | 378 return new WebInspector.MediaQueryInspector.MediaQueryUIModel( |
| 379 cssMedia, minWidthExpression, maxWidthExpression, mediaQuery.active()); |
| 380 } |
| 381 |
| 382 /** |
| 383 * @param {!WebInspector.MediaQueryInspector.MediaQueryUIModel} other |
| 384 * @return {boolean} |
| 385 */ |
| 386 equals(other) { |
| 387 return this.compareTo(other) === 0; |
| 388 } |
| 389 |
| 390 /** |
| 391 * @param {!WebInspector.MediaQueryInspector.MediaQueryUIModel} other |
| 392 * @return {boolean} |
| 393 */ |
| 394 dimensionsEqual(other) { |
| 395 return this.section() === other.section() && |
| 396 (!this.minWidthExpression() || |
| 397 (this.minWidthExpression().computedLength() === other.minWidthExpressio
n().computedLength())) && |
| 398 (!this.maxWidthExpression() || |
| 399 (this.maxWidthExpression().computedLength() === other.maxWidthExpressio
n().computedLength())); |
| 400 } |
| 401 |
| 402 /** |
| 403 * @param {!WebInspector.MediaQueryInspector.MediaQueryUIModel} other |
| 404 * @return {number} |
| 405 */ |
| 406 compareTo(other) { |
| 407 if (this.section() !== other.section()) |
| 408 return this.section() - other.section(); |
| 409 if (this.dimensionsEqual(other)) { |
| 410 var myLocation = this.rawLocation(); |
| 411 var otherLocation = other.rawLocation(); |
| 412 if (!myLocation && !otherLocation) |
| 413 return this.mediaText().compareTo(other.mediaText()); |
| 414 if (myLocation && !otherLocation) |
| 415 return 1; |
| 416 if (!myLocation && otherLocation) |
| 417 return -1; |
| 418 if (this.active() !== other.active()) |
| 419 return this.active() ? -1 : 1; |
| 420 return myLocation.url.compareTo(otherLocation.url) || myLocation.lineNumbe
r - otherLocation.lineNumber || |
| 421 myLocation.columnNumber - otherLocation.columnNumber; |
| 422 } |
| 423 if (this.section() === WebInspector.MediaQueryInspector.Section.Max) |
| 424 return other.maxWidthExpression().computedLength() - this.maxWidthExpressi
on().computedLength(); |
| 425 if (this.section() === WebInspector.MediaQueryInspector.Section.Min) |
| 426 return this.minWidthExpression().computedLength() - other.minWidthExpressi
on().computedLength(); |
| 427 return this.minWidthExpression().computedLength() - other.minWidthExpression
().computedLength() || |
| 428 other.maxWidthExpression().computedLength() - this.maxWidthExpression().
computedLength(); |
| 429 } |
| 430 |
| 431 /** |
| 432 * @return {!WebInspector.MediaQueryInspector.Section} |
| 433 */ |
| 434 section() { |
| 435 return this._section; |
| 436 } |
| 437 |
| 438 /** |
| 439 * @return {string} |
| 440 */ |
| 441 mediaText() { |
| 442 return this._cssMedia.text; |
| 443 } |
| 444 |
| 445 /** |
| 446 * @return {?WebInspector.CSSLocation} |
| 447 */ |
| 448 rawLocation() { |
| 449 if (!this._rawLocation) |
| 450 this._rawLocation = this._cssMedia.rawLocation(); |
| 451 return this._rawLocation; |
| 452 } |
| 453 |
| 454 /** |
| 455 * @return {?WebInspector.CSSMediaQueryExpression} |
| 456 */ |
| 457 minWidthExpression() { |
| 458 return this._minWidthExpression; |
| 459 } |
| 460 |
| 461 /** |
| 462 * @return {?WebInspector.CSSMediaQueryExpression} |
| 463 */ |
| 464 maxWidthExpression() { |
| 465 return this._maxWidthExpression; |
| 466 } |
| 467 |
| 468 /** |
| 469 * @return {boolean} |
| 470 */ |
| 471 active() { |
| 472 return this._active; |
| 473 } |
| 379 }; | 474 }; |
| 380 | 475 |
| 381 WebInspector.MediaQueryInspector.MediaQueryUIModel.prototype = { | 476 |
| 382 /** | |
| 383 * @param {!WebInspector.MediaQueryInspector.MediaQueryUIModel} other | |
| 384 * @return {boolean} | |
| 385 */ | |
| 386 equals: function(other) | |
| 387 { | |
| 388 return this.compareTo(other) === 0; | |
| 389 }, | |
| 390 | |
| 391 /** | |
| 392 * @param {!WebInspector.MediaQueryInspector.MediaQueryUIModel} other | |
| 393 * @return {boolean} | |
| 394 */ | |
| 395 dimensionsEqual: function(other) | |
| 396 { | |
| 397 return this.section() === other.section() | |
| 398 && (!this.minWidthExpression() || (this.minWidthExpression().compute
dLength() === other.minWidthExpression().computedLength())) | |
| 399 && (!this.maxWidthExpression() || (this.maxWidthExpression().compute
dLength() === other.maxWidthExpression().computedLength())); | |
| 400 }, | |
| 401 | |
| 402 /** | |
| 403 * @param {!WebInspector.MediaQueryInspector.MediaQueryUIModel} other | |
| 404 * @return {number} | |
| 405 */ | |
| 406 compareTo: function(other) | |
| 407 { | |
| 408 if (this.section() !== other.section()) | |
| 409 return this.section() - other.section(); | |
| 410 if (this.dimensionsEqual(other)) { | |
| 411 var myLocation = this.rawLocation(); | |
| 412 var otherLocation = other.rawLocation(); | |
| 413 if (!myLocation && !otherLocation) | |
| 414 return this.mediaText().compareTo(other.mediaText()); | |
| 415 if (myLocation && !otherLocation) | |
| 416 return 1; | |
| 417 if (!myLocation && otherLocation) | |
| 418 return -1; | |
| 419 if (this.active() !== other.active()) | |
| 420 return this.active() ? -1 : 1; | |
| 421 return myLocation.url.compareTo(otherLocation.url) || myLocation.lin
eNumber - otherLocation.lineNumber || myLocation.columnNumber - otherLocation.co
lumnNumber; | |
| 422 } | |
| 423 if (this.section() === WebInspector.MediaQueryInspector.Section.Max) | |
| 424 return other.maxWidthExpression().computedLength() - this.maxWidthEx
pression().computedLength(); | |
| 425 if (this.section() === WebInspector.MediaQueryInspector.Section.Min) | |
| 426 return this.minWidthExpression().computedLength() - other.minWidthEx
pression().computedLength(); | |
| 427 return this.minWidthExpression().computedLength() - other.minWidthExpres
sion().computedLength() || other.maxWidthExpression().computedLength() - this.ma
xWidthExpression().computedLength(); | |
| 428 }, | |
| 429 | |
| 430 /** | |
| 431 * @return {!WebInspector.MediaQueryInspector.Section} | |
| 432 */ | |
| 433 section: function() | |
| 434 { | |
| 435 return this._section; | |
| 436 }, | |
| 437 | |
| 438 /** | |
| 439 * @return {string} | |
| 440 */ | |
| 441 mediaText: function() | |
| 442 { | |
| 443 return this._cssMedia.text; | |
| 444 }, | |
| 445 | |
| 446 /** | |
| 447 * @return {?WebInspector.CSSLocation} | |
| 448 */ | |
| 449 rawLocation: function() | |
| 450 { | |
| 451 if (!this._rawLocation) | |
| 452 this._rawLocation = this._cssMedia.rawLocation(); | |
| 453 return this._rawLocation; | |
| 454 }, | |
| 455 | |
| 456 /** | |
| 457 * @return {?WebInspector.CSSMediaQueryExpression} | |
| 458 */ | |
| 459 minWidthExpression: function() | |
| 460 { | |
| 461 return this._minWidthExpression; | |
| 462 }, | |
| 463 | |
| 464 /** | |
| 465 * @return {?WebInspector.CSSMediaQueryExpression} | |
| 466 */ | |
| 467 maxWidthExpression: function() | |
| 468 { | |
| 469 return this._maxWidthExpression; | |
| 470 }, | |
| 471 | |
| 472 /** | |
| 473 * @return {boolean} | |
| 474 */ | |
| 475 active: function() | |
| 476 { | |
| 477 return this._active; | |
| 478 } | |
| 479 }; | |
| OLD | NEW |