jsx
Version:
a faster, safer, easier JavaScript
219 lines (168 loc) • 4.67 kB
JavaScript
window.addEventListener('load', function (e) {
"use strict";
var Lexer = require("jslexer");
require("source-map");
var SOURCE = "source"; // should have data-file="..."
var ORIG = "original";
var GEN = "generated";
var colorMap = {
space: "#ccc", // includes comments
identifier: "#333",
keyword: "#09d",
string: "#d70",
number: "#f60",
regexp: "#660"
};
var skipMap = {
"(": true,
")": true,
",": true
};
var DEBUG = false;
function element(id) {
var item = document.getElementById(id);
if(!item) {
throw new Error("Element not found for id " + id);
}
return item;
}
function elementOffset(elem) {
// point to the center
var valueL = elem.offsetWidth >> 1;
var valueT = elem.offsetHeight >> 1;
// accumulate
do {
valueT += elem.offsetTop | 0;
valueL += elem.offsetLeft | 0;
elem = elem.offsetParent;
} while (elem);
return { x: valueL, y: valueT };
}
function load(name) {
var xhr = new XMLHttpRequest();
xhr.open("GET", name, false);
xhr.send(null);
return xhr.responseText;
}
function format(fmt, __va_args__) {
var args = arguments;
return fmt.replace(/%([0-9]+)/g, function (_unused, n) {
return args[ n | 0 ];
});
}
function asHTML(prefix, tokens) {
var elements = tokens.map(function (tokenObject) {
var s = tokenObject.token.
replace(/&/g, "&").
replace(/</g, "<").
replace(/>/g, ">");
var t = tokenObject.type;
var style = t in colorMap ? "color:" + colorMap[t] : "";
return format('<span id="%1%2" style="%3;" class="%4">%5</span>',
prefix, tokenObject.id, style, t, s);
});
return "<code>" + elements.join("") + "</code>";
}
function initSourcePane(s, p) {
p.style.padding = "0px";
p.style.margin = "1px";
p.style.width = format("%1px", (s.offsetWidth >> 1) - 4);
// TODO: make panes scrollable
//p.style.maxHeight = "800px";
//p.style.overflowY = "scroll";
}
var source = element(SOURCE);
console.assert(source.dataset.file, "source.dataset.file");
initSourcePane(source, element(ORIG));
initSourcePane(source, element(GEN));
var gen = Lexer.tokenize(source.dataset.file, load(source.dataset.file));
var mappingURL;
if(gen[gen.length-1].type === "space") {
var m = gen[gen.length-1].token.
match(/\@ *sourceMappingURL=([^ \t\r\n]+)/);
if(!m) {
return;
}
mappingURL = m[1];
}
console.assert(mappingURL, "sourceMappingURL");
var mapping = JSON.parse(load(mappingURL));
var mainFile = mapping.sourceRoot != null ? mapping.sourceRoot + "/" + mapping.sources[0] : mapping.sources[0];
var orig = Lexer.tokenize(mainFile, load(mainFile));
element(ORIG).innerHTML = asHTML(ORIG, orig);
element(GEN).innerHTML = asHTML(GEN, gen);
var consumer = new sourceMap.SourceMapConsumer(mapping);
var basePos = {
x: source.offsetLeft,
y: source.offsetTop
};
var cx = (function () {
var c = document.createElement('canvas');
if(DEBUG) {
c.style.border = "solid 1px black";
}
c.style.position = "absolute";
c.style.margin = "0px";
c.style.padding = "0px";
c.style.left = source.offsetLeft + "px";
c.style.top = source.offset + "px";
c.width = source.offsetWidth;
c.height = source.offsetHeight;
source.appendChild(c);
return c.getContext("2d");
}());
console.assert(cx, "cx");
var original = [];
orig.forEach(function (tokenObject) {
if(!original[tokenObject.line]) {
original[tokenObject.line] = [];
}
original[tokenObject.line][tokenObject.column] = tokenObject;
});
function getOriginalToken(genToken) {
if(genToken.type === "space") {
return null;
}
if(genToken.token in skipMap) {
return null;
}
var p = consumer.originalPositionFor(genToken);
if(! (original[p.line] && original[p.line][p.column])) {
return null;
}
return original[p.line][p.column];
}
var t0 = Date.now();
var i = 0;
function drawLine() {
var genToken;
var origToken;
do {
if(i >= gen.length) {
if(DEBUG) {
console.debug("finish drawLine by %s ms.", Date.now() - t0);
}
return;
}
genToken = gen[i++];
origToken = getOriginalToken(genToken);
} while (origToken=== null);
setTimeout(drawLine);
if(origToken.done) {
return;
}
origToken.done = true;
cx.beginPath();
// left pane
var elem = element(ORIG + origToken.id);
var p = elementOffset(elem);
cx.moveTo(p.x - basePos.x, p.y - basePos.y);
cx.strokeStyle = elem.style.color;
// to right pane
elem = element(GEN + genToken.id);
p = elementOffset(elem);
cx.lineTo(p.x - basePos.x, p.y - basePos.y);
cx.stroke();
}
setTimeout(drawLine);
});