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"> &#xD7; </span><span id="node-height"></span><span class="px">px</span>
637</div>
638<div id="log"></div>
639</html>
640