Move zooming into its own component, imported via HTML Imports.

Also, start building our own polyfill library (polyfill.js) using Grunt and Bower.

Finally, refactor all the web pages so that the common header and footer material comes from templates.

BUG=skia:
R=mtklein@google.com

Author: jcgregorio@google.com

Review URL: https://codereview.chromium.org/292433002

git-svn-id: http://skia.googlecode.com/svn/trunk@14783 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
commit-bot@chromium.org 2014-05-19 16:40:08 +00:00
parent 99bd7d8174
commit 015c08e5ca
19 changed files with 520 additions and 406 deletions

View File

@ -89,5 +89,6 @@ Do this step only once, but only after running webtry_setup.sh the first time
Third Party Code Third Party Code
---------------- ----------------
* res/js/pointerevents.min.js - obtained from https://github.com/components/pointerevents-polyfill * res/js/polyfill.js - Various JS polyfill libraries. To rebuild or update
see polyfill/README.md.

View File

@ -0,0 +1,3 @@
{
"directory": "../../../out/bower"
}

View File

@ -0,0 +1,64 @@
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
// Install all the packages listed in the bower.json file.
shell: {
bower_install: {
command: 'bower install'
}
},
// Copy all the bower files into a single directory.
bower: {
dev: {
dest: '../../../out/grunt/third_party'
}
},
// Concatenate all the files in third_party into a single file.
concat: {
dist: {
src: [
'../../../out/grunt/third_party/WeakMap.js',
'../../../out/grunt/third_party/classlist.js',
'../../../out/grunt/third_party/pointerevents-polyfill.js',
'../../../out/grunt/third_party/MutationObserver.js',
'../../../out/grunt/third_party/CustomElements.js',
'../../../out/grunt/third_party/HTMLImports.js',
],
dest: '../../../out/grunt/src/<%= pkg.name %>.js'
}
},
// Uglify the one big file into one smaller file.
uglify: {
options: {
banner: '/*! <%= pkg.name %> built from /exerimental/webtry/poly <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
build: {
src: '../../../out/grunt/src/<%= pkg.name %>.js',
dest: '../res/js/<%= pkg.name %>.js'
}
},
copy: {
simple: {
src: '../../../out/grunt/src/<%= pkg.name %>.js',
dest: '../res/js/<%= pkg.name %>.js'
}
}
});
// Load the plugins for the above commands.
grunt.loadNpmTasks('grunt-bower');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-shell');
// By default run all the commands in the right sequence to build our custom minified polyfill.
grunt.registerTask('default', ['shell:bower_install', 'bower', 'concat', 'uglify']);
// A target to build an unminified version, for debugging.
grunt.registerTask('notmin', ['shell:bower_install', 'bower', 'concat', 'copy:simple']);
};

View File

