define(function(require, exports, module) {
main.consumes = ["Evaluator", "ui"];
main.provides = ["immediate.browserjs"];
return main;
/*
Test Cases:
1
"1"
new Error()
window
console.log("1");
throw new Error("1");
Missing:
get/set in object
__proto__
*/
function main(options, imports, register) {
var Evaluator = imports.Evaluator;
var ui = imports.ui;
var escapeHTML = require("ace/lib/lang").escapeHTML;
/***** Initialization *****/
var plugin = new Evaluator("Ajax.org", main.consumes, {
caption: "Javascript (browser)",
id: "jsbrowser",
mode: "ace/mode/javascript",
message: "Welcome to the Javascript REPL. This REPL allows you to "
+ "test any single or multi line code in\na browser based "
+ "javascript environment (iframe). It operates similar to "
+ "your browser console."
});
// var emit = plugin.getEmitter();
var iframe;
var win;
function createIframe() {
if (iframe) return;
iframe = document.body.appendChild(document.createElement("iframe"));
iframe.setAttribute("nwdisable", "nwdisable");
iframe.style.width = "1px";
iframe.style.height = "1px";
iframe.style.position = "absolute";
iframe.style.left = "-100px";
iframe.style.top = "-100px";
win = iframe.contentWindow;
}
/***** Evaluator *****/
var counter = 0;
function Console(cell) {
this.name = "output_section" + (++counter);
this.cell = cell;
this.html = document.createElement("div");
this.cell.addWidget({ el: this.html, coverLine: true, fixedWidth: true });
}
Console.prototype = {
write: function () {
var html = this.html.appendChild(document.createElement("div"));
var type = arguments[arguments.length - 1];
html.className = type;
for (var i = 0; i < arguments.length - 1; i++) {
renderType(arguments[i], html, type != "return");
}
insert(html, "
");
html.updateWidget = this.$update.bind(this);
html.updateWidget();
this.$scrollIntoView();
},
log: function(output) {
var args = Array.prototype.slice.apply(arguments);
args.push("log");
return this.write.apply(this, args);
},
error: function(output) {
var args = Array.prototype.slice.apply(arguments);
args.push("error");
return this.write.apply(this, args);
},
warn: function(output) {
var args = Array.prototype.slice.apply(arguments);
args.push("warning");
return this.write.apply(this, args);
},
$update: function() {
this.cell.session.repl.onWidgetChanged(this.cell);
},
$scrollIntoView: function() {
var editor = this.cell.session.repl.editor;
if (!editor) // tab isn't active
return;
var renderer = editor.renderer;
// TODO add a better way to scroll ace cursor into view when rendered
setTimeout(function() {
renderer.scrollCursorIntoView();
});
}
};
function insert(div, markup, name) {
if (name !== undefined)
insert(div, "" + escapeHTML(name) + ": ");
markup = markup.replace(/([a-z]\w{1,4}:\/\/[\w:_\-\?&\/\.\#]*)/gi, "$1");
div.insertAdjacentHTML("beforeend", markup);
if (div.lastChild && div.lastChild.nodeType == 1) {
var nodes = div.lastChild.querySelectorAll("a");
for (var i = 0; i < nodes.length; i++) {
nodes[i].addEventListener("click", function(e) {
//@todo
alert(this.firstChild.nodeValue);
e.stopPropagation();
});
}
}
}
function insertTree(div, caption, object, drawChildren) {
// caption can be a string or an html element
var treeitem = div.appendChild(document.createElement("span"));
var arrow = treeitem.appendChild(document.createElement("span"));
treeitem.className = "treeitem";
arrow.className = "arrow";
treeitem.appendChild(caption);
var container;
treeitem.addEventListener("click", function(e) {
if (container && ui.isChildOf(container, e.target, true))
return;
e.stopPropagation();
if (!container) {
container = treeitem.appendChild(document.createElement("div"));
container.className = "treecontainer";
container.style.display = "none";
drawChildren(object, container);
}
var collapsed = container.style.display == "none";
arrow.className = "arrow " + (collapsed ? "expanded" : "");
container.style.display = collapsed ? "block" : "none";
// hack!
var target = e.currentTarget;
while (target) {
if (target.updateWidget) {
target.updateWidget();
break;
}
target = target.parentNode;
}
});
}
function parseChildren(object, html) {
if (object instanceof win.Array) {
if (object.length < 101) {
// object.forEach(function(item, i) {
// renderType(item, html, 2, false, i);
// insert(html, "
");
// });
}
else {
}
}
else if (object.$arrayWalker) {
}
else if (object instanceof win.Error) {
var stack = object.stack.split("\n");
stack.shift();
stack = stack.join("
");
insert(html, "
"})
// cell.addWidget({rowCount: 8, el:editor.container, editor: editor})
// var session = cell.session;
// var args = str.trim().split(" ");
// if (evaluator.name && str.indexOf("-a") == -1)
// args.push("-a", evaluator.name);
// cb("Authorization Required");
// cell.insert(data);
// //cell.addWidget({rowCount: 6, html:"" + data + ""});
// cell.insert(pos, "Error: " + data);
// cb(buffer);
var output = new Console(cell);
win.console = output;
var result = evaluateHeadless(expression);
output.write(result, "return");
cell.setWaiting(false);
//cb("Done");
delete win.result;
}
function evaluateHeadless(expression) {
if (!iframe) createIframe();
try {
win.thrown = false;
win.result = win.eval(expression);
} catch (e) {
win.result = e;
win.thrown = true;
}
var result = win.result;
if (win.thrown)
result = { "$$error": result, type: win.thrown };
return result;
}
function getAllProperties(context, callback) {
var results = evaluateHeadless(
"(" + function getAllProperties(obj) {
if (obj == null)
return [];
var results = [];
do {
var props = Object.getOwnPropertyNames(obj);
props.forEach(function(prop) {
if (results.indexOf(prop) === -1)
results.push(prop);
});
props = Object.keys(obj);
props.forEach(function(prop) {
if (results.indexOf(prop) === -1)
results.push(prop);
});
} while ((obj = Object.getPrototypeOf(obj)));
return results;
}.toString() + ")(" + context + ")");
callback(null, results);
}
/***** Lifecycle *****/
plugin.on("load", function() {
});
plugin.on("canEvaluate", function(e) {
return canEvaluate(e.expression);
});
plugin.on("evaluate", function(e) {
return evaluate(e.expression, e.cell, e.callback);
});
plugin.on("enable", function() {
});
plugin.on("disable", function() {
});
plugin.on("unload", function() {
iframe && iframe.remove();
win = iframe = null;
});
/***** Register and define API *****/
/**
*
**/
plugin.freezePublicAPI({
/** @ignore */
evaluateHeadless: evaluateHeadless,
/** @ignore */
getAllProperties: getAllProperties
});
register(null, {
"immediate.browserjs": plugin
});
}
});