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.message-line {
65    margin: 10px 0;
66    text-align: center;
67}
68
69.message-box {
70    background-color: rgb(255, 255, 194);
71    border: 1px solid rgb(128, 128, 128);
72    display: inline-block;
73    padding: 2px 4px;
74}
75
76.px {
77    color: rgb(128, 128, 128);
78}
79
80#element-title {
81    position: absolute;
82    z-index: 10;
83}
84
85#tag-name {
86    /* Keep this in sync with view-source.css (.webkit-html-tag-name) */
87    color: rgb(136, 18, 128);
88}
89
90#node-id {
91    /* Keep this in sync with view-source.css (.webkit-html-attribute-value) */
92    color: rgb(26, 26, 166);
93}
94
95#class-name {
96    /* Keep this in sync with view-source.css (.webkit-html-attribute-name) */
97    color: rgb(153, 69, 0);
98}
99
100#right-gutter {
101    display: none;
102    right: 0;
103    top: 0;
104    bottom: 0;
105    position: absolute;
106    background-color: darkgray;
107}
108
109#bottom-gutter {
110    display: none;
111    left: 0;
112    right: 0;
113    bottom: 0;
114    position: absolute;
115    background-color: darkgray;
116}
117</style>
118<script>
119const lightGridColor = "rgba(0,0,0,0.2)";
120const darkGridColor = "rgba(0,0,0,0.5)";
121const transparentColor = "rgba(0, 0, 0, 0)";
122const gridBackgroundColor = "rgba(255, 255, 255, 0.6)";
123
124function drawPausedInDebuggerMessage(message)
125{
126    var pausedInDebugger = document.getElementById("paused-in-debugger");
127    pausedInDebugger.textContent = message;
128    pausedInDebugger.style.visibility = "visible";
129    document.body.classList.add("dimmed");
130}
131
132function _drawGrid(highlight, rulerAtRight, rulerAtBottom)
133{
134    if (!highlight.showRulers)
135        return;
136    context.save();
137
138    var width = canvas.width;
139    var height = canvas.height;
140
141    const gridSubStep = 5;
142    const gridStep = 50;
143
144    {
145        // Draw X grid background
146        context.save();
147        context.fillStyle = gridBackgroundColor;
148        if (rulerAtBottom)
149            context.fillRect(0, height - 15, width, height);
150        else
151            context.fillRect(0, 0, width, 15);
152
153        // Clip out backgrounds intersection
154        context.globalCompositeOperation = "destination-out";
155        context.fillStyle = "red";
156        if (rulerAtRight)
157            context.fillRect(width - 15, 0, width, height);
158        else
159            context.fillRect(0, 0, 15, height);
160        context.restore();
161
162        // Draw Y grid background
163        context.fillStyle = gridBackgroundColor;
164        if (rulerAtRight)
165            context.fillRect(width - 15, 0, width, height);
166        else
167            context.fillRect(0, 0, 15, height);
168    }
169
170    context.lineWidth = 1;
171    context.strokeStyle = darkGridColor;
172    context.fillStyle = darkGridColor;
173    {
174        // Draw labels.
175        context.save();
176        context.translate(-highlight.scrollX, 0.5 - highlight.scrollY);
177        for (var y = 2 * gridStep; y < height + highlight.scrollY; y += 2 * gridStep) {
178            context.save();
179            context.translate(highlight.scrollX, y);
180            context.rotate(-Math.PI / 2);
181            context.fillText(y, 2, rulerAtRight ? width - 7 : 13);
182            context.restore();
183        }
184        context.translate(0.5, -0.5);
185        for (var x = 2 * gridStep; x < width + highlight.scrollX; x += 2 * gridStep) {
186            context.save();
187            context.fillText(x, x + 2, rulerAtBottom ? highlight.scrollY + height - 7 : highlight.scrollY + 13);
188            context.restore();
189        }
190        context.restore();
191    }
192
193    {   
194        // Draw vertical grid
195        context.save();
196        if (rulerAtRight) {
197            context.translate(width, 0);
198            context.scale(-1, 1);
199        }
200        context.translate(-highlight.scrollX, 0.5 - highlight.scrollY);
201        for (var y = gridStep; y < height + highlight.scrollY; y += gridStep) {
202            context.beginPath();
203            context.moveTo(highlight.scrollX, y);
204            var markLength = (y % (gridStep * 2)) ? 5 : 8;
205            context.lineTo(highlight.scrollX + markLength, y);
206            context.stroke();
207        }  
208        context.strokeStyle = lightGridColor;
209        for (var y = gridSubStep; y < highlight.scrollY + height; y += gridSubStep) {
210            if (!(y % gridStep))
211                continue;
212            context.beginPath();
213            context.moveTo(highlight.scrollX, y);
214            context.lineTo(highlight.scrollX + gridSubStep, y);
215            context.stroke();
216        }
217        context.restore();
218    }
219
220    {
221        // Draw horizontal grid
222        context.save();
223        if (rulerAtBottom) {
224            context.translate(0, height);
225            context.scale(1, -1);
226        }
227        context.translate(0.5 - highlight.scrollX, -highlight.scrollY);
228        for (var x = gridStep; x < width + highlight.scrollX; x += gridStep) {
229            context.beginPath();
230            context.moveTo(x, highlight.scrollY);
231            var markLength = (x % (gridStep * 2)) ? 5 : 8;
232            context.lineTo(x, highlight.scrollY + markLength);
233            context.stroke();
234        }
235        context.strokeStyle = lightGridColor;
236        for (var x = gridSubStep; x < highlight.scrollX + width; x += gridSubStep) {
237            if (!(x % gridStep))
238                continue;
239            context.beginPath();
240            context.moveTo(x, highlight.scrollY);
241            context.lineTo(x, highlight.scrollY + gridSubStep);
242            context.stroke();
243        }
244        context.restore();
245    }
246
247    context.restore();
248}
249
250function quadToPath(quad)
251{
252    context.beginPath();
253    context.moveTo(quad[0].x, quad[0].y);
254    context.lineTo(quad[1].x, quad[1].y);
255    context.lineTo(quad[2].x, quad[2].y);
256    context.lineTo(quad[3].x, quad[3].y);
257    context.closePath();
258    return context;
259}
260
261function drawOutlinedQuad(quad, fillColor, outlineColor)
262{
263    context.save();
264    context.lineWidth = 2;
265    quadToPath(quad).clip();
266    context.fillStyle = fillColor;
267    context.fill();
268    if (outlineColor) {
269        context.strokeStyle = outlineColor;
270        context.stroke();
271    }
272    context.restore();
273}
274
275function drawOutlinedQuadWithClip(quad, clipQuad, fillColor)
276{
277    var canvas = document.getElementById("canvas");
278    context.fillStyle = fillColor;
279    context.save();
280    context.lineWidth = 0;
281    quadToPath(quad).fill();
282    context.globalCompositeOperation = "destination-out";
283    context.fillStyle = "red";
284    quadToPath(clipQuad).fill();
285    context.restore();
286}
287
288function quadEquals(quad1, quad2)
289{
290    return quad1[0].x === quad2[0].x && quad1[0].y === quad2[0].y &&
291        quad1[1].x === quad2[1].x && quad1[1].y === quad2[1].y &&
292        quad1[2].x === quad2[2].x && quad1[2].y === quad2[2].y &&
293        quad1[3].x === quad2[3].x && quad1[3].y === quad2[3].y;
294}
295
296function drawGutter()
297{
298    var frameWidth = frameViewFullSize.width;
299    var frameHeight = frameViewFullSize.height;
300
301    if (!frameWidth || document.body.offsetWidth <= frameWidth)
302        rightGutter.style.removeProperty("display");
303    else {
304        rightGutter.style.display = "block";
305        rightGutter.style.left = frameWidth + "px";
306    }
307
308    if (!frameHeight || document.body.offsetHeight <= frameHeight)
309        bottomGutter.style.removeProperty("display");
310    else {
311        bottomGutter.style.display = "block";
312        bottomGutter.style.top = frameHeight + "px";
313    }
314}
315
316function reset(resetData)
317{
318    var deviceScaleFactor = resetData.deviceScaleFactor;
319    var viewportSize = resetData.viewportSize;
320    window.frameViewFullSize = resetData.frameViewFullSize;
321
322    window.canvas = document.getElementById("canvas");
323    window.context = canvas.getContext("2d");
324    window.rightGutter = document.getElementById("right-gutter");
325    window.bottomGutter = document.getElementById("bottom-gutter");
326
327    canvas.width = deviceScaleFactor * viewportSize.width;
328    canvas.height = deviceScaleFactor * viewportSize.height;
329    canvas.style.width = viewportSize.width + "px";
330    canvas.style.height = viewportSize.height + "px";
331    context.scale(deviceScaleFactor, deviceScaleFactor);
332
333    document.getElementById("paused-in-debugger").style.visibility = "hidden";
334    document.getElementById("element-title").style.visibility = "hidden";
335    document.body.classList.remove("dimmed");
336}
337
338function _drawElementTitle(highlight)
339{
340    var elementInfo = highlight.elementInfo;
341    if (!highlight.elementInfo)
342        return;
343
344    document.getElementById("tag-name").textContent = elementInfo.tagName;
345    document.getElementById("node-id").textContent = elementInfo.idValue ? "#" + elementInfo.idValue : "";
346    var className = elementInfo.className;
347    if (className && className.length > 50)
348       className = className.substring(0, 50) + "\u2026";
349    document.getElementById("class-name").textContent = className || "";
350    document.getElementById("node-width").textContent = elementInfo.nodeWidth;
351    document.getElementById("node-height").textContent = elementInfo.nodeHeight;
352    var elementTitle = document.getElementById("element-title");
353
354    var marginQuad = highlight.quads[0];
355
356    var titleWidth = elementTitle.offsetWidth + 6;
357    var titleHeight = elementTitle.offsetHeight + 4;
358
359    var anchorTop = marginQuad[0].y;
360    var anchorBottom = marginQuad[3].y
361
362    const arrowHeight = 7;
363    var renderArrowUp = false;
364    var renderArrowDown = false;
365
366    var boxX = Math.max(2, marginQuad[0].x);
367    if (boxX + titleWidth > canvas.width)
368        boxX = canvas.width - titleWidth - 2;
369
370    var boxY;
371    if (anchorTop > canvas.height) {
372        boxY = canvas.height - titleHeight - arrowHeight;
373        renderArrowDown = true;
374    } else if (anchorBottom < 0) {
375        boxY = arrowHeight;
376        renderArrowUp = true;
377    } else if (anchorBottom + titleHeight + arrowHeight < canvas.height) {
378        boxY = anchorBottom + arrowHeight - 4;
379        renderArrowUp = true;
380    } else if (anchorTop - titleHeight - arrowHeight > 0) {
381        boxY = anchorTop - titleHeight - arrowHeight + 3;
382        renderArrowDown = true;
383    } else
384        boxY = arrowHeight;
385
386    context.save();
387    context.translate(0.5, 0.5);
388    context.beginPath();
389    context.moveTo(boxX, boxY);
390    if (renderArrowUp) {
391        context.lineTo(boxX + 2 * arrowHeight, boxY);
392        context.lineTo(boxX + 3 * arrowHeight, boxY - arrowHeight);
393        context.lineTo(boxX + 4 * arrowHeight, boxY);
394    }
395    context.lineTo(boxX + titleWidth, boxY);
396    context.lineTo(boxX + titleWidth, boxY + titleHeight);
397    if (renderArrowDown) {
398        context.lineTo(boxX + 4 * arrowHeight, boxY + titleHeight);
399        context.lineTo(boxX + 3 * arrowHeight, boxY + titleHeight + arrowHeight);
400        context.lineTo(boxX + 2 * arrowHeight, boxY + titleHeight);
401    }
402    context.lineTo(boxX, boxY + titleHeight);
403    context.closePath();
404    context.fillStyle = "rgb(255, 255, 194)";
405    context.fill();
406    context.strokeStyle = "rgb(128, 128, 128)";
407    context.stroke();
408
409    context.restore();
410
411    elementTitle.style.visibility = "visible";
412    elementTitle.style.top = (boxY + 3) + "px";
413    elementTitle.style.left = (boxX + 3) + "px";
414}
415
416function _drawRulers(highlight, rulerAtRight, rulerAtBottom)
417{
418    if (!highlight.showRulers)
419        return;
420    context.save();
421    var width = canvas.width;
422    var height = canvas.height;
423    context.strokeStyle = "rgba(128, 128, 128, 0.3)";
424    context.lineWidth = 1;
425    context.translate(0.5, 0.5);
426    var leftmostXForY = {};
427    var rightmostXForY = {};
428    var topmostYForX = {};
429    var bottommostYForX = {};
430
431    for (var i = 0; i < highlight.quads.length; ++i) {
432        var quad = highlight.quads[i];
433        for (var j = 0; j < quad.length; ++j) {
434            var x = quad[j].x;
435            var y = quad[j].y;
436            leftmostXForY[Math.round(y)] = Math.min(leftmostXForY[y] || Number.MAX_VALUE, Math.round(quad[j].x));
437            rightmostXForY[Math.round(y)] = Math.max(rightmostXForY[y] || Number.MIN_VALUE, Math.round(quad[j].x));
438            topmostYForX[Math.round(x)] = Math.min(topmostYForX[x] || Number.MAX_VALUE, Math.round(quad[j].y));
439            bottommostYForX[Math.round(x)] = Math.max(bottommostYForX[x] || Number.MIN_VALUE, Math.round(quad[j].y));
440        }
441    }
442
443    if (rulerAtRight) {
444        for (var y in rightmostXForY) {
445            context.beginPath();
446            context.moveTo(width, y);
447            context.lineTo(rightmostXForY[y], y);
448            context.stroke();
449        }
450    } else {
451        for (var y in leftmostXForY) {
452            context.beginPath();
453            context.moveTo(0, y);
454            context.lineTo(leftmostXForY[y], y);
455            context.stroke();
456        }
457    }
458
459    if (rulerAtBottom) {
460        for (var x in bottommostYForX) {
461            context.beginPath();
462            context.moveTo(x, height);
463            context.lineTo(x, topmostYForX[x]);
464            context.stroke();
465        }
466    } else {
467        for (var x in topmostYForX) {
468            context.beginPath();
469            context.moveTo(x, 0);
470            context.lineTo(x, topmostYForX[x]);
471            context.stroke();
472        }
473    }
474
475    context.restore();
476}
477
478function drawNodeHighlight(highlight)
479{
480    if (!highlight.quads.length) {
481        _drawGrid(highlight, false, false);
482        return;
483    }
484
485    context.save();
486
487    for (var i = 0; i < highlight.quads.length; ++i) {
488        var quad = highlight.quads[i];
489        for (var j = 0; j < quad.length; ++j) {
490            quad[j].x -= highlight.scrollX;
491            quad[j].y -= highlight.scrollY;
492        }
493    }
494
495    var quads = highlight.quads.slice();
496    var contentQuad = quads.pop();
497    var paddingQuad = quads.pop();
498    var borderQuad = quads.pop();
499    var marginQuad = quads.pop();
500
501    var hasContent = contentQuad && highlight.contentColor !== transparentColor || highlight.contentOutlineColor !== transparentColor;
502    var hasPadding = paddingQuad && highlight.paddingColor !== transparentColor;
503    var hasBorder = borderQuad && highlight.borderColor !== transparentColor;
504    var hasMargin = marginQuad && highlight.marginColor !== transparentColor;
505
506    var clipQuad;
507    if (hasMargin && (!hasBorder || !quadEquals(marginQuad, borderQuad))) {
508        drawOutlinedQuadWithClip(marginQuad, borderQuad, highlight.marginColor);
509        clipQuad = borderQuad;
510    }
511    if (hasBorder && (!hasPadding || !quadEquals(borderQuad, paddingQuad))) {
512        drawOutlinedQuadWithClip(borderQuad, paddingQuad, highlight.borderColor);
513        clipQuad = paddingQuad;
514    }
515    if (hasPadding && (!hasContent || !quadEquals(paddingQuad, contentQuad))) {
516        drawOutlinedQuadWithClip(paddingQuad, contentQuad, highlight.paddingColor);
517        clipQuad = contentQuad;
518    }
519    if (hasContent)
520        drawOutlinedQuad(contentQuad, highlight.contentColor, highlight.contentOutlineColor);
521
522    var width = canvas.width;
523    var height = canvas.height;
524    var minX = Number.MAX_VALUE, minY = Number.MAX_VALUE, maxX = Number.MIN_VALUE; maxY = Number.MIN_VALUE;
525    for (var i = 0; i < highlight.quads.length; ++i) {
526        var quad = highlight.quads[i];
527        for (var j = 0; j < quad.length; ++j) {
528            minX = Math.min(minX, quad[j].x);
529            maxX = Math.max(maxX, quad[j].x);
530            minY = Math.min(minY, quad[j].y);
531            maxY = Math.max(maxY, quad[j].y);
532        }
533    }
534
535    var rulerAtRight = minX < 20 && maxX + 20 < width;
536    var rulerAtBottom = minY < 20 && maxY + 20 < height;
537
538    _drawGrid(highlight, rulerAtRight, rulerAtBottom);
539    _drawRulers(highlight, rulerAtRight, rulerAtBottom);
540    _drawElementTitle(highlight);
541    context.restore();
542}
543
544function drawQuadHighlight(highlight)
545{
546    context.save();
547    drawOutlinedQuad(highlight.quads[0], highlight.contentColor, highlight.contentOutlineColor);
548    context.restore();
549}
550
551function setPlatform(platform)
552{
553    document.body.classList.add("platform-" + platform);
554}
555
556function dispatch(message)
557{
558    var functionName = message.shift();
559    window[functionName].apply(null, message);
560}
561
562function log(text)
563{
564    var logEntry = document.createElement("div");
565    logEntry.textContent = text;
566    document.getElementById("log").appendChild(logEntry);
567}
568
569</script>
570</head>
571<body class="fill">
572<div class="message-line"><span class="message-box" id="paused-in-debugger"></span></div>
573</body>
574<canvas id="canvas" class="fill"></canvas>
575<div id="element-title">
576  <span id="tag-name"></span><span id="node-id"></span><span id="class-name"></span>
577  <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>
578</div>
579<div id="right-gutter"></div>
580<div id="bottom-gutter"></div>
581<div id="log"></div>
582</html>
583