Skip to content

Instantly share code, notes, and snippets.

@jonasfj
Created July 6, 2015 07:28
Show Gist options
  • Save jonasfj/d0d22890514ea37e17b4 to your computer and use it in GitHub Desktop.
Save jonasfj/d0d22890514ea37e17b4 to your computer and use it in GitHub Desktop.
Initial hack for interactive tutorials...
var util = require('util');
var slugid = require('slugid');
var ace = require('brace');
require('brace/mode/javascript');
require('brace/theme/ambiance');
var babel = require('babel-core/lib/babel/api/browser');
// Babel runtime part we use
require('babel-runtime/regenerator');
require('babel-runtime/core-js/promise');
// Modules for use in code examples
require('taskcluster-client');
require('url');
require('querystring');
var preClass = document.currentScript.dataset.preClass;
// Not sure how to create scoped <style>, so instead we'll just
// include a slugid in the className that way the risk of collisions is fairly
// small :)
var btnClassId = slugid.v4();
var style = document.createElement('style');
style.textContent = [
// Largely borrowed from http://purecss.io/buttons/
// Credits to Yahoo for nice buttons released under BSD
".btn-" + btnClassId + "-button {",
" display: inline-block;",
" zoom: 1;",
" line-height: normal;",
" white-space: nowrap;",
" vertical-align: middle;",
" text-align: center;",
" cursor: pointer;",
" -webkit-user-drag: none;",
" -webkit-user-select: none;",
" -moz-user-select: none;",
" -ms-user-select: none;",
" user-select: none;",
" -webkit-box-sizing: border-box;",
" -moz-box-sizing: border-box;",
" box-sizing: border-box;",
" font-family: inherit;",
" font-size: 100%;",
" padding: 0.5em 1em;",
" color: #444; /* rgba not supported (IE 8) */",
" color: rgba(0, 0, 0, 0.80); /* rgba supported */",
" border: 1px solid #999; /*IE 6/7/8*/",
" border: none rgba(0, 0, 0, 0); /*IE9 + everything else*/",
" background-color: #E6E6E6;",
" text-decoration: none;",
" border-radius: 4px;",
" margin-right: 5px;",
" color: white;",
" border-radius: 4px;",
" text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);",
"}",
".btn-" + btnClassId + "-button:hover,",
".btn-" + btnClassId + "-button:focus {",
" filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#1a000000',GradientType=0);",
" background-image: -webkit-gradient(linear, 0 0, 0 100%, from(transparent), color-stop(40%, rgba(0,0,0, 0.05)), to(rgba(0,0,0, 0.10)));",
" background-image: -webkit-linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10));",
" background-image: -moz-linear-gradient(top, rgba(0,0,0, 0.05) 0%, rgba(0,0,0, 0.10));",
" background-image: -o-linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10));",
" background-image: linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10));",
"}",
".btn-" + btnClassId + "-button:focus {",
" outline: 0;",
"}",
".btn-" + btnClassId + "-button:active {",
" box-shadow: 0 0 0 1px rgba(0,0,0, 0.15) inset, 0 0 6px rgba(0,0,0, 0.20) inset;",
" border-color: #000\9;",
"}",
".btn-" + btnClassId + "-button-green {",
" background: #1CB841;",
"}",
".btn-" + btnClassId + "-button-red {",
" background: #CA3C3C;",
"}",
].join('\n');
document.currentScript.parentNode.insertBefore(
style, document.currentScript.parentNode.firstChild
);
var makeButton = function(text, color) {
var btn = document.createElement("a");
btn.innerHTML = text;
btn.classList.add("btn-" + btnClassId + "-button");
btn.classList.add("btn-" + btnClassId + "-button-" + color);
return btn;
};
var cleanEval = function(window, require, console) {
return eval(arguments[3]);
};
var evalCount = 0;
document.addEventListener("DOMContentLoaded", function() {
var nodeList = document.querySelectorAll('pre.' + preClass);
var preList = Array.prototype.slice.call(nodeList);
preList.forEach(function(pre) {
var initialCode = pre.textContent;
pre.style.width = '100%';
var editor = ace.edit(pre);
editor.$blockScrolling = Infinity;
editor.setTheme('ace/theme/ambiance');
editor.setValue(initialCode, 1);
editor.setAutoScrollEditorIntoView(true);
editor.setOption("maxLines", 50);
editor.setOption("showFoldWidgets", false);
editor.setHighlightActiveLine(false);
editor.setHighlightGutterLine(false);
editor.setShowPrintMargin(false);
editor.getSession().setMode('ace/mode/javascript');
editor.getSession().setUseSoftTabs(true);
editor.getSession().setTabSize(2);
editor.getSession().setUseWorker(false);
// Create output
var output = document.createElement("pre");
var outputWrap = document.createElement("div");
output.style.width = '108ex';
outputWrap.style.borderLeft = '8px solid #ccc';
outputWrap.style.paddingLeft = '8px';
outputWrap.appendChild(output);
var capturingConsole = Object.create(console);
var writeOutput = function() {
var args = Array.prototype.slice.call(arguments);
Function.prototype.apply.call(console.log, console, args);
var data = args.map(function(arg) {
if (typeof arg === 'string') {
return arg;
} else if (typeof arg === 'Function') {
return arg.toString();
}
return util.inspect(arg);
});
output.textContent += data + '\n';
};
capturingConsole.log = writeOutput;
capturingConsole.info = writeOutput;
capturingConsole.warn = writeOutput;
capturingConsole.error = writeOutput;
capturingConsole.debug = writeOutput;
capturingConsole.clear = function() {
output.textContent = "";
};
// Create buttons
var buttons = document.createElement("div");
var run = makeButton('Run Code', 'green');
var reset = makeButton('Reset', 'red');
buttons.appendChild(run);
buttons.appendChild(reset);
run.onclick = function() {
capturingConsole.clear();
var value = editor.getValue();
try {
var code = babel.transform([
"(async function example() {",
value,
"})"
].join('\n'), {
stage: 1,
optional: ['runtime'],
filename: 'eval-' + (evalCount++) + '.js',
sourceMaps: 'inline'
}).code;
} catch (err) {
capturingConsole.log("Compilation Error: " + err.message);
if (err.codeFrame) {
capturingConsole.log(err.codeFrame);
}
}
var f = cleanEval(window, require, capturingConsole, code);
var displayError = function(err) {
capturingConsole.log(err.stack);
capturingConsole.log('details: ' + util.inspect(err));
};
setTimeout(function run() {
try {
f().catch(displayError);
} catch (err) {
displayError(err);
}
}, 0);
};
reset.onclick = function() {
editor.setValue(initialCode, 1);
capturingConsole.clear();
};
// Insert buttons and output after pre
var parent = pre.parentNode;
parent.insertBefore(buttons, pre.nextSibling);
parent.insertBefore(outputWrap, buttons.nextSibling);
});
});
{
"name": "interactive-js-pre",
"version": "0.0.1",
"description": "Turn pre-tags into interactive JS editors",
"main": "main.js",
"scripts": {
"build": "browserify main.js | uglifyjs > bundle.min.js",
"develop": "watchify main.js -o bundle.js"
},
"dependencies": {
"brace": "^0.5.1",
"babel-core": "^5.6.15",
"babel-runtime": "^5.6.15",
"taskcluster-client": "^0.22.4",
"slugid": "^1.0.3"
},
"devDependencies": {
"browserify": "^10.2.4",
"watchify": "^3.2.3",
"uglify-js": "^2.4.23"
}
}
<!DOCTYPE html>
<html>
<body>
<script src="bundle.js" data-pre-class="test" charset="UTF-8"></script>
<pre class="test">
console.log("test");
throw new Error("test error");
</pre>
<pre class="test">
let querystring = require('querystring');
let credentials = querystring.parse(window.location.search.substr(1));
if (credentials.clientId && credentials.accessToken) {
// Store credentials on window object for use in later steps
window.tempCreds = {
clientId: credentials.clientId,
accessToken: credentials.accessToken,
certificate: credentials.certificate
};
console.log("TaskCluster credentials loaded from query string,");
console.log("You may now continue to the next step...");
} else {
// If no credentials is present in querystring we redirect you to
// auth.taskcluster.net with current URL as target, so you can redirect back
// with temporary credentials in querystring using the "Grant Access" button
console.log("Redirecting to login in 3 seconds");
// Wait for promise to resolve, and create promise that we resolve 3s later
await new Promise(resolve => setTimeout(resolve, 3000));
window.location = "https://auth.taskcluster.net?" + querystring.stringify({
target: window.location.href,
description: "Redirect back to tutorial with credentials in query string"
});
}
</pre>
<hr>
<pre class="test">
let taskcluster = require('taskcluster-client');
let slugid = require('slugid');
// Create a queue client object w. temporary credentials
let queue = new taskcluster.Queue({
credentials: window.tempCreds
});
// Create new taskId (Random UUID encoded as url-safe base64)
let taskId = slugid.v4();
console.log("Generated taskId: " + taskId);
// Create task
let result = await queue.createTask(taskId, {
provisionerId: "aws-provisioner-v1", // Provisioner to find worker under
workerType: "b2gtest", // WorkerType to run task on
created: new Date().toJSON(),
deadline: taskcluster.fromNowJSON('2 hours'),
payload: { // docker-worker specific payload
image: "ubuntu:15.04", // Use the ubuntu image tag 15.04
command: ["du", "/usr"], // Run the command "du" with argument "/usr"
maxRunTime: 600 // Run for at most 600s == 10min
},
metadata: {
name: "Interactive Tutorial Task",
description: "Task created from **interactive tutorial**...",
owner: "nobody@localhost.local", // Person who caused this task
source: window.location.href // Source for the task definition
}
});
console.log("Created task:\n" + JSON.stringify(result.status, null, 2));
console.log("Inspect it at: https://tools.taskcluster.net/task-inspector/#" + taskId);
</pre>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment