Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(479)

Side by Side Diff: experimental/webtry/res/js/webtry.js

Issue 292433002: Move zooming into its own component, imported via HTML Imports. (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /** 1 /**
2 * Common JS that talks XHR back to the server and runs the code and receives 2 * Common JS that talks XHR back to the server and runs the code and receives
3 * the results. 3 * the results.
4 */ 4 */
5 5
6 /**
7 * A polyfill for HTML Templates.
8 *
9 * This just adds in the content attribute, it doesn't stop scripts
10 * from running nor does it stop other side-effects.
11 */
12 (function polyfillTemplates() {
13 if('content' in document.createElement('template')) {
14 return false;
15 }
16
17 var templates = document.getElementsByTagName('template');
18 for (var i=0; i<templates.length; i++) {
19 var content = document.createDocumentFragment();
20 while (templates[i].firstChild) {
21 content.appendChild(templates[i].firstChild);
22 }
23 templates[i].content = content;
24 }
25 })();
26
27 /**
28 * Enable zooming for any images with a class of 'zoom'.
29 */
30 (function () {
31 var PIXELS = 20; // The number of pixels in width and height in a zoom.
32 var clientX = 0;
33 var clientY = 0;
34 var lastClientX = 0;
35 var lastClientY = 0;
36 var ctx = null; // The 2D canvas context of the zoom.
37 var currentImage = null; // The img node we are zooming for, otherwise null.
38 var hex = document.getElementById('zoomHex');
39 var canvasCopy = null;
40
41 function zoomMove(e) {
42 clientX = e.clientX;
43 clientY = e.clientY;
44 }
45
46 function zoomMouseDown(e) {
47 e.preventDefault();
48 // Only do zooming on the primary mouse button.
49 if (e.button != 0) {
50 return
51 }
52 currentImage = e.target;
53 clientX = e.clientX;
54 clientY = e.clientY;
55 lastClientX = clientX-1;
56 lastClientY = clientY-1;
57 document.body.style.cursor = 'crosshair';
58 canvas = document.createElement('canvas');
59 canvas.width = 1024;
60 canvas.height = 1024;
61 canvas.classList.add('zoomCanvas');
62 ctx = canvas.getContext('2d');
63 ctx.imageSmoothingEnabled = false;
64 this.parentNode.insertBefore(canvas, this);
65
66 // Copy the image over to a canvas so we can read RGBA values for each point .
67 if (hex) {
68 canvasCopy = document.createElement('canvas');
69 canvasCopy.width = currentImage.width;
70 canvasCopy.height = currentImage.height;
71 canvasCopy.id = 'zoomCopy';
72 canvasCopy.getContext('2d').drawImage(currentImage, 0, 0, currentImage.wid th, currentImage.height);
73 this.parentNode.insertBefore(canvasCopy, this);
74 }
75
76 document.body.addEventListener('pointermove', zoomMove, true);
77 document.body.addEventListener('pointerup', zoomFinished);
78 document.body.addEventListener('pointerleave', zoomFinished);
79
80 // Kick off the drawing.
81 setTimeout(drawZoom, 1);
82 }
83
84 function hexify(i) {
85 var s = i.toString(16).toUpperCase();
86 // Pad out to two hex digits if necessary.
87 if (s.length < 2) {
88 s = '0' + s;
89 }
90 return s;
91 }
92
93 function drawZoom() {
94 if (currentImage) {
95 // Only draw if the mouse has moved from the last time we drew.
96 if (lastClientX != clientX || lastClientY != clientY) {
97 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
98 var x = clientX - currentImage.x;
99 var y = clientY - currentImage.y;
100 var dx = Math.floor(ctx.canvas.width/PIXELS);
101 var dy = Math.floor(ctx.canvas.height/PIXELS);
102
103 ctx.lineWidth = 1;
104 ctx.strokeStyle = '#000';
105
106 // Draw out each pixel as a rect on the target canvas, as this works aro und
107 // FireFox doing a blur as it copies from one canvas to another.
108 var colors = canvasCopy.getContext('2d').getImageData(x, y, PIXELS, PIXE LS).data;
109 for (var i=0; i<PIXELS; i++) {
110 for (var j=0; j<PIXELS; j++) {
111 var offset = (j*PIXELS+i)*4; // Offset into the colors array.
112 ctx.fillStyle = 'rgba(' + colors[offset] + ', ' + colors[offset+1] + ', ' + colors[offset+2] + ', ' + colors[offset+3]/255.0 + ')';
113 ctx.fillRect(i*dx, j*dy, dx-1, dy-1);
114 // Box and label one selected pixel with its rgba values.
115 if (hex && i==PIXELS/2 && j == PIXELS/2) {
116 ctx.strokeRect(i*dx, j*dy, dx-1, dy-1);
117 hex.textContent = 'rgba('
118 + colors[offset] + ', '
119 + colors[offset+1] + ', '
120 + colors[offset+2] + ', '
121 + colors[offset+3] + ') '
122 + hexify(colors[offset])
123 + hexify(colors[offset+1])
124 + hexify(colors[offset+2])
125 + hexify(colors[offset+3]);
126 }
127 }
128 }
129 lastClientX = clientX;
130 lastClientY = clientY;
131 }
132 setTimeout(drawZoom, 1000/30);
133 }
134 }
135
136 function zoomFinished() {
137 currentImage = null;
138 if (hex) {
139 hex.textContent = '';
140 }
141 document.body.style.cursor = 'default';
142 ctx.canvas.parentNode.removeChild(ctx.canvas);
143 canvasCopy.parentNode.removeChild(canvasCopy);
144 document.body.removeEventListener('pointermove', zoomMove, true);
145 document.body.removeEventListener('pointerup', zoomFinished);
146 document.body.removeEventListener('pointerleave', zoomFinished);
147 }
148
149 this.addEventListener('DOMContentLoaded', function() {
150 var zoomables = document.body.querySelectorAll('.zoom');
151 for (var i=0; i<zoomables.length; i++) {
152 zoomables[i].addEventListener('pointerdown', zoomMouseDown);
153 }
154 });
155 })();
156
157 6
158 /** 7 /**
159 * All the functionality is wrapped up in this anonymous closure, but we need 8 * All the functionality is wrapped up in this anonymous closure, but we need
160 * to be told if we are on the workspace page or a normal try page, so the 9 * to be told if we are on the workspace page or a normal try page, so the
161 * workspaceName is passed into the closure, it must be set in the global 10 * workspaceName is passed into the closure, it must be set in the global
162 * namespace. If workspaceName is the empty string then we know we aren't 11 * namespace. If workspaceName is the empty string then we know we aren't
163 * running on a workspace page. 12 * running on a workspace page.
164 * 13 *
165 * If we are on a workspace page we also look for a 'history' 14 * If we are on a workspace page we also look for a 'history'
166 * variable in the global namespace which contains the list of tries 15 * variable in the global namespace which contains the list of tries
167 * that are included in this workspace. That variable is used to 16 * that are included in this workspace. That variable is used to
168 * populate the history list. 17 * populate the history list.
169 */ 18 */
170 (function(workspaceName) { 19 (function() {
171 var run = document.getElementById('run'); 20 function onLoad() {
172 var permalink = document.getElementById('permalink'); 21 var run = document.getElementById('run');
173 var embed = document.getElementById('embed'); 22 var permalink = document.getElementById('permalink');
174 var embedButton = document.getElementById('embedButton'); 23 var embed = document.getElementById('embed');
175 var code = document.getElementById('code'); 24 var embedButton = document.getElementById('embedButton');
176 var output = document.getElementById('output'); 25 var code = document.getElementById('code');
177 var stdout = document.getElementById('stdout'); 26 var output = document.getElementById('output');
178 var img = document.getElementById('img'); 27 var stdout = document.getElementById('stdout');
179 var tryHistory = document.getElementById('tryHistory'); 28 var img = document.getElementById('img');
180 var parser = new DOMParser(); 29 var tryHistory = document.getElementById('tryHistory');
181 var tryTemplate = document.getElementById('tryTemplate'); 30 var parser = new DOMParser();
31 var tryTemplate = document.getElementById('tryTemplate');
182 32
183 var editor = CodeMirror.fromTextArea(code, { 33 var editor = CodeMirror.fromTextArea(code, {
184 theme: "default", 34 theme: "default",
185 lineNumbers: true, 35 lineNumbers: true,
186 matchBrackets: true, 36 matchBrackets: true,
187 mode: "text/x-c++src", 37 mode: "text/x-c++src",
188 indentUnit: 4, 38 indentUnit: 4,
189 }); 39 });
190 40
191 // Match the initial textarea size. 41 // Match the initial textarea size.
192 editor.setSize(editor.defaultCharWidth() * code.cols, 42 editor.setSize(editor.defaultCharWidth() * code.cols,
193 editor.defaultTextHeight() * code.rows); 43 editor.defaultTextHeight() * code.rows);
194 44
195 function beginWait() { 45 function beginWait() {
196 document.body.classList.add('waiting'); 46 document.body.classList.add('waiting');
197 run.disabled = true; 47 run.disabled = true;
198 } 48 }
199 49
200 50
201 function endWait() { 51 function endWait() {
202 document.body.classList.remove('waiting'); 52 document.body.classList.remove('waiting');
203 run.disabled = false; 53 run.disabled = false;
204 } 54 }
205 55
206 56
207 /** 57 /**
208 * Callback when there's an XHR error. 58 * Callback when there's an XHR error.
209 * @param e The callback event. 59 * @param e The callback event.
210 */ 60 */
211 function xhrError(e) { 61 function xhrError(e) {
212 endWait(); 62 endWait();
213 alert('Something bad happened: ' + e); 63 alert('Something bad happened: ' + e);
214 } 64 }
215 65
216 function clearOutput() { 66 function clearOutput() {
217 output.textContent = ""; 67 output.textContent = "";
218 if (stdout) { 68 if (stdout) {
219 stdout.textContent = ""; 69 stdout.textContent = "";
70 }
71 embed.style.display='none';
220 } 72 }
221 embed.style.display='none';
222 }
223 73
224 /** 74 /**
225 * Called when an image in the workspace history is clicked. 75 * Called when an image in the workspace history is clicked.
226 */ 76 */
227 function historyClick() { 77 function historyClick() {
228 beginWait(); 78 beginWait();
229 clearOutput(); 79 clearOutput();
230 var req = new XMLHttpRequest(); 80 var req = new XMLHttpRequest();
231 req.addEventListener('load', historyComplete); 81 req.addEventListener('load', historyComplete);
232 req.addEventListener('error', xhrError); 82 req.addEventListener('error', xhrError);
233 req.overrideMimeType('application/json'); 83 req.overrideMimeType('application/json');
234 req.open('GET', this.getAttribute('data-try'), true); 84 req.open('GET', this.getAttribute('data-try'), true);
235 req.send(); 85 req.send();
236 } 86 }
237 87
238 88
239 /** 89 /**
240 * Callback for when the XHR kicked off in historyClick() returns. 90 * Callback for when the XHR kicked off in historyClick() returns.
241 */ 91 */
242 function historyComplete(e) { 92 function historyComplete(e) {
243 // The response is JSON of the form: 93 // The response is JSON of the form:
244 // { 94 // {
245 // "hash": "unique id for a try", 95 // "hash": "unique id for a try",
246 // "code": "source code for try" 96 // "code": "source code for try"
247 // } 97 // }
248 endWait(); 98 endWait();
249 body = JSON.parse(e.target.response); 99 body = JSON.parse(e.target.response);
250 code.value = body.code; 100 code.value = body.code;
251 editor.setValue(body.code); 101 editor.setValue(body.code);
252 img.src = '/i/'+body.hash+'.png'; 102 img.src = '/i/'+body.hash+'.png';
253 if (permalink) { 103 if (permalink) {
254 permalink.href = '/c/' + body.hash; 104 permalink.href = '/c/' + body.hash;
105 }
106 }
107
108
109 /**
110 * Add the given try image to the history of a workspace.
111 */
112 function addToHistory(hash, imgUrl) {
113 var clone = tryTemplate.content.cloneNode(true);
114 clone.querySelector('img').src = imgUrl;
115 clone.querySelector('.tries').setAttribute('data-try', '/json/' + hash);
116 tryHistory.insertBefore(clone, tryHistory.firstChild);
117 tryHistory.querySelector('.tries').addEventListener('click', historyClic k, true);
118 }
119
120
121 /**
122 * Callback for when the XHR returns after attempting to run the code.
123 * @param e The callback event.
124 */
125 function codeComplete(e) {
126 // The response is JSON of the form:
127 // {
128 // "message": "you had an error...",
129 // "img": "<base64 encoded image but only on success>"
130 // }
131 //
132 // The img is optional and only appears if there is a valid
133 // image to display.
134 endWait();
135 console.log(e.target.response);
136 body = JSON.parse(e.target.response);
137 output.textContent = body.message;
138 if (stdout) {
139 stdout.textContent = body.stdout;
140 }
141 if (body.hasOwnProperty('img')) {
142 img.src = 'data:image/png;base64,' + body.img;
143 } else {
144 img.src = '';
145 }
146 // Add the image to the history if we are on a workspace page.
147 if (tryHistory) {
148 addToHistory(body.hash, 'data:image/png;base64,' + body.img);
149 } else {
150 window.history.pushState(null, null, '/c/' + body.hash);
151 }
152 if (permalink) {
153 permalink.href = '/c/' + body.hash;
154 }
155 if (embed) {
156 var url = document.URL;
157 url = url.replace('/c/', '/iframe/');
158 embed.value = '<iframe src="' + url + '" width="740" height="550" styl e="border: solid #00a 5px; border-radius: 5px;"/>'
159 }
160 if (embedButton && embedButton.hasAttribute('disabled')) {
161 embedButton.removeAttribute('disabled');
162 }
163 }
164
165
166 function onSubmitCode() {
167 beginWait();
168 clearOutput();
169 var req = new XMLHttpRequest();
170 req.addEventListener('load', codeComplete);
171 req.addEventListener('error', xhrError);
172 req.overrideMimeType('application/json');
173 req.open('POST', '/', true);
174 req.setRequestHeader('content-type', 'application/json');
175 req.send(JSON.stringify({'code': editor.getValue(), 'name': workspaceNam e}));
176 }
177 run.addEventListener('click', onSubmitCode);
178
179
180 function onEmbedClick() {
181 embed.style.display='inline';
182 }
183
184 if (embedButton) {
185 embedButton.addEventListener('click', onEmbedClick);
186 }
187
188 // Add the images to the history if we are on a workspace page.
189 if (tryHistory && history) {
190 for (var i=0; i<history.length; i++) {
191 addToHistory(history[i].hash, '/i/'+history[i].hash+'.png');
192 }
255 } 193 }
256 } 194 }
257 195
258 196 // If loaded via HTML Imports then DOMContentLoaded will be long done.
259 /** 197 if (document.readyState != "loading") {
260 * Add the given try image to the history of a workspace. 198 onLoad();
261 */ 199 } else {
262 function addToHistory(hash, imgUrl) { 200 this.addEventListener('DOMContentLoaded', onLoad);
263 var clone = tryTemplate.content.cloneNode(true);
264 clone.querySelector('img').src = imgUrl;
265 clone.querySelector('.tries').setAttribute('data-try', '/json/' + hash);
266 tryHistory.insertBefore(clone, tryHistory.firstChild);
267 tryHistory.querySelector('.tries').addEventListener('click', historyClick, true);
268 } 201 }
269 202
270 203 })();
271 /**
272 * Callback for when the XHR returns after attempting to run the code.
273 * @param e The callback event.
274 */
275 function codeComplete(e) {
276 // The response is JSON of the form:
277 // {
278 // "message": "you had an error...",
279 // "img": "<base64 encoded image but only on success>"
280 // }
281 //
282 // The img is optional and only appears if there is a valid
283 // image to display.
284 endWait();
285 console.log(e.target.response);
286 body = JSON.parse(e.target.response);
287 output.textContent = body.message;
288 if (stdout) {
289 stdout.textContent = body.stdout;
290 }
291 if (body.hasOwnProperty('img')) {
292 img.src = 'data:image/png;base64,' + body.img;
293 } else {
294 img.src = '';
295 }
296 // Add the image to the history if we are on a workspace page.
297 if (tryHistory) {
298 addToHistory(body.hash, 'data:image/png;base64,' + body.img);
299 } else {
300 window.history.pushState(null, null, '/c/' + body.hash);
301 }
302 if (permalink) {
303 permalink.href = '/c/' + body.hash;
304 }
305 if (embed) {
306 var url = document.URL;
307 url = url.replace('/c/', '/iframe/');
308 embed.value = '<iframe src="' + url + '" width="740" height="550" style= "border: solid #00a 5px; border-radius: 5px;"/>'
309 }
310 if (embedButton && embedButton.hasAttribute('disabled')) {
311 embedButton.removeAttribute('disabled');
312 }
313 }
314
315
316 function onSubmitCode() {
317 beginWait();
318 clearOutput();
319 var req = new XMLHttpRequest();
320 req.addEventListener('load', codeComplete);
321 req.addEventListener('error', xhrError);
322 req.overrideMimeType('application/json');
323 req.open('POST', '/', true);
324 req.setRequestHeader('content-type', 'application/json');
325 req.send(JSON.stringify({'code': editor.getValue(), 'name': workspaceName} ));
326 }
327 run.addEventListener('click', onSubmitCode);
328
329
330 function onEmbedClick() {
331 embed.style.display='inline';
332 }
333
334 if (embedButton) {
335 embedButton.addEventListener('click', onEmbedClick);
336 }
337
338
339 // Add the images to the history if we are on a workspace page.
340 if (tryHistory && history) {
341 for (var i=0; i<history.length; i++) {
342 addToHistory(history[i].hash, '/i/'+history[i].hash+'.png');
343 }
344 }
345
346 })(workspaceName);
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698