@ -0,0 +1,18 @@
polyfill.js
===========
To rebuild or update res/js/polyfill.js you will need to have
[node.js](http://nodejs.org/) installed. Once you have it installed run the
following:
cd polyfill
npm install
grunt
If you want to create a verion of the polyfill.js that hasn't been minified,
say for debugging purposes, then run:
cd polyfill
npm install
grunt notmin

View File

@ -0,0 +1,22 @@
{
"name": "polyfill",
"version": "0.0.1",
"authors": [
"Joe Gregorio <jcgregorio@google.com>"
],
"description": "All the third party polyfills we use.",
"license": "MIT",
"private": true,
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"dependencies": {
"pointerevents-polyfill": "~0.2.0",
"CustomElements": "~0.2.3",
"HTMLImports": "~0.2.3"
}
}

View File

@ -0,0 +1,17 @@
{
"name": "polyfill",
"version": "0.0.1",
"description": "All the dependencies we need to run grunt and build our minified polyfill JS library.",
"main": "polyfill.js",
"author": "jcgregorio@google.com",
"license": "MIT",
"devDependencies": {
"grunt": "~0.4.5",
"grunt-contrib-uglify": "~0.4.0",
"grunt-bower": "~0.13.1",
"grunt-contrib-concat": "~0.4.0",
"grunt-shell": "~0.7.0",
"grunt-contrib-copy": "~0.5.0",
"grunt-contrib-cssmin": "~0.9.0"
}
}

View File

@ -9,26 +9,6 @@ img {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAAXNSR0IArs4c6QAAAAJiS0dEAP+Hj8y/AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAB3RJTUUH3gUBEi4DGRAQYgAAAB1JREFUGNNjfMoAAVJQmokBDdBHgPE/lPFsYN0BABdaAwN6tehMAAAAAElFTkSuQmCC"); background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAAXNSR0IArs4c6QAAAAJiS0dEAP+Hj8y/AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAB3RJTUUH3gUBEi4DGRAQYgAAAB1JREFUGNNjfMoAAVJQmokBDdBHgPE/lPFsYN0BABdaAwN6tehMAAAAAElFTkSuQmCC");
} }
.zoom {
cursor: crosshair;
}
.zoomCanvas {
position: absolute;
width: vmin;
height: vmin;
top: 3em;
right: 1em;
z-index: -1;
}
#zoomCopy {
display: none;
}
#zoomHex {
text-shadow: 1px 1px #eee;
}
h1 { h1 {
font-size: 18px; font-size: 18px;

View File

@ -0,0 +1,169 @@
<script type="text/javascript" charset="utf-8">
/**
* A reusable HTML Import to enable zooming on images.
*
* To use, simply include this HTML Import and add the class 'zoom' to any
* images you want zoomable.
*
* <link rel='import' type='text/html' href='/res/imp/zoom.html'>
*
* <img src="http://..." class="zoom"/>
*
* Any number of images on a page can be zoomable.
*
* If you want to display the rgb colors of the pixel at the center of the
* zoom then add an id of 'zoomHex' to any element that supports
* textContent, such as a div, p, span, etc.
*
* <p id=zoomHex></p>
*
* Note that HTML Imports need to be polyfilled in the near term.
*/
(function () {
function onLoad() {
var PIXELS = 20; // The number of pixels in width and height in a zoom.
var clientX = 0;
var clientY = 0;
var lastClientX = 0;
var lastClientY = 0;
var ctx = null; // The 2D canvas context of the zoom.
var currentImage = null; // The img node we are zooming for, otherwise null.
var hex = document.getElementById('zoomHex');
var canvasCopy = null;
function zoomMove(e) {
clientX = e.clientX;
clientY = e.clientY;
}
function zoomMouseDown(e) {
e.preventDefault();
// Only do zooming on the primary mouse button.
if (e.button != 0) {
return
}
currentImage = e.target;
clientX = e.clientX;
clientY = e.clientY;
lastClientX = clientX-1;
lastClientY = clientY-1;
document.body.style.cursor = 'crosshair';
canvas = document.createElement('canvas');
canvas.width = 1024;
canvas.height = 1024;
canvas.classList.add('zoomCanvas');
ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled = false;
this.parentNode.insertBefore(canvas, this);
// Copy the image over to a canvas so we can read RGBA values for each point.
if (hex) {
canvasCopy = document.createElement('canvas');
canvasCopy.width = currentImage.width;
canvasCopy.height = currentImage.height;
canvasCopy.id = 'zoomCopy';
canvasCopy.getContext('2d').drawImage(currentImage, 0, 0, currentImage.width, currentImage.height);
this.parentNode.insertBefore(canvasCopy, this);
}
document.body.addEventListener('pointermove', zoomMove, true);
document.body.addEventListener('pointerup', zoomFinished);
document.body.addEventListener('pointerleave', zoomFinished);
// Kick off the drawing.
setTimeout(drawZoom, 1);
}
function hexify(i) {
var s = i.toString(16).toUpperCase();
// Pad out to two hex digits if necessary.
if (s.length < 2) {
s = '0' + s;
}
return s;
}
function drawZoom() {
if (currentImage) {
// Only draw if the mouse has moved from the last time we drew.
if (lastClientX != clientX || lastClientY != clientY) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
var x = clientX - currentImage.x;
var y = clientY - currentImage.y;
var dx = Math.floor(ctx.canvas.width/PIXELS);
var dy = Math.floor(ctx.canvas.height/PIXELS);
ctx.lineWidth = 1;
ctx.strokeStyle = '#000';
// Draw out each pixel as a rect on the target canvas, as this works around
// FireFox doing a blur as it copies from one canvas to another.
var colors = canvasCopy.getContext('2d').getImageData(x, y, PIXELS, PIXELS).data;
for (var i=0; i<PIXELS; i++) {
for (var j=0; j<PIXELS; j++) {
var offset = (j*PIXELS+i)*4; // Offset into the colors array.
ctx.fillStyle = 'rgba(' + colors[offset] + ', ' + colors[offset+1] + ', ' + colors[offset+2] + ', ' + colors[offset+3]/255.0 + ')';
ctx.fillRect(i*dx, j*dy, dx-1, dy-1);
// Box and label one selected pixel with its rgba values.
if (hex && i==PIXELS/2 && j == PIXELS/2) {
ctx.strokeRect(i*dx, j*dy, dx-1, dy-1);
hex.textContent = 'rgba('
+ colors[offset] + ', '
+ colors[offset+1] + ', '
+ colors[offset+2] + ', '
+ colors[offset+3] + ') '
+ hexify(colors[offset])
+ hexify(colors[offset+1])
+ hexify(colors[offset+2])
+ hexify(colors[offset+3]);
}
}
}
lastClientX = clientX;
lastClientY = clientY;
}
setTimeout(drawZoom, 1000/30);
}
}
function zoomFinished() {
currentImage = null;
if (hex) {
hex.textContent = '';
}
document.body.style.cursor = 'default';
ctx.canvas.parentNode.removeChild(ctx.canvas);
canvasCopy.parentNode.removeChild(canvasCopy);
document.body.removeEventListener('pointermove', zoomMove, true);
document.body.removeEventListener('pointerup', zoomFinished);
document.body.removeEventListener('pointerleave', zoomFinished);
}
var zoomables = document.body.querySelectorAll('.zoom');
for (var i=0; i<zoomables.length; i++) {
zoomables[i].addEventListener('pointerdown', zoomMouseDown);
}
}
// If loaded via HTML Imports then DOMContentLoaded will be long done.
if (document.readyState != "loading") {
onLoad();
} else {
this.addEventListener('DOMContentLoaded', onLoad);
}
})();
</script>
<style type="text/css" media="screen">
.zoom {
cursor: crosshair;
}
.zoomCanvas {
position: absolute;
width: vmin;
height: vmin;
top: 3em;
right: 1em;
z-index: -1;
}
#zoomCopy {
display: none;
}
#zoomHex {
text-shadow: 1px 1px #eee;
}
</style>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -3,157 +3,6 @@
* the results. * the results.
*/ */
/**
* A polyfill for HTML Templates.
*
* This just adds in the content attribute, it doesn't stop scripts
* from running nor does it stop other side-effects.
*/
(function polyfillTemplates() {
if('content' in document.createElement('template')) {
return false;
}
var templates = document.getElementsByTagName('template');
for (var i=0; i<templates.length; i++) {
var content = document.createDocumentFragment();
while (templates[i].firstChild) {
content.appendChild(templates[i].firstChild);
}
templates[i].content = content;
}
})();
/**
* Enable zooming for any images with a class of 'zoom'.
*/
(function () {
var PIXELS = 20; // The number of pixels in width and height in a zoom.
var clientX = 0;
var clientY = 0;
var lastClientX = 0;
var lastClientY = 0;
var ctx = null; // The 2D canvas context of the zoom.
var currentImage = null; // The img node we are zooming for, otherwise null.
var hex = document.getElementById('zoomHex');
var canvasCopy = null;
function zoomMove(e) {
clientX = e.clientX;
clientY = e.clientY;
}
function zoomMouseDown(e) {
e.preventDefault();
// Only do zooming on the primary mouse button.
if (e.button != 0) {
return
}
currentImage = e.target;
clientX = e.clientX;
clientY = e.clientY;
lastClientX = clientX-1;
lastClientY = clientY-1;
document.body.style.cursor = 'crosshair';
canvas = document.createElement('canvas');
canvas.width = 1024;
canvas.height = 1024;
canvas.classList.add('zoomCanvas');
ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled = false;
this.parentNode.insertBefore(canvas, this);
// Copy the image over to a canvas so we can read RGBA values for each point.
if (hex) {
canvasCopy = document.createElement('canvas');
canvasCopy.width = currentImage.width;
canvasCopy.height = currentImage.height;
canvasCopy.id = 'zoomCopy';
canvasCopy.getContext('2d').drawImage(currentImage, 0, 0, currentImage.width, currentImage.height);
this.parentNode.insertBefore(canvasCopy, this);
}
document.body.addEventListener('pointermove', zoomMove, true);
document.body.addEventListener('pointerup', zoomFinished);
document.body.addEventListener('pointerleave', zoomFinished);
// Kick off the drawing.
setTimeout(drawZoom, 1);
}
function hexify(i) {
var s = i.toString(16).toUpperCase();
// Pad out to two hex digits if necessary.
if (s.length < 2) {
s = '0' + s;
}
return s;
}
function drawZoom() {
if (currentImage) {
// Only draw if the mouse has moved from the last time we drew.
if (lastClientX != clientX || lastClientY != clientY) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
var x = clientX - currentImage.x;
var y = clientY - currentImage.y;
var dx = Math.floor(ctx.canvas.width/PIXELS);
var dy = Math.floor(ctx.canvas.height/PIXELS);
ctx.lineWidth = 1;
ctx.strokeStyle = '#000';
// Draw out each pixel as a rect on the target canvas, as this works around
// FireFox doing a blur as it copies from one canvas to another.
var colors = canvasCopy.getContext('2d').getImageData(x, y, PIXELS, PIXELS).data;
for (var i=0; i<PIXELS; i++) {
for (var j=0; j<PIXELS; j++) {
var offset = (j*PIXELS+i)*4; // Offset into the colors array.
ctx.fillStyle = 'rgba(' + colors[offset] + ', ' + colors[offset+1] + ', ' + colors[offset+2] + ', ' + colors[offset+3]/255.0 + ')';
ctx.fillRect(i*dx, j*dy, dx-1, dy-1);
// Box and label one selected pixel with its rgba values.
if (hex && i==PIXELS/2 && j == PIXELS/2) {
ctx.strokeRect(i*dx, j*dy, dx-1, dy-1);
hex.textContent = 'rgba('
+ colors[offset] + ', '
+ colors[offset+1] + ', '
+ colors[offset+2] + ', '
+ colors[offset+3] + ') '
+ hexify(colors[offset])
+ hexify(colors[offset+1])
+ hexify(colors[offset+2])
+ hexify(colors[offset+3]);
}
}
}
lastClientX = clientX;
lastClientY = clientY;
}
setTimeout(drawZoom, 1000/30);
}
}
function zoomFinished() {
currentImage = null;
if (hex) {
hex.textContent = '';
}
document.body.style.cursor = 'default';
ctx.canvas.parentNode.removeChild(ctx.canvas);
canvasCopy.parentNode.removeChild(canvasCopy);
document.body.removeEventListener('pointermove', zoomMove, true);
document.body.removeEventListener('pointerup', zoomFinished);
document.body.removeEventListener('pointerleave', zoomFinished);
}
this.addEventListener('DOMContentLoaded', function() {
var zoomables = document.body.querySelectorAll('.zoom');
for (var i=0; i<zoomables.length; i++) {
zoomables[i].addEventListener('pointerdown', zoomMouseDown);
}
});
})();
/** /**
* All the functionality is wrapped up in this anonymous closure, but we need * All the functionality is wrapped up in this anonymous closure, but we need
@ -167,180 +16,188 @@
* that are included in this workspace. That variable is used to * that are included in this workspace. That variable is used to
* populate the history list. * populate the history list.
*/ */
(function(workspaceName) { (function() {
var run = document.getElementById('run'); function onLoad() {
var permalink = document.getElementById('permalink'); var run = document.getElementById('run');
var embed = document.getElementById('embed'); var permalink = document.getElementById('permalink');
var embedButton = document.getElementById('embedButton'); var embed = document.getElementById('embed');
var code = document.getElementById('code'); var embedButton = document.getElementById('embedButton');
var output = document.getElementById('output'); var code = document.getElementById('code');
var stdout = document.getElementById('stdout'); var output = document.getElementById('output');
var img = document.getElementById('img'); var stdout = document.getElementById('stdout');
var tryHistory = document.getElementById('tryHistory'); var img = document.getElementById('img');
var parser = new DOMParser(); var tryHistory = document.getElementById('tryHistory');
var tryTemplate = document.getElementById('tryTemplate'); var parser = new DOMParser();
var tryTemplate = document.getElementById('tryTemplate');
var editor = CodeMirror.fromTextArea(code, { var editor = CodeMirror.fromTextArea(code, {
theme: "default", theme: "default",
lineNumbers: true, lineNumbers: true,
matchBrackets: true, matchBrackets: true,
mode: "text/x-c++src", mode: "text/x-c++src",
indentUnit: 4, indentUnit: 4,
}); });
// Match the initial textarea size. // Match the initial textarea size.
editor.setSize(editor.defaultCharWidth() * code.cols, editor.setSize(editor.defaultCharWidth() * code.cols,
editor.defaultTextHeight() * code.rows); editor.defaultTextHeight() * code.rows);
function beginWait() { function beginWait() {
document.body.classList.add('waiting'); document.body.classList.add('waiting');
run.disabled = true; run.disabled = true;
}
function endWait() {
document.body.classList.remove('waiting');
run.disabled = false;
}
/**
* Callback when there's an XHR error.
* @param e The callback event.
*/
function xhrError(e) {
endWait();
alert('Something bad happened: ' + e);
}
function clearOutput() {
output.textContent = "";
if (stdout) {
stdout.textContent = "";
} }
embed.style.display='none';
}
/**
* Called when an image in the workspace history is clicked.
*/
function historyClick() {
beginWait();
clearOutput();
var req = new XMLHttpRequest();
req.addEventListener('load', historyComplete);
req.addEventListener('error', xhrError);
req.overrideMimeType('application/json');
req.open('GET', this.getAttribute('data-try'), true);
req.send();
}
/** function endWait() {
* Callback for when the XHR kicked off in historyClick() returns. document.body.classList.remove('waiting');
*/ run.disabled = false;
function historyComplete(e) { }
// The response is JSON of the form:
// {
// "hash": "unique id for a try", /**
// "code": "source code for try" * Callback when there's an XHR error.
// } * @param e The callback event.
endWait(); */
body = JSON.parse(e.target.response); function xhrError(e) {
code.value = body.code; endWait();
editor.setValue(body.code); alert('Something bad happened: ' + e);
img.src = '/i/'+body.hash+'.png'; }
if (permalink) {
permalink.href = '/c/' + body.hash; function clearOutput() {
output.textContent = "";
if (stdout) {
stdout.textContent = "";
}
embed.style.display='none';
}
/**
* Called when an image in the workspace history is clicked.
*/
function historyClick() {
beginWait();
clearOutput();
var req = new XMLHttpRequest();
req.addEventListener('load', historyComplete);
req.addEventListener('error', xhrError);
req.overrideMimeType('application/json');
req.open('GET', this.getAttribute('data-try'), true);
req.send();
}
/**
* Callback for when the XHR kicked off in historyClick() returns.
*/
function historyComplete(e) {
// The response is JSON of the form:
// {
// "hash": "unique id for a try",
// "code": "source code for try"
// }
endWait();
body = JSON.parse(e.target.response);
code.value = body.code;
editor.setValue(body.code);
img.src = '/i/'+body.hash+'.png';
if (permalink) {
permalink.href = '/c/' + body.hash;
}
}
/**
* Add the given try image to the history of a workspace.
*/
function addToHistory(hash, imgUrl) {
var clone = tryTemplate.content.cloneNode(true);
clone.querySelector('img').src = imgUrl;
clone.querySelector('.tries').setAttribute('data-try', '/json/' + hash);
tryHistory.insertBefore(clone, tryHistory.firstChild);
tryHistory.querySelector('.tries').addEventListener('click', historyClick, true);
}
/**
* Callback for when the XHR returns after attempting to run the code.
* @param e The callback event.
*/
function codeComplete(e) {
// The response is JSON of the form:
// {
// "message": "you had an error...",
// "img": "<base64 encoded image but only on success>"
// }
//
// The img is optional and only appears if there is a valid
// image to display.
endWait();
console.log(e.target.response);
body = JSON.parse(e.target.response);
output.textContent = body.message;
if (stdout) {
stdout.textContent = body.stdout;
}
if (body.hasOwnProperty('img')) {
img.src = 'data:image/png;base64,' + body.img;
} else {
img.src = '';
}
// Add the image to the history if we are on a workspace page.
if (tryHistory) {
addToHistory(body.hash, 'data:image/png;base64,' + body.img);
} else {
window.history.pushState(null, null, '/c/' + body.hash);
}
if (permalink) {
permalink.href = '/c/' + body.hash;
}
if (embed) {
var url = document.URL;
url = url.replace('/c/', '/iframe/');
embed.value = '<iframe src="' + url + '" width="740" height="550" style="border: solid #00a 5px; border-radius: 5px;"/>'
}
if (embedButton && embedButton.hasAttribute('disabled')) {
embedButton.removeAttribute('disabled');
}
}
function onSubmitCode() {
beginWait();
clearOutput();
var req = new XMLHttpRequest();
req.addEventListener('load', codeComplete);
req.addEventListener('error', xhrError);
req.overrideMimeType('application/json');
req.open('POST', '/', true);
req.setRequestHeader('content-type', 'application/json');
req.send(JSON.stringify({'code': editor.getValue(), 'name': workspaceName}));
}
run.addEventListener('click', onSubmitCode);
function onEmbedClick() {
embed.style.display='inline';
}
if (embedButton) {
embedButton.addEventListener('click', onEmbedClick);
}
// Add the images to the history if we are on a workspace page.
if (tryHistory && history) {
for (var i=0; i<history.length; i++) {
addToHistory(history[i].hash, '/i/'+history[i].hash+'.png');
}
} }
} }
// If loaded via HTML Imports then DOMContentLoaded will be long done.
/** if (document.readyState != "loading") {
* Add the given try image to the history of a workspace. onLoad();
*/ } else {
function addToHistory(hash, imgUrl) { this.addEventListener('DOMContentLoaded', onLoad);
var clone = tryTemplate.content.cloneNode(true);
clone.querySelector('img').src = imgUrl;
clone.querySelector('.tries').setAttribute('data-try', '/json/' + hash);
tryHistory.insertBefore(clone, tryHistory.firstChild);
tryHistory.querySelector('.tries').addEventListener('click', historyClick, true);
} }
})();
/**
* Callback for when the XHR returns after attempting to run the code.
* @param e The callback event.
*/
function codeComplete(e) {
// The response is JSON of the form:
// {
// "message": "you had an error...",
// "img": "<base64 encoded image but only on success>"
// }
//
// The img is optional and only appears if there is a valid
// image to display.
endWait();
console.log(e.target.response);
body = JSON.parse(e.target.response);
output.textContent = body.message;
if (stdout) {
stdout.textContent = body.stdout;
}
if (body.hasOwnProperty('img')) {
img.src = 'data:image/png;base64,' + body.img;
} else {
img.src = '';
}
// Add the image to the history if we are on a workspace page.
if (tryHistory) {
addToHistory(body.hash, 'data:image/png;base64,' + body.img);
} else {
window.history.pushState(null, null, '/c/' + body.hash);
}
if (permalink) {
permalink.href = '/c/' + body.hash;
}
if (embed) {
var url = document.URL;
url = url.replace('/c/', '/iframe/');
embed.value = '<iframe src="' + url + '" width="740" height="550" style="border: solid #00a 5px; border-radius: 5px;"/>'
}
if (embedButton && embedButton.hasAttribute('disabled')) {
embedButton.removeAttribute('disabled');
}
}
function onSubmitCode() {
beginWait();
clearOutput();
var req = new XMLHttpRequest();
req.addEventListener('load', codeComplete);
req.addEventListener('error', xhrError);
req.overrideMimeType('application/json');
req.open('POST', '/', true);
req.setRequestHeader('content-type', 'application/json');
req.send(JSON.stringify({'code': editor.getValue(), 'name': workspaceName}));
}
run.addEventListener('click', onSubmitCode);
function onEmbedClick() {
embed.style.display='inline';
}
if (embedButton) {
embedButton.addEventListener('click', onEmbedClick);
}
// Add the images to the history if we are on a workspace page.
if (tryHistory && history) {
for (var i=0; i<history.length; i++) {
addToHistory(history[i].hash, '/i/'+history[i].hash+'.png');
}
}
})(workspaceName);

