Commit | Line | Data |
---|---|---|
6a1aa64f DV |
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 | }); |