all tests pass in IE8!
[dygraphs.git] / mochikit_v14 / examples / interpreter / interpreter.js
1 /*
2
3 Interpreter: JavaScript Interactive Interpreter
4
5 */
6 InterpreterManager = function () {
7 bindMethods(this);
8 };
9
10 InterpreterManager.prototype.initialize = function () {
11 connect("interpreter_text", "onkeyup", this.keyUp);
12 connect("interpreter_textarea", "onkeydown", this.areaKeyDown);
13 connect("interpreter_form", "onsubmit", this.submit);
14 getElement("interpreter_text").focus();
15
16 this.banner();
17 this.lines = [];
18 this.history = [];
19 this.currentHistory = "";
20 this.historyPos = -1;
21 this.blockingOn = null;
22 if (typeof(this.doEval) == "undefined") {
23 // detect broken eval, warn at some point if a namespace ever gets used
24 this.doEval = function () {
25 return eval(arguments[0]);
26 }
27 }
28 window.help = this.help;
29 this.help.NAME = 'type help(func) for help on a MochiKit function';
30 };
31
32 InterpreterManager.prototype.banner = function () {
33 var _ua = window.navigator.userAgent;
34 var ua = _ua.replace(/^Mozilla\/.*?\(.*?\)\s*/, "");
35 if (ua == "") {
36 // MSIE
37 ua = _ua.replace(/^Mozilla\/4\.0 \(compatible; MS(IE .*?);.*$/, "$1");
38 }
39 appendChildNodes("interpreter_output",
40 SPAN({"class": "banner"},
41 "MochiKit v" + MochiKit.Base.VERSION + " [" + ua + "]",
42 BR(),
43 "Type your expression in the input box below and press return, or see the notes below for more information.",
44 BR()
45 ),
46 BR()
47 );
48 };
49
50 InterpreterManager.prototype.submit = function (event) {
51 if (this.blockingOn) {
52 try {
53 this.blockingOn.cancel();
54 } catch (e) {
55 this.showError(e);
56 }
57 this.blockingOn = null;
58 }
59 this.doSubmit();
60 this.doScroll();
61 event.stop();
62 };
63
64 InterpreterManager.prototype.help = function (fn) {
65 if (fn && fn.NAME) {
66 fn = fn.NAME;
67 }
68 if (typeof(fn) != "string" || fn.length == 0) {
69 writeln("help(func) on any MochiKit function for help");
70 return;
71 }
72 var comps = fn.split('.');
73 var base = comps.splice(0, 2);
74 var shortfn = comps.join('.');
75 var url = '../../doc/html/' + base.join('/') + '.html';
76 var d = doXHR(url, {mimeType: 'text/xml'});
77 d.addCallback(function (req) {
78 var els = getElementsByTagAndClassName(
79 'a', 'mochidef', req.responseXML);
80 var match = '#fn-' + shortfn.toLowerCase();
81 for (var i = 0; i < els.length; i++) {
82 var elem = els[i];
83 var href = elem.href;
84 var idx = href.indexOf('#');
85 if (idx != -1 && href.substring(idx) == match) {
86 writeln(A({href: url + match, target: '_blank'},
87 scrapeText(elem)));
88 return;
89 }
90 }
91 writeln('documentation for ' + fn + ' not found');
92 });
93 blockOn(d);
94 };
95
96
97 InterpreterManager.prototype.doScroll = function () {
98 var p = getElement("interpreter_output").lastChild;
99 if (typeof(p) == "undefined" || p == null) {
100 return;
101 }
102 var area = getElement("interpreter_area");
103 if (area.offsetHeight > area.scrollHeight) {
104 area.scrollTop = 0;
105 } else {
106 area.scrollTop = area.scrollHeight;
107 }
108 };
109
110 InterpreterManager.prototype.moveHistory = function (dir) {
111 // totally bogus value
112 if (dir == 0 || this.history.length == 0) {
113 return;
114 }
115 var elem = getElement("interpreter_text");
116 if (this.historyPos == -1) {
117 this.currentHistory = elem.value;
118 if (dir > 0) {
119 return;
120 }
121 this.historyPos = this.history.length - 1;
122 elem.value = this.history[this.historyPos];
123 return;
124 }
125 if (this.historyPos == 0 && dir < 0) {
126 return;
127 }
128 if (this.historyPos == this.history.length - 1 && dir > 0) {
129 this.historyPos = -1;
130 elem.value = this.currentHistory;
131 return;
132 }
133 this.historyPos += dir;
134 elem.value = this.history[this.historyPos];
135 }
136
137 InterpreterManager.prototype.runMultipleLines = function (text) {
138 var lines = rstrip(text).replace("\r\n", "\n").split(/\n/);
139 appendChildNodes("interpreter_output",
140 SPAN({"class": "code"}, ">>> ", izip(lines, imap(BR, cycle([null]))))
141 );
142 this.runCode(text);
143 }
144
145 InterpreterManager.prototype.areaKeyDown = function (e) {
146 var mod = e.modifier();
147 var hasMod = mod.alt || mod.ctrl || mod.meta;
148 if (e.key().string == 'KEY_ENTER' && hasMod) {
149 var elem = getElement("interpreter_textarea");
150 var text = elem.value;
151 elem.value = "";
152 this.runMultipleLines(text);
153 e.stop();
154 }
155 };
156
157 InterpreterManager.prototype.keyUp = function (e) {
158 var key = e.key();
159 // if any meta key is pressed, don't handle the signal
160 if (e.modifier().any) {
161 return;
162 }
163 switch (key.string) {
164 case 'KEY_ARROW_UP': this.moveHistory(-1); break;
165 case 'KEY_ARROW_DOWN': this.moveHistory(1); break;
166 default: return;
167 }
168 e.stop();
169 };
170
171 InterpreterManager.prototype.blockOn = function (d) {
172 var node = SPAN({"class": "banner"}, "blocking on " + repr(d) + "...");
173 this.blockingOn = d;
174 appendChildNodes("interpreter_output", node);
175 this.doScroll();
176 d.addBoth(function (res) {
177 swapDOM(node);
178 this.blockingOn = null;
179 if (res instanceof CancelledError) {
180 window.writeln(SPAN({"class": "error"}, repr(d) + " cancelled!"));
181 return undefined;
182 }
183 return res;
184 });
185 d.addCallbacks(this.showResult, this.showError);
186 };
187
188 InterpreterManager.prototype.showError = function (e) {
189 if (typeof(e) != "object") {
190 e = new Error(e);
191 }
192 appendChildNodes("interpreter_output",
193 SPAN({"class": "error"}, "Error:"),
194 TABLE({"class": "error"},
195 THEAD({"class": "invisible"}, TD({"colspan": 2})),
196 TFOOT({"class": "invisible"}, TD({"colspan": 2})),
197 TBODY(null,
198 map(function (kv) {
199 var v = kv[1];
200 if (typeof(v) == "function") {
201 return;
202 }
203 if (typeof(v) == "object") {
204 v = repr(v);
205 }
206 return TR(null,
207 TD({"class": "error"}, kv[0]),
208 TD({"class": "data"}, v)
209 );
210 }, sorted(items(e)))
211 )
212 )
213 );
214 window.last_exc = e;
215 this.doScroll();
216 };
217
218 EvalFunctions = {
219 evalWith: function () {
220 with (arguments[1] || window) { return eval(arguments[0]); };
221 },
222 evalCall: function () {
223 return eval.call(arguments[1] || window, arguments[0]);
224 },
225 choose: function () {
226 var ns = {__test__: this};
227 var e;
228 try {
229 if (this.evalWith("return __test__", ns) === this) {
230 return this.evalWith;
231 }
232 } catch (e) {
233 // pass
234 }
235 try {
236 if (this.evalCall("return __test__", ns) === this) {
237 return this.evalCall;
238 }
239 } catch (e) {
240 // pass
241 }
242 return undefined;
243 }
244 };
245
246 InterpreterManager.prototype.doEval = EvalFunctions.choose();
247
248 InterpreterManager.prototype.doSubmit = function () {
249 var elem = getElement("interpreter_text");
250 var code = elem.value;
251 elem.value = "";
252 var isContinuation = false;
253 if (code.length >= 2 && code.lastIndexOf("//") == code.length - 2) {
254 isContinuation = true;
255 code = code.substr(0, code.length - 2);
256 }
257 appendChildNodes("interpreter_output",
258 SPAN({"class": "code"}, ">>> ", code),
259 BR()
260 );
261 this.lines.push(code);
262 this.history.push(code);
263 this.historyPos = -1;
264 this.currentHistory = "";
265 if (isContinuation) {
266 return;
267 }
268 var allCode = this.lines.join("\n");
269 this.lines = [];
270 this.runCode(allCode);
271 return;
272 };
273
274 InterpreterManager.prototype.runCode = function (allCode) {
275 var res;
276 try {
277 res = this.doEval(allCode);
278 } catch (e) {
279 // mozilla shows some keys more than once!
280 this.showError(e);
281 return;
282 }
283 this.showResult(res);
284 };
285
286 InterpreterManager.prototype.showResult = function (res) {
287 if (typeof(res) != "undefined") {
288 window._ = res;
289 }
290 if (typeof(res) != "undefined") {
291 appendChildNodes("interpreter_output",
292 SPAN({"class": "data"}, repr(res)),
293 BR()
294 );
295 this.doScroll();
296 }
297 };
298
299 window.writeln = function () {
300 appendChildNodes("interpreter_output",
301 SPAN({"class": "data"}, arguments),
302 BR()
303 );
304 interpreterManager.doScroll();
305 };
306
307 window.clear = function () {
308 replaceChildNodes("interpreter_output");
309 getElement("interpreter_area").scrollTop = 0;
310 };
311
312 window.blockOn = function (d) {
313 if (!(d instanceof Deferred)) {
314 throw new TypeError(repr(d) + " is not a Deferred!");
315 }
316 interpreterManager.blockOn(d);
317 };
318
319 window.dir = function (o) {
320 // Python muscle memory!
321 return sorted(keys(o));
322 };
323
324 window.inspect = function (o) {
325 window._ = o;
326 if ((typeof(o) != "function" && typeof(o) != "object") || o == null) {
327 window.writeln(repr(o));
328 return;
329 }
330 var pairs = items(o);
331 if (pairs.length == 0) {
332 window.writeln(repr(o));
333 return;
334 }
335 window.writeln(TABLE({"border": "1"},
336 THEAD({"class": "invisible"}, TR(null, TD(), TD())),
337 TFOOT({"class": "invisible"}, TR(null, TD(), TD())),
338 TBODY(null,
339 map(
340 function (kv) {
341 var click = function () {
342 try {
343 window.inspect(kv[1]);
344 } catch (e) {
345 interpreterManager.showError(e);
346 }
347 return false;
348 }
349 return TR(null,
350 TD(null, A({href: "#", onclick: click}, kv[0])),
351 TD(null, repr(kv[1]))
352 );
353 },
354 pairs
355 )
356 )
357 ));
358 };
359
360 interpreterManager = new InterpreterManager();
361 addLoadEvent(interpreterManager.initialize);
362
363 // rewrite the view-source links
364 addLoadEvent(function () {
365 var elems = getElementsByTagAndClassName("A", "view-source");
366 var page = "interpreter/";
367 for (var i = 0; i < elems.length; i++) {
368 var elem = elems[i];
369 var href = elem.href.split(/\//).pop();
370 elem.target = "_blank";
371 elem.href = "../view-source/view-source.html#" + page + href;
372 }
373 });