View File

@ -1,8 +1,5 @@
<section id=content> <section id=content>
<script src="/res/js/cm/codemirror.js"></script>
<script src="/res/js/cm/clike.js"></script>
<pre> <pre>
<textarea spellcheck=false name='code' id='code' rows='15' cols='100'>{{.Code}}</textarea> <textarea spellcheck=false name='code' id='code' rows='15' cols='100'>{{.Code}}</textarea>
</pre> </pre>

View File

@ -0,0 +1,6 @@
<link rel='stylesheet' href='/res/css/webtry.css' type='text/css'>
<link rel='stylesheet' href='/res/css/cm/codemirror.css' type='text/css'>
<script src='/res/js/polyfill.js' type='text/javascript'></script>
<script src='/res/js/cm/codemirror.js' type='text/javascript'></script>
<script src='/res/js/cm/clike.js' type='text/javascript'></script>
<script src='/res/js/webtry.js' type='text/javascript'></script>

View File

@ -0,0 +1,2 @@
<meta charset='utf-8' />
<meta name=viewport content='width=device-width, initial-scale=1'>

View File

@ -2,17 +2,15 @@
<html> <html>
<head> <head>
<title>Skia WebTry</title> <title>Skia WebTry</title>
<meta charset='utf-8' /> {{template "headercommon.html" .}}
<script src="/res/js/pointerevents.min.js" type="text/javascript" charset="utf-8"></script> <link rel='import' type='text/html' href='/res/imp/zoom.html'>
<link rel='stylesheet' href='/res/css/webtry.css' type='text/css' media='screen'>
<link rel="stylesheet" href="/res/css/cm/codemirror.css" type="text/css" media="screen">
</head> </head>
<body class=iframe> <body class=iframe>
{{template "content.html" .}} {{template "content.html" .}}
<script type='text/javascript' charset='utf-8'> <script type='text/javascript'>
// Not running in a workspace. // Not running in a workspace.
var workspaceName = ''; var workspaceName = '';
</script> </script>
<script src="/res/js/webtry.js" type="text/javascript" charset="utf-8"></script> {{template "footercommon.html" .}}
</body> </body>
</html> </html>

