Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 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. | |
| 4 | |
| 5 /** | |
| 6 * @fileoverview Behavior for handling display layout, specifically | |
| 7 * edge snapping and collisions. | |
| 8 */ | |
| 9 | |
| 10 /** @polymerBehavior */ | |
| 11 var LayoutBehavior = { | |
| 12 properties: { | |
| 13 /** | |
| 14 * Array of display layouts. | |
| 15 * @type {!Array<!chrome.system.display.DisplayLayout>} | |
| 16 */ | |
| 17 layouts: Array, | |
| 18 }, | |
| 19 | |
| 20 /** @private {!Map<string, chrome.system.display.Bounds>} */ | |
| 21 displayBoundsMap_: new Map(), | |
| 22 | |
| 23 /** @private {!Map<string, chrome.system.display.DisplayLayout>} */ | |
| 24 displayLayoutMap_: new Map(), | |
| 25 | |
| 26 /** | |
| 27 * The calculated bounds used for generating the div bounds. | |
| 28 * @private {!Map<string, chrome.system.display.Bounds>} | |
| 29 */ | |
| 30 calculatedBoundsMap_: new Map(), | |
| 31 | |
| 32 /** @private {string} */ | |
| 33 dragLayoutId: '', | |
| 34 | |
| 35 /** @private {string} */ | |
| 36 dragParentId_: '', | |
| 37 | |
| 38 /** @private {!chrome.system.display.Bounds|undefined} */ | |
| 39 dragBounds_: undefined, | |
| 40 | |
| 41 /** @private {!chrome.system.display.LayoutPosition|undefined} */ | |
| 42 dragLayoutPosition_: undefined, | |
| 43 | |
| 44 /** | |
| 45 * @param {!Array<!chrome.system.display.DisplayUnitInfo>} displays | |
| 46 * @param {!Array<!chrome.system.display.DisplayLayout>} layouts | |
| 47 */ | |
| 48 initializeDisplayLayout: function(displays, layouts) { | |
| 49 this.dragLayoutId = ''; | |
| 50 this.dragParentId_ = ''; | |
| 51 | |
| 52 this.displayBoundsMap_.clear(); | |
| 53 for (let display of displays) | |
| 54 this.displayBoundsMap_.set(display.id, display.bounds); | |
| 55 | |
| 56 this.displayLayoutMap_.clear(); | |
| 57 for (let layout of layouts) | |
| 58 this.displayLayoutMap_.set(layout.id, layout); | |
| 59 | |
| 60 this.calculatedBoundsMap_.clear(); | |
| 61 for (let display of displays) { | |
| 62 if (!this.calculatedBoundsMap_.has(display.id)) { | |
| 63 let bounds = display.bounds; | |
| 64 this.calculateBounds_(display.id, bounds.width, bounds.height); | |
| 65 } | |
| 66 } | |
| 67 }, | |
| 68 | |
| 69 /** | |
| 70 * Called when a drag event occurs. Checks collisions and updates the layout. | |
| 71 * @param {string} id | |
| 72 * @param {!chrome.system.display.Bounds} newBounds The new calculated | |
| 73 * bounds for the display. | |
| 74 * @return {!chrome.system.display.Bounds} | |
| 75 */ | |
| 76 updateDisplayBounds(id, newBounds) { | |
| 77 this.dragLayoutId = id; | |
| 78 | |
| 79 // Find the closest parent. | |
| 80 var closestId = this.findClosest_(id, newBounds); | |
| 81 | |
| 82 // Find the closest edge. | |
| 83 var layoutPosition = this.getLayoutPositionForBounds_(newBounds, closestId); | |
| 84 | |
| 85 // Snap to the closest edge | |
| 86 this.snapBounds_(closestId, layoutPosition, newBounds); | |
| 87 | |
| 88 // Calculate the new bounds and delta. | |
| 89 var oldBounds = this.dragBounds_ || this.getCalculatedDisplayBounds(id); | |
| 90 var deltaPos = { | |
| 91 x: newBounds.left - oldBounds.left, | |
| 92 y: newBounds.top - oldBounds.top | |
| 93 }; | |
| 94 | |
| 95 // Check for collisions. | |
| 96 this.collideAndModifyDelta_(id, oldBounds, deltaPos); | |
| 97 | |
| 98 // If the edge changed, update and highlight it. | |
| 99 if (layoutPosition != this.draglayoutPosition_ || | |
| 100 closestId != this.dragParentId_) { | |
| 101 this.draglayoutPosition_ = layoutPosition; | |
| 102 this.dragParentId_ = closestId; | |
| 103 this.highlightEdge_(closestId, layoutPosition); | |
| 104 } | |
| 105 | |
| 106 newBounds.left = oldBounds.left + deltaPos.x; | |
| 107 newBounds.top = oldBounds.top + deltaPos.y; | |
| 108 | |
| 109 this.dragBounds_ = newBounds; | |
| 110 | |
| 111 return newBounds; | |
| 112 }, | |
| 113 | |
| 114 /** | |
| 115 * Called when dragging ends. Sends the updated layout to chrome. | |
| 116 * @param {string} id | |
| 117 */ | |
| 118 finishUpdateDisplayBounds(id) { | |
| 119 this.highlightEdge_('', undefined); // Remove any highlights. | |
| 120 if (id != this.dragLayoutId || !this.dragBounds_) | |
| 121 return; | |
| 122 var layout = this.displayLayoutMap_.get(id); | |
| 123 if (!layout) | |
| 124 return; | |
| 125 // Note: This updates layout in this.displayLayoutMap_ which is also the | |
| 126 // entry in this.layouts. | |
| 127 this.updateOffsetAndPosition_( | |
| 128 this.dragBounds_, this.draglayoutPosition_, layout); | |
| 129 | |
| 130 // Send the updated layouts. | |
| 131 chrome.system.display.setDisplayLayout(this.layouts, function() { | |
| 132 if (chrome.runtime.lastError) { | |
| 133 console.error( | |
| 134 'setDisplayLayout Error: ' + chrome.runtime.lastError.message); | |
| 135 } | |
| 136 }); | |
| 137 }, | |
| 138 | |
| 139 /** | |
| 140 * @param {string} displayId | |
| 141 * @return {!chrome.system.display.Bounds} bounds | |
| 142 */ | |
| 143 getCalculatedDisplayBounds: function(displayId) { | |
| 144 var bounds = this.calculatedBoundsMap_.get(displayId); | |
| 145 assert(bounds); | |
| 146 return bounds; | |
| 147 }, | |
| 148 | |
| 149 /** | |
| 150 * @param {string} displayId | |
| 151 * @param {!chrome.system.display.Bounds|undefined} bounds | |
| 152 * @private | |
| 153 */ | |
| 154 setCalculatedDisplayBounds_: function(displayId, bounds) { | |
| 155 assert(bounds); | |
| 156 this.calculatedBoundsMap_.set( | |
| 157 displayId, | |
| 158 /** @type {!chrome.system.display.Bounds} */ ( | |
| 159 Object.assign({}, bounds))); | |
| 160 }, | |
| 161 | |
| 162 /** | |
| 163 * Recursively calculate the absolute bounds of a display. | |
| 164 * Caches the display bounds so that parent bounds are only calculated once. | |
| 165 * @param {string} id | |
| 166 * @param {number} width | |
| 167 * @param {number} height | |
| 168 * @private | |
| 169 */ | |
| 170 calculateBounds_: function(id, width, height) { | |
| 171 var left, top; | |
| 172 var layout = this.displayLayoutMap_.get(id); | |
| 173 if (!layout || !layout.parentId) { | |
| 174 left = -width / 2; | |
| 175 top = -height / 2; | |
| 176 } else { | |
| 177 if (!this.calculatedBoundsMap_.has(layout.parentId)) { | |
| 178 var pbounds = this.displayBoundsMap_.get(layout.parentId); | |
| 179 this.calculateBounds_(layout.parentId, pbounds.width, pbounds.height); | |
| 180 } | |
| 181 var parentBounds = this.getCalculatedDisplayBounds(layout.parentId); | |
| 182 left = parentBounds.left; | |
| 183 top = parentBounds.top; | |
| 184 switch (layout.position) { | |
| 185 case chrome.system.display.LayoutPosition.TOP: | |
| 186 left += layout.offset; | |
| 187 top -= height; | |
| 188 break; | |
| 189 case chrome.system.display.LayoutPosition.RIGHT: | |
| 190 left += parentBounds.width; | |
| 191 top += layout.offset; | |
| 192 break; | |
| 193 case chrome.system.display.LayoutPosition.BOTTOM: | |
| 194 left += layout.offset; | |
| 195 top += parentBounds.height; | |
| 196 break; | |
| 197 case chrome.system.display.LayoutPosition.LEFT: | |
| 198 left -= width; | |
| 199 top += layout.offset; | |
| 200 break; | |
| 201 } | |
| 202 } | |
| 203 var result = { | |
| 204 left: left, | |
| 205 top: top, | |
| 206 width: width, | |
| 207 height: height, | |
| 208 }; | |
| 209 this.setCalculatedDisplayBounds_(id, result); | |
| 210 }, | |
| 211 | |
| 212 /** | |
| 213 * Finds the display closest to |bounds| ignoring |opt_ignoreIds|. | |
| 214 * @param {string} displayId | |
| 215 * @param {!chrome.system.display.Bounds} bounds | |
| 216 * @param {Array<string>=} opt_ignoreIds Ids to ignore. | |
| 217 * @return {string} | |
| 218 * @private | |
| 219 */ | |
| 220 findClosest_: function(displayId, bounds, opt_ignoreIds) { | |
| 221 var x = bounds.left + bounds.width / 2; | |
| 222 var y = bounds.top + bounds.height / 2; | |
| 223 var closestId = ''; | |
| 224 var closestDelta2 = 0; | |
| 225 for (let otherId of this.calculatedBoundsMap_.keys()) { | |
| 226 if (otherId == displayId) | |
| 227 continue; | |
| 228 if (opt_ignoreIds && opt_ignoreIds.includes(otherId)) | |
| 229 continue; | |
| 230 var otherBounds = this.getCalculatedDisplayBounds(otherId); | |
| 231 var left = otherBounds.left; | |
| 232 var top = otherBounds.top; | |
| 233 var width = otherBounds.width; | |
| 234 var height = otherBounds.height; | |
| 235 if (x >= left && x < left + width && y >= top && y < top + height) | |
| 236 return otherId; // point is inside rect | |
| 237 var dx, dy; | |
| 238 if (x < left) | |
| 239 dx = left - x; | |
| 240 else if (x > left + width) | |
| 241 dx = x - (left + width); | |
| 242 else | |
| 243 dx = 0; | |
| 244 if (y < top) | |
| 245 dy = top - y; | |
| 246 else if (y > top + height) | |
| 247 dy = y - (top + height); | |
| 248 else | |
| 249 dy = 0; | |
| 250 var delta2 = dx * dx + dy * dy; | |
| 251 if (closestId == '' || delta2 < closestDelta2) { | |
| 252 closestId = otherId; | |
| 253 closestDelta2 = delta2; | |
| 254 } | |
| 255 } | |
| 256 return closestId; | |
| 257 }, | |
| 258 | |
| 259 /** | |
| 260 * Calculates the LayoutPosition for |bounds| relative to |parentId|. | |
| 261 * @param {!chrome.system.display.Bounds} bounds | |
| 262 * @param {string} parentId | |
| 263 * @return {!chrome.system.display.LayoutPosition} | |
| 264 */ | |
| 265 getLayoutPositionForBounds_: function(bounds, parentId) { | |
| 266 // Translate bounds from top-left to center. | |
| 267 var x = bounds.left + bounds.width / 2; | |
| 268 var y = bounds.top + bounds.height / 2; | |
| 269 | |
| 270 // Determine the distance from the new bounds to both of the near edges. | |
| 271 var parentBounds = this.getCalculatedDisplayBounds(parentId); | |
| 272 var left = parentBounds.left; | |
| 273 var top = parentBounds.top; | |
| 274 var width = parentBounds.width; | |
| 275 var height = parentBounds.height; | |
| 276 | |
| 277 // Signed deltas to the center of the div. | |
| 278 var dx = x - (left + width / 2); | |
| 279 var dy = y - (top + height / 2); | |
| 280 | |
| 281 // Unsigned distance to each edge. | |
| 282 var distx = Math.abs(dx) - width / 2; | |
| 283 var disty = Math.abs(dy) - height / 2; | |
| 284 | |
| 285 if (distx > disty) { | |
| 286 if (dx < 0) | |
| 287 return chrome.system.display.LayoutPosition.LEFT; | |
| 288 else | |
| 289 return chrome.system.display.LayoutPosition.RIGHT; | |
| 290 } else { | |
| 291 if (dy < 0) | |
| 292 return chrome.system.display.LayoutPosition.TOP; | |
| 293 else | |
| 294 return chrome.system.display.LayoutPosition.BOTTOM; | |
| 295 } | |
| 296 }, | |
| 297 | |
| 298 /** | |
| 299 * Modifes |bounds| to the position closest to it along the edge of |parentId| | |
| 300 * specified by |layoutPosition|. | |
| 301 * @param {string} parentId | |
| 302 * @param {!chrome.system.display.LayoutPosition} layoutPosition | |
| 303 * @param {!chrome.system.display.Bounds} bounds | |
| 304 */ | |
| 305 snapBounds_: function(parentId, layoutPosition, bounds) { | |
| 306 var parentBounds = this.getCalculatedDisplayBounds(parentId); | |
| 307 | |
| 308 var x; | |
| 309 if (layoutPosition == chrome.system.display.LayoutPosition.LEFT) { | |
| 310 x = parentBounds.left - bounds.width; | |
| 311 } else if (layoutPosition == chrome.system.display.LayoutPosition.RIGHT) { | |
| 312 x = parentBounds.left + parentBounds.width; | |
| 313 } else { | |
| 314 x = this.snapToX_(bounds, parentBounds); | |
| 315 } | |
| 316 | |
| 317 var y; | |
| 318 if (layoutPosition == chrome.system.display.LayoutPosition.TOP) { | |
| 319 y = parentBounds.top - bounds.height; | |
| 320 } else if (layoutPosition == chrome.system.display.LayoutPosition.BOTTOM) { | |
| 321 y = parentBounds.top + parentBounds.height; | |
| 322 } else { | |
| 323 y = this.snapToY_(bounds, parentBounds); | |
| 324 } | |
| 325 | |
| 326 bounds.left = x; | |
| 327 bounds.top = y; | |
| 328 }, | |
| 329 | |
| 330 /** | |
| 331 * Snaps a horizontal value, see snapToEdge. | |
| 332 * @param {!chrome.system.display.Bounds} newBounds | |
| 333 * @param {!chrome.system.display.Bounds} parentBounds | |
| 334 * @param {number=} opt_snapDistance Provide to override the snap distance. | |
| 335 * 0 means snap from any distance. | |
| 336 * @return {number} | |
| 337 */ | |
| 338 snapToX_: function(newBounds, parentBounds, opt_snapDistance) { | |
| 339 return this.snapToEdge_( | |
| 340 newBounds.left, newBounds.width, parentBounds.left, parentBounds.width, | |
| 341 opt_snapDistance); | |
| 342 }, | |
| 343 | |
| 344 /** | |
| 345 * Snaps a vertical value, see snapToEdge. | |
| 346 * @param {!chrome.system.display.Bounds} newBounds | |
| 347 * @param {!chrome.system.display.Bounds} parentBounds | |
| 348 * @param {number=} opt_snapDistance Provide to override the snap distance. | |
| 349 * 0 means snap from any distance. | |
| 350 * @return {number} | |
| 351 */ | |
| 352 snapToY_: function(newBounds, parentBounds, opt_snapDistance) { | |
| 353 return this.snapToEdge_( | |
| 354 newBounds.top, newBounds.height, parentBounds.top, parentBounds.height, | |
| 355 opt_snapDistance); | |
| 356 }, | |
| 357 | |
| 358 /** | |
| 359 * Snaps the region [point, width] to [basePoint, baseWidth] if | |
| 360 * the [point, width] is close enough to the base's edge. | |
| 361 * @param {number} point The starting point of the region. | |
| 362 * @param {number} width The width of the region. | |
| 363 * @param {number} basePoint The starting point of the base region. | |
| 364 * @param {number} baseWidth The width of the base region. | |
| 365 * @param {number=} opt_snapDistance Provide to override the snap distance. | |
| 366 * 0 means snap at any distance. | |
| 367 * @return {number} The moved point. Returns the point itself if it doesn't | |
| 368 * need to snap to the edge. | |
| 369 * @private | |
| 370 */ | |
| 371 snapToEdge_: function(point, width, basePoint, baseWidth, opt_snapDistance) { | |
| 372 // If the edge of the region is smaller than this, it will snap to the | |
| 373 // base's edge. | |
| 374 /** @const */ var SNAP_DISTANCE_PX = 16; | |
| 375 var snapDist = | |
| 376 (opt_snapDistance !== undefined) ? opt_snapDistance : SNAP_DISTANCE_PX; | |
| 377 | |
| 378 var startDiff = Math.abs(point - basePoint); | |
| 379 var endDiff = Math.abs(point + width - (basePoint + baseWidth)); | |
| 380 // Prefer the closer one if both edges are close enough. | |
| 381 if ((!snapDist || startDiff < snapDist) && startDiff < endDiff) | |
| 382 return basePoint; | |
| 383 else if (!snapDist || endDiff < snapDist) | |
| 384 return basePoint + baseWidth - width; | |
| 385 | |
| 386 return point; | |
| 387 }, | |
| 388 | |
| 389 /** | |
| 390 * Intersects |layout| with each other layout and reduces |deltaPos| to | |
| 391 * avoid any collisions (or sets it to [0,0] if the display can not be moved | |
| 392 * in the direction of |deltaPos|). | |
| 393 * @param {string} id | |
| 394 * @param {!chrome.system.display.Bounds} bounds | |
| 395 * @param {!{x: number, y: number}} deltaPos | |
| 396 */ | |
| 397 collideAndModifyDelta_: function(id, bounds, deltaPos) { | |
| 398 var keys = this.calculatedBoundsMap_.keys(); | |
| 399 var others = new Set(keys); | |
| 400 others.delete(id); | |
| 401 var checkCollisions = true; | |
| 402 while (checkCollisions) { | |
| 403 checkCollisions = false; | |
| 404 for (let otherId of others) { | |
| 405 var otherBounds = this.getCalculatedDisplayBounds(otherId); | |
| 406 if (this.collideWithBoundsAndModifyDelta_( | |
| 407 bounds, otherBounds, deltaPos)) { | |
| 408 if (deltaPos.x == 0 && deltaPos.y == 0) | |
| 409 return; | |
| 410 others.delete(otherId); | |
| 411 checkCollisions = true; | |
| 412 break; | |
| 413 } | |
| 414 } | |
| 415 } | |
| 416 }, | |
| 417 | |
| 418 /** | |
| 419 * Intersects |bounds| with |otherBounds|. If there is a collision, modifies | |
| 420 * |deltaPos| to limit movement to a single axis and avoid the collision | |
|
michaelpg
2016/06/29 16:42:08
add a comment that this happens after snapping, ie
stevenjb
2016/06/29 22:34:41
Done.
| |
| 421 * and returns true. | |
| 422 * @param {!chrome.system.display.Bounds} bounds | |
| 423 * @param {!chrome.system.display.Bounds} otherBounds | |
| 424 * @param {!{x: number, y: number}} deltaPos | |
| 425 * @return {boolean} Whether there was a collision. | |
| 426 */ | |
| 427 collideWithBoundsAndModifyDelta_: function(bounds, otherBounds, deltaPos) { | |
| 428 var newX = bounds.left + deltaPos.x; | |
| 429 var newY = bounds.top + deltaPos.y; | |
| 430 | |
| 431 if ((newX + bounds.width <= otherBounds.left) || | |
| 432 (newX >= otherBounds.left + otherBounds.width) || | |
| 433 (newY + bounds.height <= otherBounds.top) || | |
| 434 (newY >= otherBounds.top + otherBounds.height)) { | |
| 435 return false; | |
| 436 } | |
| 437 | |
| 438 // We restrict the delta to X or Y, and limit the delta to stay outside | |
| 439 // the bounds, however we do not change the sign of the delta, i.e. we do | |
| 440 // not "push" the point outside the bounds if already inside. | |
| 441 if (Math.abs(deltaPos.x) > Math.abs(deltaPos.y)) { | |
| 442 deltaPos.y = 0; | |
| 443 let snapDeltaX; | |
| 444 if (deltaPos.x > 0) { | |
| 445 let x = otherBounds.left - bounds.width; | |
| 446 snapDeltaX = Math.max(0, x - bounds.left); | |
| 447 } else { | |
| 448 let x = otherBounds.left + otherBounds.width; | |
| 449 snapDeltaX = Math.min(x - bounds.left, 0); | |
| 450 } | |
| 451 deltaPos.x = snapDeltaX; | |
| 452 } else { | |
| 453 deltaPos.x = 0; | |
| 454 let snapDeltaY; | |
| 455 if (deltaPos.y > 0) { | |
| 456 let y = otherBounds.top - bounds.height; | |
| 457 snapDeltaY = Math.min(0, y - bounds.top); | |
| 458 } else if (deltaPos.y < 0) { | |
| 459 let y = otherBounds.top + otherBounds.top; | |
|
michaelpg
2016/06/29 16:42:08
height?
stevenjb
2016/06/29 22:34:41
Doh! Thanks. Done.
| |
| 460 snapDeltaY = Math.max(y - bounds.top, 0); | |
| 461 } else { | |
| 462 snapDeltaY = 0; | |
| 463 } | |
| 464 deltaPos.y = snapDeltaY; | |
| 465 } | |
| 466 | |
| 467 return true; | |
| 468 }, | |
| 469 | |
| 470 /** | |
| 471 * Updates the offset for |layout| from |bounds|. | |
| 472 * @param {!chrome.system.display.Bounds} bounds | |
| 473 * @param {!chrome.system.display.LayoutPosition} position | |
| 474 * @param {!chrome.system.display.DisplayLayout} layout | |
| 475 */ | |
| 476 updateOffsetAndPosition_: function(bounds, position, layout) { | |
| 477 layout.position = position; | |
| 478 if (!layout.parentId) { | |
| 479 layout.offset = 0; | |
| 480 return; | |
| 481 } | |
| 482 | |
| 483 // Offset is calculated from top or left edge. | |
| 484 var parentBounds = this.getCalculatedDisplayBounds(layout.parentId); | |
| 485 var offset, minOffset, maxOffset; | |
| 486 if (position == chrome.system.display.LayoutPosition.LEFT || | |
| 487 position == chrome.system.display.LayoutPosition.RIGHT) { | |
| 488 offset = bounds.top - parentBounds.top; | |
| 489 minOffset = -bounds.height; | |
| 490 maxOffset = parentBounds.height; | |
| 491 } else { | |
| 492 offset = bounds.left - parentBounds.left; | |
| 493 minOffset = -bounds.width; | |
| 494 maxOffset = parentBounds.width; | |
| 495 } | |
| 496 /** @const */ var MIN_OFFSET_OVERLAP = 50; | |
| 497 minOffset += MIN_OFFSET_OVERLAP; | |
| 498 maxOffset -= MIN_OFFSET_OVERLAP; | |
| 499 layout.offset = Math.max(minOffset, Math.min(offset, maxOffset)); | |
| 500 | |
| 501 // Update the calculated bounds to match the new offset. | |
| 502 this.calculateBounds_(layout.id, bounds.width, bounds.height); | |
| 503 }, | |
| 504 | |
| 505 /** | |
| 506 * Highlights the edge of the div associated with |id| based on | |
| 507 * |layoutPosition| and removes any other highlights. If |layoutPosition| is | |
| 508 * undefined, removes all highlights. | |
| 509 * @param {string} id | |
| 510 * @param {chrome.system.display.LayoutPosition|undefined} layoutPosition | |
| 511 * @private | |
| 512 */ | |
| 513 highlightEdge_: function(id, layoutPosition) { | |
| 514 for (let layout of this.layouts) { | |
| 515 var highlight = (layout.id == id) ? layoutPosition : undefined; | |
| 516 var div = this.$$('#_' + layout.id); | |
| 517 div.classList.toggle( | |
| 518 'highlight-right', | |
| 519 highlight == chrome.system.display.LayoutPosition.RIGHT); | |
| 520 div.classList.toggle( | |
| 521 'highlight-left', | |
| 522 highlight == chrome.system.display.LayoutPosition.LEFT); | |
| 523 div.classList.toggle( | |
| 524 'highlight-top', | |
| 525 highlight == chrome.system.display.LayoutPosition.TOP); | |
| 526 div.classList.toggle( | |
| 527 'highlight-bottom', | |
| 528 highlight == chrome.system.display.LayoutPosition.BOTTOM); | |
| 529 } | |
| 530 }, | |
| 531 }; | |
| OLD | NEW |