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

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

Issue 261693003: Use CodeMirror for WebTry snippets. (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: design.md notes 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 | Annotate | Revision Log
« no previous file with comments | « experimental/webtry/css/webtry.css ('k') | experimental/webtry/res/css/cm/ambiance.css » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /**
2 * Common JS that talks XHR back to the server and runs the code and receives
3 * the results.
4 */
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 clientX = 0;
32 var clientY = 0;
33 var lastClientX = 0;
34 var lastClientY = 0;
35 var ctx = null; // The 2D canvas context of the zoom.
36 var currentImage = null; // The img node we are zooming for, otherwise null.
37
38 function zoomMove(e) {
39 clientX = e.clientX;
40 clientY = e.clientY;
41 }
42
43 function zoomMouseDown(e) {
44 e.preventDefault();
45 // Only do zooming on the primary mouse button.
46 if (e.button != 0) {
47 return
48 }
49 currentImage = e.target;
50 clientX = e.clientX;
51 clientY = e.clientY;
52 lastClientX = clientX-1;
53 lastClientY = clientY-1;
54 document.body.style.cursor = 'crosshair';
55 canvas = document.createElement('canvas');
56 canvas.width=256;
57 canvas.height=256;
58 canvas.classList.add('zoomCanvas');
59 ctx = canvas.getContext('2d');
60 ctx.imageSmoothingEnabled = false;
61 this.parentNode.insertBefore(canvas, this);
62
63 document.body.addEventListener('mousemove', zoomMove, true);
64 document.body.addEventListener('mouseup', zoomFinished);
65 document.body.addEventListener('mouseleave', zoomFinished);
66
67 // Kick off the drawing.
68 setTimeout(drawZoom, 1);
69 }
70
71 function drawZoom() {
72 if (currentImage) {
73 // Only draw if the mouse has moved from the last time we drew.
74 if (lastClientX != clientX || lastClientY != clientY) {
75 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
76 ctx.drawImage(currentImage,
77 clientX - currentImage.x, clientY - currentImage.y, // src zero
78 32, 32, // src dimensio ns
79 0, 0, // dst zero
80 ctx.canvas.width, ctx.canvas.height); // dst dimensio ns
81 lastClientX = clientX;
82 lastClientY = clientY;
83 }
84 setTimeout(drawZoom, 1000/30);
85 }
86 }
87
88 function zoomFinished() {
89 currentImage = null;
90 document.body.style.cursor = 'default';
91 ctx.canvas.parentNode.removeChild(ctx.canvas);
92 document.body.removeEventListener('mousemove', zoomMove, true);
93 document.body.removeEventListener('mouseup', zoomFinished);
94 document.body.removeEventListener('mouseleave', zoomFinished);
95 }
96
97 this.addEventListener('DOMContentLoaded', function() {
98 var zoomables = document.body.querySelectorAll('.zoom');
99 for (var i=0; i<zoomables.length; i++) {
100 zoomables[i].addEventListener('mousedown', zoomMouseDown);
101 }
102 });
103 })();
104
105
106 /**
107 * All the functionality is wrapped up in this anonymous closure, but we need
108 * to be told if we are on the workspace page or a normal try page, so the
109 * workspaceName is passed into the closure, it must be set in the global
110 * namespace. If workspaceName is the empty string then we know we aren't
111 * running on a workspace page.
112 *
113 * If we are on a workspace page we also look for a 'history'
114 * variable in the global namespace which contains the list of tries
115 * that are included in this workspace. That variable is used to
116 * populate the history list.
117 */
118 (function(workspaceName) {
119 var run = document.getElementById('run');
120 var permalink = document.getElementById('permalink');
121 var embed = document.getElementById('embed');
122 var embedButton = document.getElementById('embedButton');
123 var code = document.getElementById('code');
124 var output = document.getElementById('output');
125 var stdout = document.getElementById('stdout');
126 var img = document.getElementById('img');
127 var tryHistory = document.getElementById('tryHistory');
128 var parser = new DOMParser();
129 var tryTemplate = document.getElementById('tryTemplate');
130
131
132 function beginWait() {
133 document.body.classList.add('waiting');
134 run.disabled = true;
135 }
136
137
138 function endWait() {
139 document.body.classList.remove('waiting');
140 run.disabled = false;
141 }
142
143
144 /**
145 * Callback when there's an XHR error.
146 * @param e The callback event.
147 */
148 function xhrError(e) {
149 endWait();
150 alert('Something bad happened: ' + e);
151 }
152
153 function clearOutput() {
154 output.textContent = "";
155 if (stdout) {
156 stdout.textContent = "";
157 }
158 embed.style.display='none';
159 }
160
161 /**
162 * Called when an image in the workspace history is clicked.
163 */
164 function historyClick() {
165 beginWait();
166 clearOutput();
167 var req = new XMLHttpRequest();
168 req.addEventListener('load', historyComplete);
169 req.addEventListener('error', xhrError);
170 req.overrideMimeType('application/json');
171 req.open('GET', this.getAttribute('data-try'), true);
172 req.send();
173 }
174
175
176 /**
177 * Callback for when the XHR kicked off in historyClick() returns.
178 */
179 function historyComplete(e) {
180 // The response is JSON of the form:
181 // {
182 // "hash": "unique id for a try",
183 // "code": "source code for try"
184 // }
185 endWait();
186 body = JSON.parse(e.target.response);
187 code.value = body.code;
188 img.src = '/i/'+body.hash+'.png';
189 if (permalink) {
190 permalink.href = '/c/' + body.hash;
191 }
192 }
193
194
195 /**
196 * Add the given try image to the history of a workspace.
197 */
198 function addToHistory(hash, imgUrl) {
199 var clone = tryTemplate.content.cloneNode(true);
200 clone.querySelector('img').src = imgUrl;
201 clone.querySelector('.tries').setAttribute('data-try', '/json/' + hash);
202 tryHistory.insertBefore(clone, tryHistory.firstChild);
203 tryHistory.querySelector('.tries').addEventListener('click', historyClick, true);
204 }
205
206
207 /**
208 * Callback for when the XHR returns after attempting to run the code.
209 * @param e The callback event.
210 */
211 function codeComplete(e) {
212 // The response is JSON of the form:
213 // {
214 // "message": "you had an error...",
215 // "img": "<base64 encoded image but only on success>"
216 // }
217 //
218 // The img is optional and only appears if there is a valid
219 // image to display.
220 endWait();
221 console.log(e.target.response);
222 body = JSON.parse(e.target.response);
223 output.textContent = body.message;
224 if (stdout) {
225 stdout.textContent = body.stdout;
226 }
227 if (body.hasOwnProperty('img')) {
228 img.src = 'data:image/png;base64,' + body.img;
229 } else {
230 img.src = '';
231 }
232 // Add the image to the history if we are on a workspace page.
233 if (tryHistory) {
234 addToHistory(body.hash, 'data:image/png;base64,' + body.img);
235 } else {
236 window.history.pushState(null, null, '/c/' + body.hash);
237 }
238 if (permalink) {
239 permalink.href = '/c/' + body.hash;
240 }
241 if (embed) {
242 var url = document.URL;
243 url = url.replace('/c/', '/iframe/');
244 embed.value = '<iframe src="' + url + '" width="740" height="550" style= "border: solid #00a 5px; border-radius: 5px;"/>'
245 }
246 if (embedButton && embedButton.hasAttribute('disabled')) {
247 embedButton.removeAttribute('disabled');
248 }
249 }
250
251
252 function onSubmitCode() {
253 beginWait();
254 clearOutput();
255 var req = new XMLHttpRequest();
256 req.addEventListener('load', codeComplete);
257 req.addEventListener('error', xhrError);
258 req.overrideMimeType('application/json');
259 req.open('POST', '/', true);
260 req.setRequestHeader('content-type', 'application/json');
261 req.send(JSON.stringify({'code': code.value, 'name': workspaceName}));
262 }
263 run.addEventListener('click', onSubmitCode);
264
265
266 function onEmbedClick() {
267 embed.style.display='inline';
268 }
269
270 if (embedButton) {
271 embedButton.addEventListener('click', onEmbedClick);
272 }
273
274
275 // Add the images to the history if we are on a workspace page.
276 if (tryHistory && history) {
277 for (var i=0; i<history.length; i++) {
278 addToHistory(history[i].hash, '/i/'+history[i].hash+'.png');
279 }
280 }
281
282 })(workspaceName);
OLDNEW
« no previous file with comments | « experimental/webtry/css/webtry.css ('k') | experimental/webtry/res/css/cm/ambiance.css » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698