View File

@ -2,19 +2,17 @@
<html> <html>
<head> <head>
<title>Skia WebTry</title> <title>Skia WebTry</title>
<meta charset='utf-8' /> {{template "headercommon.html" .}}
<script src="/res/js/pointerevents.min.js" type="text/javascript" charset="utf-8"></script> <link rel='import' type='text/html' href='/res/imp/zoom.html'>
<link rel="stylesheet" href="/res/css/webtry.css" type="text/css" media="screen">
<link rel="stylesheet" href="/res/css/cm/codemirror.css" type="text/css" media="screen">
</head> </head>
<body> <body>
{{template "titlebar.html" .}} {{template "titlebar.html" .}}
{{template "content.html" .}} {{template "content.html" .}}
<script type='text/javascript' charset='utf-8'> <script type='text/javascript'>
// Not running in a workspace. // Not running in a workspace.
var workspaceName = ""; var workspaceName = '';
</script> </script>
<script src="/res/js/webtry.js" type="text/javascript" charset="utf-8"></script> {{template "footercommon.html" .}}
</body> </body>
</html> </html>

View File

@ -3,22 +3,23 @@
<head> <head>
<title>Recent SkFiddles</title> <title>Recent SkFiddles</title>
<meta charset='utf-8' /> <meta charset='utf-8' />
<link rel="stylesheet" href="/res/css/webtry.css" type="text/css" media="screen"> {{template "headercommon.html" .}}
</head> </head>
<body> <body>
{{template "titlebar.html" .}} {{template "titlebar.html" .}}
<section id=content> <section id=content>
<h1>Recent Activity</h1> <h1>Recent Activity</h1>
<section> <section>
{{range .Tries}} {{range .Tries}}
<div class=tries> <div class=tries>
<h2><a href="/c/{{.Hash}}">{{.CreateTS}}</a></h2> <h2><a href="/c/{{.Hash}}">{{.CreateTS}}</a></h2>
<a href="/c/{{.Hash}}"> <a href="/c/{{.Hash}}">
<img width=128 height=128 src="/i/{{.Hash}}.png"> <img width=128 height=128 src="/i/{{.Hash}}.png">
</a> </a>
</div> </div>
{{end}} {{end}}
</section> </section>
</section> </section>
{{template "footercommon.html" .}}
</body> </body>
</html> </html>

