#!/usr/bin/env python
, #!/usr/bin/env ruby
or choose a language template.", false)
176 | $('.inserterHit').click(function() {
177 | var line = $(this).text() + "\n\n"
178 | editor.moveCursorTo(0,0)
179 | editor.insert(line)
180 | editor.focus()
181 | })
182 | return false
183 | }
184 |
185 | // Please add more as you need them and send us a pull request!
186 | var got_lang
187 | editor.getSession().setMode("ace/mode/text")
188 | $.each(languages, function(ix, lang) {
189 | if (first.indexOf(lang.binary) != -1) {
190 | got_lang = lang
191 | editor.getSession().setMode("ace/mode/" + lang.highlight)
192 | return false
193 | }
194 | })
195 |
196 | // Remind them they now need: require 'scraperwiki'
197 | if (got_lang.binary == 'ruby') {
198 | if (code.match(/ScraperWiki\./)) {
199 | if (!code.match(/require\s*\(?\s*['"]scraperwiki['"]/)) {
200 | scraperwiki.alert("You now need to require the ScraperWiki module!", "Add require 'scraperwiki'
to your code. Top tip! You can now install it on any computer with gem install scraperwiki
.")
201 | return false
202 | }
203 | }
204 | }
205 |
206 | return true
207 | }
208 |
209 | // Show status in buttons - we have this as we can call it directly
210 | // to make the run button seem more responsive
211 | var update_display_from_status = function(use_status) {
212 | if (changing == "starting") {
213 | $("#run").attr("disabled", true)
214 | $("#running-status").html("Starting…").show()
215 | } else if (changing == "stopping") {
216 | $("#run").attr("disabled", true)
217 | $("#running-status").html("Stopping…").show()
218 | } else if (use_status == "running") {
219 | $("#run").text("Stop!").removeClass("btn-primary").addClass("btn-danger").attr("disabled", false)
220 | $("#running-status").html("Running…").show()
221 | } else if (use_status == "nothing") {
222 | $("#run").text("Run!").removeClass("btn-danger").addClass("btn-primary").attr("disabled", false)
223 | $("#running-status").hide()
224 | }
225 | }
226 |
227 | // Show/hide things according to current state
228 | var set_status = function(new_status) {
229 | if (!online) {
230 | return
231 | }
232 |
233 | if (new_status != "running" && new_status != "nothing") {
234 | scraperwiki.alert("Unknown new status!", new_status, true)
235 | return
236 | }
237 |
238 |
239 | if (new_status != status) {
240 | console.log("status change", status, "===>", new_status)
241 | if (new_status == "running") {
242 | enrunerate_and_poll_output()
243 | }
244 | status = new_status
245 | changing = ""
246 | }
247 | update_display_from_status(new_status)
248 | }
249 |
250 | // Set schedule menu from status of crontab
251 | var get_schedule_for_display = function() {
252 | $("#schedule-button").addClass("loading")
253 | $("#schedule .icon-ok").hide()
254 | scraperwiki.exec("crontab -l", function(text) {
255 | $("#schedule-button").removeClass("loading")
256 | if (text.match(/no crontab/)) {
257 | $("#schedule-none .icon-ok").show()
258 | console.log("schedule looks like: none")
259 | } else if (text.match(/THIS_IS_HOURLY/)) {
260 | $("#schedule-hourly .icon-ok").show()
261 | console.log("schedule looks like: hourly")
262 | } else if (text.match(/@daily/)) {
263 | $("#schedule-daily .icon-ok").show()
264 | console.log("schedule looks like: daily")
265 | } else if (text.match(/THIS_IS_DAILY/)) {
266 | var matches = text.match(/0 (\d+) \* \* \*/)
267 | $("#schedule-daily-" + matches[1] + " .icon-ok").show()
268 | $("#schedule-daily .icon-ok").show()
269 | console.log("schedule looks like: daily at " + matches[1] + " hour")
270 | } else if (text.match(/THIS_IS_MONTHLY/)) {
271 | $("#schedule-monthly .icon-ok").show()
272 | console.log("schedule looks like: monthly")
273 | } else {
274 | console.log("schedule looks like: custom")
275 | }
276 | }, handle_exec_error)
277 | }
278 |
279 | // When they choose the menu to change schedule
280 | var set_schedule_none = function() {
281 | $("#schedule-button").addClass("loading")
282 | scraperwiki.exec("crontab -r", function(text) {
283 | get_schedule_for_display()
284 | }, handle_exec_error)
285 | }
286 | var set_schedule_daily = function(hour) {
287 | $("#schedule-button").addClass("loading")
288 | scraperwiki.exec("cat tool/crontab-daily | sed s/HOUR/" + hour + "/ | crontab -", function(text) {
289 | get_schedule_for_display()
290 | }, handle_exec_error)
291 | }
292 | var set_schedule_hourly = function() {
293 | $("#schedule-button").addClass("loading")
294 | var minute = Math.floor(60*Math.random())
295 | scraperwiki.exec("cat tool/crontab-hourly | sed s/MINUTE/" + minute + "/ | crontab -", function(text) {
296 | get_schedule_for_display()
297 | }, handle_exec_error)
298 | }
299 | var set_schedule_monthly = function() {
300 | $("#schedule-button").addClass("loading")
301 | scraperwiki.exec("cat tool/crontab-monthly | crontab -", function(text) {
302 | get_schedule_for_display()
303 | }, handle_exec_error)
304 | }
305 |
306 | // Check status of running script, and get more output as appropriate
307 | var enrunerate_and_poll_output = function(action) {
308 | action = action || ""
309 | command = "./tool/enrunerate " + action
310 | if (action == "run") {
311 | command = "(git config --global user.email $(whoami); git config --global user.name Anon; cd code; git init; git add scraper; git commit -am 'Ran code in browser') >/dev/null; " + command
312 | }
313 |
314 | scraperwiki.exec(command, function(text) {
315 | console.log("enrunerate:", text)
316 | online = true
317 | set_status(text)
318 |
319 | // we poll one last time either way to be sure we get end of output...
320 | var again = false
321 | if (text == "running") {
322 | // ... but if script is still "running" we'll trigger the timer to do it again
323 | again = true
324 | }
325 | scraperwiki.exec("touch logs/out; tail --lines=10000 logs/out", function(text) {
326 | // XXX detect no file a better way
327 | if (text != "cat: logs/out: No such file or directory\n") {
328 | output.setValue(text)
329 | }
330 | outputSpinner.stop()
331 | output.clearSelection()
332 | if (again) {
333 | setTimeout(enrunerate_and_poll_output, 10)
334 | }
335 | }, handle_exec_error)
336 | }, handle_exec_error)
337 | }
338 |
339 | // Clear any errors
340 | var clear_alerts = function() {
341 | $(".alert").remove()
342 | }
343 |
344 | // Clear console output
345 | var clear_console = function() {
346 | // only clear if the thing isn't currently running, or would be misleading
347 | if (status == "nothing") {
348 | output.setValue("")
349 | scraperwiki.exec("rm -f logs/out", function(text) {
350 | }, handle_exec_error)
351 | }
352 | }
353 |
354 | // Save the code - optionally takes text of extra commands to also
355 | // in the same "exec" and a callback to run when done
356 | var save_code = function(callback) {
357 | clearTimeout(saveTimeout) // stop any already scheduled timed saves
358 |
359 | console.log("save_code...")
360 | var code = editor.getValue()
361 | var sep = ""
362 | if (code.length == 0 || code.charAt(code.length - 1) != "\n") {
363 | sep = "\n" // we need a separator \n at the end of the file for the ENDOFSCRAPER heredoc below
364 | }
365 | var cmd = "cat >code/scraper.new.$$ <<\"ENDOFSCRAPER\" &&\n" + code + sep + "ENDOFSCRAPER\n"
366 | cmd = cmd + "chmod a+x code/scraper.new.$$ && mv code/scraper.new.$$ code/scraper"
367 | scraperwiki.exec(cmd, function(text) {
368 | if (text != "") {
369 | scraperwiki.alert("Trouble saving code!", text, true)
370 | return
371 | }
372 | online = true
373 |
374 | // Check actual content against saved - in case there was a change while we executed
375 | if (editor.getValue() == code) {
376 | console.log("Saved fine without interference")
377 | update_dirty(false)
378 | } else {
379 | console.log("Ooops, it got dirty while saving")
380 | update_dirty(true)
381 | }
382 |
383 | if (callback) {
384 | callback(text)
385 | }
386 | }, handle_exec_error)
387 | }
388 |
389 | // When the "bugs" button is pressed
390 | var do_bugs = function() {
391 | window.open("https://github.com/scraperwiki/code-scraper-in-browser-tool/issues", "_blank")
392 | }
393 |
394 | // When the "run" button is pressed
395 | var do_run = function() {
396 | // see https://github.com/scraperwiki/code-scraper-in-browser-tool/issues/55
397 | editor.focus()
398 | // force a check that we have a shebang (#!) line
399 | clear_alerts()
400 | var code = editor.getValue()
401 | if (!set_editor_mode(code)) {
402 | return
403 | }
404 |
405 | // if it is already running, stop instead
406 | if (status == "running") {
407 | // make button show what we're doing
408 | changing = "stopping"
409 | update_display_from_status(status)
410 | // stop the running code
411 | enrunerate_and_poll_output("stop")
412 | return
413 | }
414 |
415 | // make button show what we're doing
416 | changing = "starting"
417 | update_display_from_status(status)
418 | output.setValue("")
419 | // save code and run it
420 | if (editorDirty) {
421 | save_code(function (text) {
422 | enrunerate_and_poll_output("run")
423 | })
424 | } else {
425 | enrunerate_and_poll_output("run")
426 | }
427 | }
428 |
429 | // Main entry point
430 | $(document).ready(function() {
431 | settings = scraperwiki.readSettings()
432 |
433 | // Create editor window, read only until it is ready
434 | editor = ace.edit("editor")
435 | editor.setFontSize(16)
436 | editor.getSession().setUseSoftTabs(true)
437 | editor.getSession().setNewLineMode("unix")
438 | editor.setTheme("ace/theme/monokai")
439 | editor.renderer.setShowPrintMargin(false)
440 | editor.on('change', function() {
441 | update_dirty(true)
442 | })
443 | editor.setReadOnly(true)
444 | editorSpinner = new Spinner(spinnerOptsWhite).spin($('#editor')[0])
445 |
446 | // Load initial code
447 | load_code_from_file()
448 |
449 | // Create the console output window
450 | output = ace.edit("output")
451 | output.setFontSize(16)
452 | output.setTheme("ace/theme/clouds")
453 | output.renderer.setShowGutter(false)
454 | output.renderer.setShowPrintMargin(false)
455 | output.setHighlightActiveLine(false)
456 | output.setReadOnly(true)
457 | // ... we use /bin/sh syntax highlighting, the only other at all
458 | // credible option for such varied output is plain text, which is dull.
459 | output.getSession().setMode("ace/mode/sh")
460 | outputSpinner = new Spinner(spinnerOptsBlack).spin($('#output')[0])
461 | enrunerate_and_poll_output()
462 |
463 | // Fill in the language picker
464 | $.each(languages, function(ix, lang) {
465 | var cls = "secondary collapse out"
466 | if ('primary' in lang && lang.primary == true) {
467 | cls = ""
468 | }
469 | $("#languagepicker ul").append('Warning! This will replace your existing code
97 | 98 | 100 | 103 | 104 |