1<!-- 2 Copyright (C) 2012 Google Inc. All rights reserved. 3 4 Redistribution and use in source and binary forms, with or without 5 modification, are permitted provided that the following conditions 6 are met: 7 8 1. Redistributions of source code must retain the above copyright 9 notice, this list of conditions and the following disclaimer. 10 2. Redistributions in binary form must reproduce the above copyright 11 notice, this list of conditions and the following disclaimer in the 12 documentation and/or other materials provided with the distribution. 13 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 its contributors may be used to endorse or promote products derived 15 from this software without specific prior written permission. 16 17 THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27--> 28<!DOCTYPE html> 29<html> 30<head> 31<style> 32body { 33 margin: 0; 34 padding: 0; 35} 36 37body.platform-mac { 38 font-size: 11px; 39 font-family: Menlo, Monaco; 40} 41 42body.platform-windows { 43 font-size: 12px; 44 font-family: Consolas, Lucida Console; 45} 46 47body.platform-linux { 48 font-size: 11px; 49 font-family: dejavu sans mono; 50} 51 52.fill { 53 position: absolute; 54 top: 0; 55 right: 0; 56 bottom: 0; 57 left: 0; 58} 59 60.dimmed { 61 background-color: rgba(0, 0, 0, 0.31); 62} 63 64#canvas { 65 pointer-events: none; 66} 67 68.controls-line { 69 display: flex; 70 justify-content: center; 71 margin: 10px 0; 72} 73 74.message-box { 75 padding: 2px 4px; 76 display: flex; 77 align-items: center; 78 cursor: default; 79} 80 81.controls-line > * { 82 background-color: rgb(255, 255, 194); 83 border: 1px solid rgb(202, 202, 202); 84 height: 22px; 85 box-sizing: border-box; 86} 87 88.controls-line .button { 89 width: 26px; 90 margin-left: -1px; 91 margin-right: 0; 92 padding: 0; 93} 94 95.controls-line .button:hover { 96 cursor: pointer; 97} 98 99.controls-line .button .glyph { 100 width: 100%; 101 height: 100%; 102 background-color: rgba(0, 0, 0, 0.75); 103 opacity: 0.8; 104 -webkit-mask-repeat: no-repeat; 105 -webkit-mask-position: center; 106 position: relative; 107} 108 109.controls-line .button:active .glyph { 110 top: 1px; 111 left: 1px; 112} 113 114#resume-button .glyph { 115 -webkit-mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAAKCAYAAABv7tTEAAAAAXNSR0IArs4c6QAAAFJJREFUKM+10bEJgGAMBeEPbR3BLRzEVdzEVRzELRzBVohVwEJ+iODBlQfhBeJhsmHU4C0KnFjQV6J0x1SNAhdWDJUoPTB3PvLLeaUhypM3n3sD/qc7lDrdpIEAAAAASUVORK5CYII=); 116 -webkit-mask-size: 13px 10px; 117 background-color: rgb(66, 129, 235); 118} 119 120#step-over-button .glyph { 121 -webkit-mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAAKCAYAAAC5Sw6hAAAAAXNSR0IArs4c6QAAAOFJREFUKM+N0j8rhXEUB/DPcxW35CqhvIBrtqibkklhV8qkTHe4ZbdblcXgPVhuMdqUTUl5A2KRRCF5LGc4PT1P7qnfcr5/zu/8KdTHLFaxjHnc4RZXKI0QYxjgLQTVd42l/0wmg5iFX3iq5H6w22RS4DyRH7CB8cAXcBTGJT6xUmd0mEwuMdFQcA3fwXvGTAan8BrgPabTL9fRRyfx91PRMwyjGwcJ2EyCfsrfpPw2Pipz24NT/MZciiQYVshzOKnZ5Hturxt3k2MnCpS4SPkeHpPR8Sh3tYgttBoW9II2/AHiaEqvD2Fc0wAAAABJRU5ErkJggg==); 122 -webkit-mask-size: 18px 10px; 123} 124 125.px { 126 color: rgb(128, 128, 128); 127} 128 129#element-title { 130 position: absolute; 131 z-index: 10; 132} 133 134#tag-name { 135 /* Keep this in sync with view-source.css (.html-tag) */ 136 color: rgb(136, 18, 128); 137} 138 139#node-id { 140 /* Keep this in sync with view-source.css (.html-attribute-value) */ 141 color: rgb(26, 26, 166); 142} 143 144#class-name { 145 /* Keep this in sync with view-source.css (.html-attribute-name) */ 146 color: rgb(153, 69, 0); 147} 148 149</style> 150<script> 151const lightGridColor = "rgba(0,0,0,0.2)"; 152const darkGridColor = "rgba(0,0,0,0.7)"; 153const transparentColor = "rgba(0, 0, 0, 0)"; 154const gridBackgroundColor = "rgba(255, 255, 255, 0.8)"; 155 156function drawPausedInDebuggerMessage(message) 157{ 158 window._controlsVisible = true; 159 document.querySelector(".controls-line").style.visibility = "visible"; 160 document.getElementById("paused-in-debugger").textContent = message; 161 document.body.classList.add("dimmed"); 162} 163 164function _drawGrid(rulerAtRight, rulerAtBottom) 165{ 166 if (window._gridPainted) 167 return; 168 window._gridPainted = true; 169 170 context.save(); 171 172 var pageFactor = pageZoomFactor * pageScaleFactor; 173 function zoom(x) 174 { 175 return Math.round(x * pageFactor); 176 } 177 function unzoom(x) 178 { 179 return Math.round(x / pageFactor); 180 } 181 182 var width = canvasWidth / pageFactor; 183 var height = canvasHeight / pageFactor; 184 185 const gridSubStep = 5; 186 const gridStep = 50; 187 188 { 189 // Draw X grid background 190 context.save(); 191 context.fillStyle = gridBackgroundColor; 192 if (rulerAtBottom) 193 context.fillRect(0, zoom(height) - 15, zoom(width), zoom(height)); 194 else 195 context.fillRect(0, 0, zoom(width), 15); 196 197 // Clip out backgrounds intersection 198 context.globalCompositeOperation = "destination-out"; 199 context.fillStyle = "red"; 200 if (rulerAtRight) 201 context.fillRect(zoom(width) - 15, 0, zoom(width), zoom(height)); 202 else 203 context.fillRect(0, 0, 15, zoom(height)); 204 context.restore(); 205 206 // Draw Y grid background 207 context.fillStyle = gridBackgroundColor; 208 if (rulerAtRight) 209 context.fillRect(zoom(width) - 15, 0, zoom(width), zoom(height)); 210 else 211 context.fillRect(0, 0, 15, zoom(height)); 212 } 213 214 context.lineWidth = 1; 215 context.strokeStyle = darkGridColor; 216 context.fillStyle = darkGridColor; 217 { 218 // Draw labels. 219 context.save(); 220 context.translate(-scrollX, 0.5 - scrollY); 221 var maxY = height + unzoom(scrollY); 222 for (var y = 2 * gridStep; y < maxY; y += 2 * gridStep) { 223 context.save(); 224 context.translate(scrollX, zoom(y)); 225 context.rotate(-Math.PI / 2); 226 context.fillText(y, 2, rulerAtRight ? zoom(width) - 7 : 13); 227 context.restore(); 228 } 229 context.translate(0.5, -0.5); 230 var maxX = width + unzoom(scrollX); 231 for (var x = 2 * gridStep; x < maxX; x += 2 * gridStep) { 232 context.save(); 233 context.fillText(x, zoom(x) + 2, rulerAtBottom ? scrollY + zoom(height) - 7 : scrollY + 13); 234 context.restore(); 235 } 236 context.restore(); 237 } 238 239 { 240 // Draw vertical grid 241 context.save(); 242 if (rulerAtRight) { 243 context.translate(zoom(width), 0); 244 context.scale(-1, 1); 245 } 246 context.translate(-scrollX, 0.5 - scrollY); 247 var maxY = height + unzoom(scrollY); 248 for (var y = gridStep; y < maxY; y += gridStep) { 249 context.beginPath(); 250 context.moveTo(scrollX, zoom(y)); 251 var markLength = (y % (gridStep * 2)) ? 5 : 8; 252 context.lineTo(scrollX + markLength, zoom(y)); 253 context.stroke(); 254 } 255 context.strokeStyle = lightGridColor; 256 for (var y = gridSubStep; y < maxY; y += gridSubStep) { 257 if (!(y % gridStep)) 258 continue; 259 context.beginPath(); 260 context.moveTo(scrollX, zoom(y)); 261 context.lineTo(scrollX + gridSubStep, zoom(y)); 262 context.stroke(); 263 } 264 context.restore(); 265 } 266 267 { 268 // Draw horizontal grid 269 context.save(); 270 if (rulerAtBottom) { 271 context.translate(0, zoom(height)); 272 context.scale(1, -1); 273 } 274 context.translate(0.5 - scrollX, -scrollY); 275 var maxX = width + unzoom(scrollX); 276 for (var x = gridStep; x < maxX; x += gridStep) { 277 context.beginPath(); 278 context.moveTo(zoom(x), scrollY); 279 var markLength = (x % (gridStep * 2)) ? 5 : 8; 280 context.lineTo(zoom(x), scrollY + markLength); 281 context.stroke(); 282 } 283 context.strokeStyle = lightGridColor; 284 for (var x = gridSubStep; x < maxX; x += gridSubStep) { 285 if (!(x % gridStep)) 286 continue; 287 context.beginPath(); 288 context.moveTo(zoom(x), scrollY); 289 context.lineTo(zoom(x), scrollY + gridSubStep); 290 context.stroke(); 291 } 292 context.restore(); 293 } 294 295 context.restore(); 296} 297 298function drawViewSize(drawViewSizeWithGrid) 299{ 300 var drawGridBoolean = drawViewSizeWithGrid === "true"; 301 var text = viewportSize.width + "px \u00D7 " + viewportSize.height + "px"; 302 context.save(); 303 context.font = "20px "; 304 switch (platform) { 305 case "windows": context.font = "14px Consolas"; break; 306 case "mac": context.font = "14px Menlo"; break; 307 case "linux": context.font = "14px dejavu sans mono"; break; 308 } 309 310 var frameWidth = canvasWidth; 311 var textWidth = context.measureText(text).width; 312 context.fillStyle = gridBackgroundColor; 313 context.fillRect(frameWidth - textWidth - 12, drawGridBoolean ? 15 : 0, frameWidth, 25); 314 context.fillStyle = darkGridColor; 315 context.fillText(text, frameWidth - textWidth - 6, drawGridBoolean ? 33 : 18); 316 context.restore(); 317 if (drawGridBoolean) 318 _drawGrid(); 319} 320 321function reset(resetData) 322{ 323 window.viewportSize = resetData.viewportSize; 324 window.deviceScaleFactor = resetData.deviceScaleFactor; 325 window.pageZoomFactor = resetData.pageZoomFactor; 326 window.pageScaleFactor = resetData.pageScaleFactor; 327 window.scrollX = Math.round(resetData.scrollX * pageScaleFactor); 328 window.scrollY = Math.round(resetData.scrollY * pageScaleFactor); 329 330 window.canvas = document.getElementById("canvas"); 331 window.context = canvas.getContext("2d"); 332 333 canvas.width = deviceScaleFactor * viewportSize.width; 334 canvas.height = deviceScaleFactor * viewportSize.height; 335 canvas.style.width = viewportSize.width + "px"; 336 canvas.style.height = viewportSize.height + "px"; 337 context.scale(deviceScaleFactor, deviceScaleFactor); 338 window.canvasWidth = viewportSize.width; 339 window.canvasHeight = viewportSize.height; 340 341 window._controlsVisible = false; 342 document.querySelector(".controls-line").style.visibility = "hidden"; 343 document.getElementById("element-title").style.visibility = "hidden"; 344 document.body.classList.remove("dimmed"); 345 window._gridPainted = false; 346} 347 348function _drawElementTitle(elementInfo, bounds) 349{ 350 document.getElementById("tag-name").textContent = elementInfo.tagName; 351 document.getElementById("node-id").textContent = elementInfo.idValue ? "#" + elementInfo.idValue : ""; 352 var className = elementInfo.className; 353 if (className && className.length > 50) 354 className = className.substring(0, 50) + "\u2026"; 355 document.getElementById("class-name").textContent = className || ""; 356 document.getElementById("node-width").textContent = elementInfo.nodeWidth; 357 document.getElementById("node-height").textContent = elementInfo.nodeHeight; 358 var elementTitle = document.getElementById("element-title"); 359 360 var titleWidth = elementTitle.offsetWidth + 6; 361 var titleHeight = elementTitle.offsetHeight + 4; 362 363 var anchorTop = bounds.minY; 364 var anchorBottom = bounds.maxY; 365 var anchorLeft = bounds.minX; 366 367 const arrowHeight = 7; 368 var renderArrowUp = false; 369 var renderArrowDown = false; 370 371 var boxX = Math.max(2, anchorLeft); 372 if (boxX + titleWidth > canvasWidth) 373 boxX = canvasWidth - titleWidth - 2; 374 375 var boxY; 376 if (anchorTop > canvasHeight) { 377 boxY = canvasHeight - titleHeight - arrowHeight; 378 renderArrowDown = true; 379 } else if (anchorBottom < 0) { 380 boxY = arrowHeight; 381 renderArrowUp = true; 382 } else if (anchorBottom + titleHeight + arrowHeight < canvasHeight) { 383 boxY = anchorBottom + arrowHeight - 4; 384 renderArrowUp = true; 385 } else if (anchorTop - titleHeight - arrowHeight > 0) { 386 boxY = anchorTop - titleHeight - arrowHeight + 3; 387 renderArrowDown = true; 388 } else 389 boxY = arrowHeight; 390 391 context.save(); 392 context.translate(0.5, 0.5); 393 context.beginPath(); 394 context.moveTo(boxX, boxY); 395 if (renderArrowUp) { 396 context.lineTo(boxX + 2 * arrowHeight, boxY); 397 context.lineTo(boxX + 3 * arrowHeight, boxY - arrowHeight); 398 context.lineTo(boxX + 4 * arrowHeight, boxY); 399 } 400 context.lineTo(boxX + titleWidth, boxY); 401 context.lineTo(boxX + titleWidth, boxY + titleHeight); 402 if (renderArrowDown) { 403 context.lineTo(boxX + 4 * arrowHeight, boxY + titleHeight); 404 context.lineTo(boxX + 3 * arrowHeight, boxY + titleHeight + arrowHeight); 405 context.lineTo(boxX + 2 * arrowHeight, boxY + titleHeight); 406 } 407 context.lineTo(boxX, boxY + titleHeight); 408 context.closePath(); 409 context.fillStyle = "rgb(255, 255, 194)"; 410 context.fill(); 411 context.strokeStyle = "rgb(128, 128, 128)"; 412 context.stroke(); 413 414 context.restore(); 415 416 elementTitle.style.visibility = "visible"; 417 elementTitle.style.top = (boxY + 3) + "px"; 418 elementTitle.style.left = (boxX + 3) + "px"; 419} 420 421function _drawRulers(bounds, rulerAtRight, rulerAtBottom) 422{ 423 context.save(); 424 var width = canvasWidth; 425 var height = canvasHeight; 426 context.strokeStyle = "rgba(128, 128, 128, 0.3)"; 427 context.lineWidth = 1; 428 context.translate(0.5, 0.5); 429 430 if (rulerAtRight) { 431 for (var y in bounds.rightmostXForY) { 432 context.beginPath(); 433 context.moveTo(width, y); 434 context.lineTo(bounds.rightmostXForY[y], y); 435 context.stroke(); 436 } 437 } else { 438 for (var y in bounds.leftmostXForY) { 439 context.beginPath(); 440 context.moveTo(0, y); 441 context.lineTo(bounds.leftmostXForY[y], y); 442 context.stroke(); 443 } 444 } 445 446 if (rulerAtBottom) { 447 for (var x in bounds.bottommostYForX) { 448 context.beginPath(); 449 context.moveTo(x, height); 450 context.lineTo(x, bounds.topmostYForX[x]); 451 context.stroke(); 452 } 453 } else { 454 for (var x in bounds.topmostYForX) { 455 context.beginPath(); 456 context.moveTo(x, 0); 457 context.lineTo(x, bounds.topmostYForX[x]); 458 context.stroke(); 459 } 460 } 461 462 context.restore(); 463} 464 465function drawPath(commands, fillColor, outlineColor, bounds) 466{ 467 var commandsIndex = 0; 468 469 function extractPoints(count) 470 { 471 var points = []; 472 473 for (var i = 0; i < count; ++i) { 474 var x = Math.round(commands[commandsIndex++] * pageScaleFactor); 475 bounds.maxX = Math.max(bounds.maxX, x); 476 bounds.minX = Math.min(bounds.minX, x); 477 478 var y = Math.round(commands[commandsIndex++] * pageScaleFactor); 479 bounds.maxY = Math.max(bounds.maxY, y); 480 bounds.minY = Math.min(bounds.minY, y); 481 482 bounds.leftmostXForY[y] = Math.min(bounds.leftmostXForY[y] || Number.MAX_VALUE, x); 483 bounds.rightmostXForY[y] = Math.max(bounds.rightmostXForY[y] || Number.MIN_VALUE, x); 484 bounds.topmostYForX[x] = Math.min(bounds.topmostYForX[x] || Number.MAX_VALUE, y); 485 bounds.bottommostYForX[x] = Math.max(bounds.bottommostYForX[x] || Number.MIN_VALUE, y); 486 487 points.push(x, y); 488 } 489 return points; 490 } 491 492 context.save(); 493 context.beginPath(); 494 495 var commandsLength = commands.length; 496 while (commandsIndex < commandsLength) { 497 switch (commands[commandsIndex++]) { 498 case "M": 499 context.moveTo.apply(context, extractPoints(1)); 500 break; 501 case "L": 502 context.lineTo.apply(context, extractPoints(1)); 503 break; 504 case "C": 505 context.bezierCurveTo.apply(context, extractPoints(3)); 506 break; 507 case "Q": 508 context.quadraticCurveTo.apply(context, extractPoints(2)); 509 break; 510 case "Z": 511 context.closePath(); 512 break; 513 } 514 } 515 context.closePath(); 516 context.lineWidth = 0; 517 context.fillStyle = fillColor; 518 context.fill(); 519 520 if (outlineColor) { 521 context.lineWidth = 2; 522 context.strokeStyle = outlineColor; 523 context.stroke(); 524 } 525 526 context.restore(); 527} 528 529function drawHighlight(highlight) 530{ 531 context.save(); 532 533 var bounds = { 534 minX: Number.MAX_VALUE, 535 minY: Number.MAX_VALUE, 536 maxX: Number.MIN_VALUE, 537 maxY: Number.MIN_VALUE, 538 leftmostXForY: {}, 539 rightmostXForY: {}, 540 topmostYForX: {}, 541 bottommostYForX: {} 542 }; 543 544 for (var paths = highlight.paths.slice(); paths.length;) { 545 var path = paths.pop(); 546 drawPath(path.path, path.fillColor, path.outlineColor, bounds); 547 if (paths.length) { 548 context.save(); 549 context.globalCompositeOperation = "destination-out"; 550 drawPath(paths[paths.length - 1].path, "red", null, bounds); 551 context.restore(); 552 } 553 } 554 555 var rulerAtRight = highlight.paths.length && highlight.showRulers && bounds.minX < 20 && bounds.maxX + 20 < canvasWidth; 556 var rulerAtBottom = highlight.paths.length && highlight.showRulers && bounds.minY < 20 && bounds.maxY + 20 < canvasHeight; 557 558 if (highlight.showRulers) 559 _drawGrid(rulerAtRight, rulerAtBottom); 560 561 if (highlight.paths.length) { 562 if (highlight.showExtensionLines) 563 _drawRulers(bounds, rulerAtRight, rulerAtBottom); 564 565 if (highlight.elementInfo) 566 _drawElementTitle(highlight.elementInfo, bounds); 567 } 568 context.restore(); 569} 570 571function setPlatform(platform) 572{ 573 window.platform = platform; 574 document.body.classList.add("platform-" + platform); 575} 576 577function dispatch(message) 578{ 579 var functionName = message.shift(); 580 window[functionName].apply(null, message); 581} 582 583function log(text) 584{ 585 var logEntry = document.createElement("div"); 586 logEntry.textContent = text; 587 document.getElementById("log").appendChild(logEntry); 588} 589 590function onResumeClick() 591{ 592 InspectorOverlayHost.resume(); 593} 594 595function onStepOverClick() 596{ 597 InspectorOverlayHost.stepOver(); 598} 599 600function onLoaded() 601{ 602 document.getElementById("resume-button").addEventListener("click", onResumeClick); 603 document.getElementById("step-over-button").addEventListener("click", onStepOverClick); 604} 605 606function eventHasCtrlOrMeta(event) 607{ 608 return window.platform == "mac" ? (event.metaKey && !event.ctrlKey) : (event.ctrlKey && !event.metaKey); 609} 610 611function onDocumentKeyDown(event) 612{ 613 if (!window._controlsVisible) 614 return; 615 if (event.keyIdentifier == "F8" || eventHasCtrlOrMeta(event) && event.keyCode == 220 /* backslash */) 616 InspectorOverlayHost.resume(); 617 else if (event.keyIdentifier == "F10" || eventHasCtrlOrMeta(event) && event.keyCode == 222 /* single quote */) 618 InspectorOverlayHost.stepOver(); 619} 620 621window.addEventListener("DOMContentLoaded", onLoaded); 622document.addEventListener("keydown", onDocumentKeyDown); 623 624</script> 625</head> 626<body class="fill"> 627<div class="controls-line"> 628 <div class="message-box"><div id="paused-in-debugger"></div></div> 629 <div class="button" id="resume-button" title="Resume script execution (F8)."><div class="glyph"></div></div> 630 <div class="button" id="step-over-button" title="Step over next function call (F10)."><div class="glyph"></div></div> 631</div> 632</body> 633<canvas id="canvas" class="fill"></canvas> 634<div id="element-title"> 635 <span id="tag-name"></span><span id="node-id"></span><span id="class-name"></span> 636 <span id="node-width"></span><span class="px">px</span><span class="px"> × </span><span id="node-height"></span><span class="px">px</span> 637</div> 638<div id="log"></div> 639</html> 640