View File

@ -2,15 +2,13 @@
<html> <html>
<head> <head>
<title>Workspace</title> <title>Workspace</title>
<meta charset='utf-8' /> {{template "headercommon.html" .}}
<script src="/res/js/pointerevents.min.js" type="text/javascript" charset="utf-8"></script> <link rel='import' type='text/html' href='/res/imp/zoom.html'>
<link rel='stylesheet' href='/res/css/webtry.css' type='text/css' media='screen'>
<link rel="stylesheet" href="/res/css/cm/codemirror.css" type="text/css" media="screen">
</head> </head>
<body> <body>
<template id=tryTemplate> <template id=tryTemplate>
<div class=tries data-try=""> <div class=tries data-try=''>
<img width=64 height=64 src=""> <img width=64 height=64 src=''>
</div> </div>
</template> </template>
{{template "titlebar.html" .}} {{template "titlebar.html" .}}
@ -20,22 +18,22 @@
<section id=tryHistory> <section id=tryHistory>
</section> </section>
<script type="text/javascript" charset="utf-8"> <script type='text/javascript'>
var history = {{.Tries}}; var history = {{.Tries}};
</script> </script>
<script type='text/javascript' charset='utf-8'> <script type='text/javascript'>
// Set the workspace name so run.js also updates the history. // Set the workspace name so run.js also updates the history.
var workspaceName = "{{.Name}}"; var workspaceName = '{{.Name}}';
</script> </script>
<script src="/res/js/webtry.js" type="text/javascript" charset="utf-8"></script>
{{else}} {{else}}
<section id=content> <section id=content>
<h1>Create</h1> <h1>Create</h1>
Create a new workspace: Create a new workspace:
<form action="." method="POST" accept-charset="utf-8"> <form action='.' method='POST'>
<p><input type="submit" value="Create"></p> <p><input type='submit' value='Create'></p>
</form> </form>
</section> </section>
{{end}} {{end}}
{{template "footercommon.html" .}}
</body> </body>
</html> </html>

