| <!-- The <debugger-app-sk> custom element declaration. |
| |
| The main application element for the Skia Debugger. |
| |
| Attributes: |
| None. |
| |
| Events: |
| None. |
| |
| Methods: |
| None. |
| |
| --> |
| <link rel=import href="/res/imp/bower_components/paper-drawer-panel/paper-drawer-panel.html"> |
| <link rel=import href="/res/imp/bower_components/paper-header-panel/paper-header-panel.html"> |
| <link rel=import href="/res/imp/bower_components/paper-icon-button/paper-icon-button.html"> |
| <link rel=import href="/res/imp/bower_components/paper-toolbar/paper-toolbar.html"> |
| <link rel=import href="/res/imp/bower_components/paper-checkbox/paper-checkbox.html"> |
| <link rel=import href="/res/imp/bower_components/iron-icons/iron-icons.html"> |
| <link rel=import href="/res/imp/bower_components/iron-icon/iron-icon.html"> |
| <link rel=import href="/res/imp/bower_components/iron-flex-layout/iron-flex-layout.html"> |
| <link rel=import href="/res/imp/bower_components/iron-selector/iron-selector.html"> |
| |
| <link rel=import href="/res/common/imp/9/canvas-layers.html"> |
| <link rel=import href="/res/common/imp/9/crosshair.html"> |
| <link rel=import href="/res/common/imp/9/details-summary.html"> |
| <link rel=import href="/res/common/imp/9/zoom.html"> |
| <link rel=import href="/res/common/imp/9/play.html"> |
| <link rel=import href="/res/common/imp/9/dbg-info.html"> |
| |
| <link rel=import href="/res/common/imp/9/error-toast-sk.html"> |
| <dom-module id="debugger-app-sk"> |
| <style type="text/css" media="screen"> |
| :root { |
| --paper-toolbar-background: #1f78b4; |
| } |
| |
| :host { |
| display: block; |
| height: 100% |
| } |
| |
| #content { |
| margin: 1em; |
| } |
| |
| op-sk { |
| display: block; |
| } |
| |
| details-sk.iron-selected { |
| background: #eee; |
| } |
| |
| #selector { |
| overflow-y: scroll; |
| height: 80vh; |
| position: relative; |
| } |
| |
| play-sk { |
| margin: 5px auto; |
| } |
| |
| #center { |
| overflow: hidden; |
| } |
| |
| #rendered { |
| margin: 0 10px; |
| overflow: auto; |
| } |
| |
| * { |
| font-family: Helvetica,Arial,'Bitstream Vera Sans',sans-serif; |
| } |
| |
| .hidden { |
| display: none; |
| } |
| |
| .shortcuts { |
| margin-left: 3em; |
| margin-bottom: 2em; |
| } |
| |
| .shortcuts td { |
| padding-left: 1em; |
| } |
| |
| .shortcuts tr { |
| padding-bottom: 0.5em; |
| } |
| |
| #colors { |
| margin: 1em 0; |
| } |
| |
| #colorBreakPoint { |
| margin-left: 3em; |
| } |
| |
| #download { |
| margin: 0 3em; |
| color: #1f78b4; |
| } |
| |
| dbg-info-sk { |
| margin-left: 3em; |
| margin-bottom: 1em; |
| display: block; |
| } |
| </style> |
| <template> |
| <paper-header-panel> |
| <paper-toolbar> |
| <div>Skia Debugger</div> |
| </paper-toolbar> |
| <div id=content> |
| <h2>Upload an SKP here.</h2> |
| <div class="layout horizontal"> |
| <form enctype="multipart/form-data" method="post" id=post_skp action="/new" name="fileinfo"> |
| <label>SKP to send:</label> |
| <input type="file" name="file" required /> |
| <input type="submit" value="Upload" /> |
| </form> |
| <a href="/download" id=download class=hidden>Download</a> |
| </div> |
| <hr/> |
| <div class="layout horizontal"> |
| <div id=left class="layout vertical flex-1"> |
| <play-sk id=play></play-sk> |
| <iron-selector id=selector> |
| <template is="dom-repeat" items="{{cmd.commands}}" as="command"> |
| <op-sk op="{{command}}" index="{{index}}"></op-sk> |
| </template> |
| </iron-selector> |
| </div> |
| <div id=center class="layout vertical flex-3"> |
| <div id=rendered> |
| <canvas-layers-sk layers='["crosshair"]' id=layers on-tap="_crosshairClick"> |
| <img id=img src="" class=hidden> |
| </canvas-layers-sk> |
| <crosshair-sk id=crosshair target=layers name=crosshair update_on=move></crosshair-sk> |
| </div> |
| </div> |
| <div id=right class="layout vertical flex-2"> |
| <details-sk open> |
| <summary-sk> |
| Clip and Matrix |
| </summary-sk> |
| <dbg-info-sk info="{{ info }}"></dbg-info-sk> |
| </details-sk> |
| <div id=position><b>Postion:</b> ({{ x }}, {{ y }})</div> |
| <div id=colors><b>Color:</b> {{ rgb }} {{ hex }} </div> |
| <div> |
| <paper-checkbox disabled id=colorBreakPoint>Break on change.</paper-checkbox> <span id=prevColor></span> |
| </div> |
| <details-sk id=keyboardshortcuts class=hidden> |
| <summary-sk> |
| Keyboard shortcuts |
| </summary-sk> |
| <table class=shortcuts> |
| <tr><th>H</th><td>Left</td></tr> |
| <tr><th>L</th><td>Right</td></tr> |
| <tr><th>J</th><td>Down</td></tr> |
| <tr><th>K</th><td>Up</td></tr> |
| <tr><td colspan=2>Click the image again to turn off keyboard navigation.</td></tr> |
| </table> |
| </details-sk> |
| <zoom-sk source=img pixels=20 id=zoom></zoom-sk> |
| </div> |
| </div> |
| <error-toast-sk></error-toast-sk> |
| </div> |
| </paper-header-panel> |
| </template> |
| </dom-module> |
| |
| <script> |
| Polymer({ |
| is: "debugger-app-sk", |
| |
| ready: function() { |
| // _targetIndex is the index of the op we are in the process of moving to. |
| // That is, we've requested to move to this op, but the image might not |
| // be loaded yet. |
| this._targetIndex = 0; |
| |
| // Load the test image served by the debugger. |
| this._setCacheBreakingURL(); |
| this.$.post_skp.action= window.location.origin + "/new"; |
| |
| // Can't set this as an attribute directly on iron-selector. Bah. |
| this.$.selector.selectedAttribute = "selected"; |
| this.$.selector.addEventListener('iron-select', function(e) { |
| // Only force reloading the image if necessary. |
| if (this._targetIndex != e.detail.item.index) { |
| this._targetIndex = e.detail.item.index; |
| this._setCacheBreakingURL(); |
| } |
| }.bind(this)); |
| this._refreshPage(); |
| |
| this.$.crosshair.addEventListener('crosshair', function(e) { |
| this.$.zoom.x = e.detail.x; |
| this.$.zoom.y = e.detail.y; |
| }.bind(this)); |
| |
| this.$.zoom.addEventListener('zoom-point', function(e) { |
| if ( this.$.colorBreakPoint.disabled == false |
| && this.$.colorBreakPoint.checked |
| && this.hex != e.detail.hex) { |
| // We've hit a breakpoint. Stop the play and show the change in color. |
| this.$.prevColor.textContent = this.hex + " -> " + e.detail.hex; |
| this.$.play.mode = "pause"; |
| } |
| this.set("rgb", e.detail.rgb); |
| this.set("hex", e.detail.hex); |
| this.set("x", e.detail.x); |
| this.set("y", e.detail.y); |
| }.bind(this)); |
| |
| this.$.zoom.addEventListener('zoom-loaded', function(e) { |
| this.$.play.movedTo(this._targetIndex); |
| }.bind(this)); |
| |
| this.$.img.addEventListener('load', function() { |
| this.$.download.href = window.location.origin + "/download"; |
| this.$.download.classList.remove("hidden"); |
| this.$.selector.scrollTop = this.$.selector.items[this._targetIndex].offsetTop; |
| this.$.selector.select(this._targetIndex); |
| }.bind(this)); |
| |
| this.$.play.addEventListener('moveto', function(e) { |
| this._targetIndex = e.detail.index; |
| this._setCacheBreakingURL(); |
| }.bind(this)); |
| |
| this.$.play.addEventListener('mode-changed-manually', function(e) { |
| // Clear the breakpoint info if the user presses a button. |
| this.$.prevColor.textContent = ""; |
| }.bind(this)); |
| }, |
| |
| properties: { |
| // The commands that make up the skp. |
| cmd: { |
| type: Object, |
| value: function() { return { commands: [] } }, |
| reflectToAttribute: false, |
| }, |
| }, |
| |
| attached: function() { |
| document.body.addEventListener('keydown', this._keyDownHandler.bind(this), true); |
| }, |
| |
| _refreshPage: function() { |
| this.$.img.classList.remove("hidden"); |
| this._setCacheBreakingURL(); |
| sk.get("/info").then(JSON.parse).then(function(json) { |
| this.set('info', json); |
| }.bind(this)).catch(sk.errorMessage); |
| sk.get("/cmd").then(JSON.parse).then(function(json) { |
| this.set('cmd', this._addDepth(json)); |
| this.$.play.size = json.commands.length; |
| this._targetIndex = json.commands.length - 1; |
| this._setCacheBreakingURL(); |
| }.bind(this)).catch(sk.errorMessage); |
| }, |
| |
| _setCacheBreakingURL: function() { |
| var path = "/img/" + this._targetIndex + "?"; |
| sk.get("/info/" + this._targetIndex).then(JSON.parse).then(function(json) { |
| this.set('info', json); |
| }.bind(this)).catch(sk.errorMessage); |
| this.$.img.src = window.location.origin + path + "?" + new Date().getTime(); |
| }, |
| |
| _crosshairClick: function() { |
| if (this.$.crosshair.update_on === "move") { |
| this.$.crosshair.update_on = "click"; |
| this.$.colorBreakPoint.disabled = false; |
| this.$.keyboardshortcuts.classList.remove("hidden"); |
| } else { |
| this.$.crosshair.update_on = "move"; |
| this.$.colorBreakPoint.disabled = true; |
| this.$.keyboardshortcuts.classList.add("hidden"); |
| } |
| }, |
| |
| _keyDownHandler: function(e) { |
| var c = String.fromCharCode(e.keyCode); |
| switch (c) { |
| case "J": |
| this.$.crosshair.y = this.$.crosshair.y+1; |
| break; |
| case "K": |
| this.$.crosshair.y = this.$.crosshair.y-1; |
| break; |
| case "H": |
| this.$.crosshair.x = this.$.crosshair.x-1; |
| break; |
| case "L": |
| this.$.crosshair.x = this.$.crosshair.x+1; |
| break; |
| } |
| if ("JKHL".indexOf(c) != -1 ) { |
| e.stopPropagation(); |
| } |
| }, |
| |
| _json: function(s) { |
| return JSON.stringify(s, null, 2); |
| }, |
| |
| // _addDepth cycles through the commands and set a depth based on Save/Restore pairs. |
| _addDepth: function(cmd) { |
| var commands = cmd.commands; |
| var depth = 0; |
| for (var i = 0; i < commands.length; i++) { |
| commands[i]._depth = depth; |
| if (commands[i].command == "Save") { |
| depth++; |
| } else if (commands[i].command == "Restore") { |
| depth--; |
| commands[i]._depth = depth; |
| } |
| } |
| return cmd; |
| } |
| }); |
| </script> |