View File

@ -140,6 +140,8 @@ func init() {
filepath.Join(cwd, "templates/index.html"), filepath.Join(cwd, "templates/index.html"),
filepath.Join(cwd, "templates/titlebar.html"), filepath.Join(cwd, "templates/titlebar.html"),
filepath.Join(cwd, "templates/content.html"), filepath.Join(cwd, "templates/content.html"),
filepath.Join(cwd, "templates/headercommon.html"),
filepath.Join(cwd, "templates/footercommon.html"),
) )
if err != nil { if err != nil {
panic(err) panic(err)
@ -147,6 +149,8 @@ func init() {
iframeTemplate, err = htemplate.ParseFiles( iframeTemplate, err = htemplate.ParseFiles(
filepath.Join(cwd, "templates/iframe.html"), filepath.Join(cwd, "templates/iframe.html"),
filepath.Join(cwd, "templates/content.html"), filepath.Join(cwd, "templates/content.html"),
filepath.Join(cwd, "templates/headercommon.html"),
filepath.Join(cwd, "templates/footercommon.html"),
) )
if err != nil { if err != nil {
panic(err) panic(err)
@ -154,6 +158,8 @@ func init() {
recentTemplate, err = htemplate.ParseFiles( recentTemplate, err = htemplate.ParseFiles(
filepath.Join(cwd, "templates/recent.html"), filepath.Join(cwd, "templates/recent.html"),
filepath.Join(cwd, "templates/titlebar.html"), filepath.Join(cwd, "templates/titlebar.html"),
filepath.Join(cwd, "templates/headercommon.html"),
filepath.Join(cwd, "templates/footercommon.html"),
) )
if err != nil { if err != nil {
panic(err) panic(err)
@ -162,6 +168,8 @@ func init() {
filepath.Join(cwd, "templates/workspace.html"), filepath.Join(cwd, "templates/workspace.html"),
filepath.Join(cwd, "templates/titlebar.html"), filepath.Join(cwd, "templates/titlebar.html"),
filepath.Join(cwd, "templates/content.html"), filepath.Join(cwd, "templates/content.html"),
filepath.Join(cwd, "templates/headercommon.html"),
filepath.Join(cwd, "templates/footercommon.html"),
) )
if err != nil { if err != nil {
panic(err) panic(err)