From 8eb873337ee443ebd987d9d6756eac787054ead2 Mon Sep 17 00:00:00 2001 From: Raynes Date: Thu, 4 Nov 2010 22:32:04 -0500 Subject: [PATCH 001/127] Updated to 1.2 and improved the sandbox, making it more like clojurebot and sexpbot's sandboxes. Closes #9, #8. --- project.clj | 14 +++++++------- src/tryclojure/core.clj | 28 +++++++++++++--------------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/project.clj b/project.clj index 5b90c5d..262ac11 100644 --- a/project.clj +++ b/project.clj @@ -1,12 +1,12 @@ (defproject tryclojure "0.1.0-SNAPSHOT" :description "A simple web-based Clojure REPL for trying out Clojure without having to install it." - :dependencies [[org.clojure/clojure "1.1.0"] - [org.clojure/clojure-contrib "1.1.0"] + :dependencies [[org.clojure/clojure "1.2.0"] + [org.clojure/clojure-contrib "1.2.0"] [net.cgrand/moustache "1.0.0-SNAPSHOT"] - [ring "0.2.5"] + [ring "0.3.3"] [commons-lang/commons-lang "2.5"] - [clj-sandbox "0.3.8"] - [clj-highlight "0.1.1-SNAPSHOT"] - [hiccup "0.2.6"]] + [org.clojars.rayne/clj-sandbox "0.4.2-SNAPSHOT"] + [org.clojars.rayne/clj-highlight "1.1.1-SNAPSHOT"] + [hiccup "0.2.7"]] :dev-dependencies [[swank-clojure "1.2.1"] - [ring/ring-devel "0.2.5"]]) + [ring/ring-devel "0.3.3"]]) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index f6eb808..3bcb923 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -1,30 +1,28 @@ (ns tryclojure.core (:use ring.adapter.jetty - net.licenser.sandbox [net.licenser.sandbox tester matcher] [hiccup core form-helpers page-helpers] [ring.middleware reload stacktrace params file session] net.cgrand.moustache [clojure.stacktrace :only [root-cause]] tryclojure.tutorial) + (:require [net.licenser.sandbox :as sb]) (:import java.io.StringWriter java.util.concurrent.TimeoutException)) -(enable-security-manager) +(sb/enable-security-manager) (def sandbox-tester - (extend-tester secure-tester - (whitelist - (function-matcher 'println 'print 'pr 'prn 'var 'print-doc 'doc 'throw - 'def 'def* 'dosync 'alter '. 'finally '*math-context*) - (class-matcher java.io.StringWriter String Byte Character StrictMath StringBuffer - java.net.URL java.net.URI java.math.MathContext)))) + (new-tester + (whitelist) + (blacklist + (function-matcher 'alter-var-root 'intern 'eval 'catch + 'load-string 'load-reader 'clojure.core/addMethod + 'ns-resolve)))) (def my-obj-tester - (extend-tester default-obj-tester - (whitelist - (class-matcher java.io.StringWriter String Byte Character StrictMath StringBuffer - java.net.URL java.net.URI java.math.MathContext)))) + (extend-tester sb/default-obj-tester + (whitelist))) (def state-tester (new-tester (whitelist (constantly '(true))) @@ -33,11 +31,11 @@ 'swap! 'compare-and-set!)))) (defn has-state? [form] - (not (state-tester form nil))) + (not (state-tester form nil))) (defn execute-text [txt history] - (let [sc (new-sandbox-compiler :tester sandbox-tester - :timeout 1000) + (let [sc (sb/new-sandbox-compiler :tester sandbox-tester + :timeout 1000) result (try (loop [history history] (when (not (empty? history)) From b9f2461e357d0fc2c37bd5ed7e07910501e63392 Mon Sep 17 00:00:00 2001 From: apgwoz Date: Fri, 5 Nov 2010 08:23:23 -0400 Subject: [PATCH 002/127] Various design changes and cleanup * added a new header based on colors from colors and style of the clojure logo * cleaned up the css to make it easier to edit * removed the table for displaying the button links * changed some colors, removed the background images. It still lacks flair, but we'll get there! --- resources/public/clojure-logo.png | Bin 0 -> 4524 bytes resources/public/css/tryclojure.css | 189 ++++++++++++++++++++-------- src/tryclojure/core.clj | 59 ++++----- 3 files changed, 168 insertions(+), 80 deletions(-) create mode 100644 resources/public/clojure-logo.png diff --git a/resources/public/clojure-logo.png b/resources/public/clojure-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..583ebce59b2ba1ad051a3e04840d96cec2533d37 GIT binary patch literal 4524 zcmV;d5mWAoP)05>B> z$a(+;m!l8>E&$sRBkMx|(U~q_rU@bd?gH?y0P3>HPa`~a2SS3&;}<4I$D;uX@I@94 zXECsOhCJ4+;Ybl7uw9a!4(9k=H%-BoKBtfeI{0zXN6!#;8qmdJT{?Z%jc7)0X zMBvNA246Sal^!(L4i_v0W=ONZgr@|Kio&d0CIxmrknILEZfad_L zo$BUsixcIr+^ND_dHinFqiyMYdf4z7OD7E@kCH z5Ch;@#J-lI`BMtd@$>%fw?7?ixZnuo=TgB;$kQ_Z`)(<1`$n^QF#u!2ZHic{{s91S zI`XGWkP*OE02>n0BYWFDM_&8L*%IYx-gE>{7Zw9M_VAtMUp7?hS0GvawgI>YfKRR5 z=@3+Nsla*iL|;(oefIT!bI(xNrdHrgqP$4k_fL0K43-sYmqXdII}rPzJ!<5sC1@pp zcK{U2(u7Zsdb(fv(0R2WUhzR^N?fwZzn`ULSt1{tMgZIk;LU7gWJ}OW0J|Y0$1xT;@Z`(A zi^5UyvKrxBVbZg%7apkXGway}vg8PWHOQhRWkXO6fWwff)y2`ssqeki(Gk;gTFM*^E z0N+5G-Yp$L41o6_>&gQmvHzi8byxU90>iNcMHUT;ts0bAHL#jkxF&cEyQ3I#Md0%C zxblvI^k5mn|MtD8=%;j0|2AOfHbsa+N` z244s`a}jX)C|q8^syq{^zzK=0L~9wCYe^r0x5~ z1GQy{&)vQNumm!HQX)ZjBa0EHKn1bGd4O?vFJQ>mgCSopoc=y&**p~L%TTPZz@1GG z!=z0;EfXokw+>KdB+GX*gk^BIl6n_ju>x;|K~Kg@fH@1b1zNI-BkG1KZjtX+5K4V(}a!`Occ+6i=mw z$1fDNo}4%$OR7VR3jj)jZktK~1*NYX-NIxd1^`yhx(&C^xnK6INOupvh{NtTFc|E> z*8cmsi=mT>_A;nPA2^<3z5Iw`znLnQTCH~a&$E*t3)bt3| zvO1}EeFK#kM%KXy9-xw>&IVK%QWR&B2LwvWqOb^&6X zNCYLR*Uqlj=to_@&r0~5!bMnGu~upZBjGNgZ9H{nI1oI66P|6-OE3gvVeRzCk8NGR zh2ePD+%nrp8miR|_;p)^j?J>M!((KiBtkYMrn1-Y6@1hpVxIrzrYjx4iNusi}=cdU$8}Y}U^P1UEXpA4@}xdV6ka<_!YF zo_M#vw9%wW&4%4kJCw`TWr+lZ4h`IsFqT=Wl3cW;xRc0^1jw!H2bx+Vnc=EdN_P8>t`q7 zjt;;PIwh&uRi&y$D9|&kysj$Msd_%j(^1)FxuOMm!|s3@VYa*y#kH4rs;bN+27?`v zn$_9Kw3dh`F_G{FHlw;m9S93v*`-p&Cc-XN!3Ea5_}oXN0`>DQRh0n%j_A;2UMJZK zyA4FIZXm2cmhH%&7_W?QBPFfQIspZb}YlHl9tA2pB1!BJ{H^V5&$49#^;K&%Sle_A-Onp zg@kw|2C*PX06<}WnJR)j;kZ3BaD^;M?M7|~+AQvd*G_>6Gg z-wH?Q6a@f6KxUVve?AqAd9mlEI{MTRWX$K}X2zi)C|_eVsS)N0jVi89T=vGJ#d-&{ zOg;+$CpHS1;|GfWm zsiUYY1j<#!tCd)Q0wbj7|J>>UaV=LfT2$6vS1hJ^LDegN&0y~g{Ksb*clJiW&3$yzdiw@>|>)RY;* zF>6b{FEAu|NL8y}jy%>Z^%9-lL|74iWcC@*C zt_kawCF%0}K-}f}zU+qPx>*}fW+@v0aBl1hRg8ck>q`HbHk;Q00GiCo$dZxp`Lyzmj=!sTNrMi|?aPT`d8n}x zxMdNcjBJkc^7bKSU?iMYHp>v)c5NYk_T3uMq@QyBxgD3bS+P$){j~hX^Ln0Rbkd6)9)A;IF|~8f9qmVXezR!N z#2wW-4M?4x2*C)LnquJkW}+~x+gn}OanL=rKmcYvi#3a^xc^qWuzaqGNfWE|41m)> z+TaE1dvw$H~jH{I(Y(Db2oR!uqXsd`yt371i2obD)I z`oIByNCYoX6qq@bST!iIX;5O*!fw-uHFg~shGdeZ2wq4V{&IG#ec$t+-?s`XK?s9y z&wU7M>;5KXp5JtDfIu^kavMzF#J5Twrz9sQhAN~Ce*uyMsG?8py~8o&>zR6BlL5;s zZ^FF`wul^~N&0I2!DF;H&_?mW`bAq*Ixz8`&dkF(EnCYybzX)(*vfQ4n(V^P^^q0Cg96bONJ+LG*jh zqR(>%My;iN%XM$mARf`8It&;{2qYIozW=e0*BX40@fkA)#fC~$n(AT6D}ptz7#3X- zL@K})9Ku*|DE2qtz*x|cSy+}R$S>=AZp}7^Axxz#S#=;eFvV5`Z)E(~W4mr>7OC)6 z*@&E=*O(?=xMA0jmR0y<(r!gaXE;T~wlPW}AttSL!4ud1ck4`4_DX|ew8FEi-|I;r zrvtHfOzGauG-&c6)`BGOe4Xu@RZp(`-9Cn}m-WPhnFJvWwp_RMgh^|=MwUE+*t?}& zYoGRpI&WnB*uU((qk1N7(&d1T(~oUgwe>`)vHE&h@{De%n?%xBVQXZ`mt{-lB&OKZOMR~%eWo+lanekJWjOvH7e9Ysf&IFbfMN^7 zW!=;}i5ett3#U$B@1b8Gezan`(~_A&m3j8y6W6}t&*Suos|QzPTlgeW9V`$%JJx>i zr6W%?sr3Tn3Jt4^Y?}MX7uVO`xe!2NaN-p`SYQ$}U!((j9K%ANKcm?C*U~_^kK6=0J49l51fApx2m$ z)wOqbU0=INZ`Kv0IzZ~+W692D`3H(1woUS~<4Kkzv0000< KMNUMnLSTXti;^z@ literal 0 HcmV?d00001 diff --git a/resources/public/css/tryclojure.css b/resources/public/css/tryclojure.css index 9d03de9..cf20489 100644 --- a/resources/public/css/tryclojure.css +++ b/resources/public/css/tryclojure.css @@ -1,54 +1,141 @@ -body { background-color: white; } -div#container { width: 600px; height: 100%; - margin: 50px auto 50px auto; background-color: white;} -h1 { font-size: 32pt; color: black; border-bottom: 1px solid #333; font-family: arial;} -.bottom { background-color: white; color: #333; } -.bottom a,a:visited { color: #111; } -table.bottom { width: 100%; border: 1px solid black;} -div#changer { width: 100%; height: 100%; } -div.buttons { background: transparent url('/resources/public/buttontdbg.png') no-repeat top left; - padding-left: 0.2em; padding-right: 0.2em; text-align: center; } -.buttons a.buttons { border-right: 1px solid #5F665F; color: black; padding-right: 0.3em; - font-family: verdana; font-size: 10pt; font-weight: bold; padding-left: 0.3em;} -a.lbutton { color: black; font-family: verdana; font-size: 10pt; font-weight: bold; - padding-left: 0.3em; padding-right: 0.3em; } -td.bholder { border: 1px solid black; } -div#tuttext { overflow-y: auto; max-height: 300px; } -a.buttons:hover,a.lbutton:hover { color: white; cursor: pointer; } -div.continue { width: 100%; text-align: center; padding-top: 0.5em; } - -p.bottom -{ - padding: 0; - font-family: verdana; - font-size: 10pt; -} -p -{ - color:gray; - font-family:\"Times New Roman\"; - font-size:14px; -} -div.bottom { width: 600px; } -div.footer { width: 100%; border-top: 1px solid black; text-align: center; } -div.console div.jquery-console-inner -{ width:595px; height:300px; background:#efefef; padding-left: 3px; padding-top: 3px; - overflow:auto; text-align:left; border: 1px solid black; - background: transparent url('/resources/public/wtf.png') no-repeat top left; } -div.console div.jquery-console-message-value -{ color:#0066FF; font-family:monospace; - padding:0.1em; } -div.console div.jquery-console-prompt-box -{ color:#444; font-family:monospace; } -div.console div.jquery-console-focus span.jquery-console-cursor -{ background:#333; color:#eee; font-weight:bold } -div.console div.jquery-console-message-error -{ color:#ef0505; font-family:sans-serif; font-weight:bold; - padding:0.1em; } -div.console div.jquery-console-message-success -{ color:#187718; font-family:monospace; - padding:0.1em; } -div.console span.jquery-console-prompt-label { font-weight:bold } +body { + background: #fff; + text-align: center; +} + +#wrapper { + text-align: left; + width: 600px; + margin: 0 auto; +} + +#header { + background: #fff; + margin-top: 24px; +} + +#header h1 { + height: 72px; + width: 72px; + line-height: 72px; + background: url(/resources/public/clojure-logo.png) no-repeat; + margin: 0; + padding: 0; + padding-left: 75px; + font-family: Arial, sans-serif; + font-weight: normal; + font-size: 48px; +} + +#header h1 span.logo-try { + color: #63b132; +} + +#header h1 span.logo-clojure { + color: #5881d8; +} + +#console { + margin-top: 24px; +} + +#console div.jquery-console-inner { + width:595px; + height:300px; + background:#efefef; + padding-left: 3px; + padding-top: 3px; + overflow:auto; + text-align:left; + border: 1px solid black; + background: transparent; +} +#console div.jquery-console-message-value { + color:#0066FF; + font-family:monospace; + padding:0.1em; +} +#console div.jquery-console-prompt-box { + color:#444; font-family:monospace; +} +#console div.jquery-console-focus span.jquery-console-cursor { + background:#333; color:#eee; font-weight:bold +} +#console div.jquery-console-message-error { + color:#ef0505; font-family:sans-serif; font-weight:bold; + padding:0.1em; +} +#console div.jquery-console-message-success { + color:#187718; font-family:monospace; + padding:0.1em; +} +#console span.jquery-console-prompt-label { + font-weight:bold +} + +.bottom { + background-color: white; color: #333; +} +.bottom a,a:visited { + color: #111; +} +table.bottom { + width: 100%; border: 1px solid black; +} +div#changer { + width: 100%; + height: 100%; +} + +#buttons { + background: transparent; + text-align: center; + margin-top: 10px; +} +#buttons a { +/* color: #4881d8;*/ + color: #3D5B99; + padding: .3em 1em; + margin-right: .7em; + font-family: Helvetica, sans-serif; + font-size: 16px; + font-weight: bold; + background: #90b4fe; +} +#buttons a.last { + margin-right: 0; +} +#buttons a:hover { + color: #5881d8; + cursor: pointer; +} + +#changer { + margin-top: 10px; + padding: 10px; + background: #EDF1FA; +} + +#changer p { + background: transparent; + line-height: 1.3em; + font-family: Arial, serif; + font-size: 16px; + color: #555; +} + +div#tuttext { +} + +div.continue { + width: 100%; + text-align: center; + padding-top: 0.5em; +} + +div.footer { + text-align: center; +} /* Coderay alpha style */ diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 3bcb923..a1ca960 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -31,7 +31,7 @@ 'swap! 'compare-and-set!)))) (defn has-state? [form] - (not (state-tester form nil))) + (not (state-tester form nil))) (defn execute-text [txt history] (let [sc (sb/new-sandbox-compiler :tester sandbox-tester @@ -110,35 +110,36 @@ "/resources/public/javascript/tryclojure.js") [:title "TryClojure"]] [:body - [:script {:type "text/javascript"} - " - var _gaq = _gaq || []; - _gaq.push(['_setAccount', 'UA-552543-3']); - _gaq.push(['_trackPageview']); - + [:div#wrapper + [:div#content + [:div#header + [:h1 + [:span.logo-try "Try"] + [:span.logo-clojure "Clo" [:em "j"] "ure"]]] + [:div#container + [:div#console.console] + [:div#buttons + [:a#tutorial.buttons "tutorial"] + [:a#links.buttons "links"] + [:a#about.buttons.last "about"]] + [:div#changer + [:p.bottom + "Welcome to TryClojure. Above, you have a Clojure REPL. You can type expressions and see " + "their results right here in your browser. We also have a brief tutorial to give you a " + "taste of Clojure. Try it out!"]]] + [:div.footer + [:p.bottom "©2010 Anthony Simpson (Raynes)"] + [:p.bottom "Domain and hosting kindly provided by " + (link-to "http://blog.licenser.net" "Heinz N. Gies") "."]]] + [:script {:type "text/javascript"} + "var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-552543-3']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); "] - [:div {:style "text-align: center;"} [:h1 "Try Clojure"]] - [:div#container [:div#console.console] - [:table.bottom {:border "0"} - [:tr] - [:td.bholder [:div.buttons - [:a#tutorial.buttons "tutorial"] - [:a#links.buttons "links"] - [:a#about.lbutton "about"]]] - [:tr] - [:td [:div#changer [:p.bottom - "Welcome to TryClojure. Above, you have a Clojure REPL. You can type expressions and see " - "their results right here in your browser. We also have a brief tutorial to give you a " - "taste of Clojure. Try it out!"]]]]] - [:div.footer - [:p.bottom "©2010 Anthony Simpson (Raynes)"] - [:p.bottom "Domain and hosting kindly provided by " - (link-to "http://blog.licenser.net" "Heinz N. Gies") "."]]])) +]])) (defn handler [{session :session}] {:status 200 @@ -147,11 +148,11 @@ :body fire-html}) (defn div-handler [{qparams :query-params session :session}] - (let [[result history] (execute-text (qparams "code") (or (:history session) []))] - {:status 200 - :headers {"Content-Type" "text/txt"} - :session {:history history} - :body result})) + (let [[result history] (execute-text (qparams "code") (or (:history session) []))] + {:status 200 + :headers {"Content-Type" "text/txt"} + :session {:history history} + :body result})) (defn about-handler [{session :session}] {:status 200 From 6d13441d87f55c6d2029a5c5b82c5654a2af3e5d Mon Sep 17 00:00:00 2001 From: Raynes Date: Fri, 5 Nov 2010 13:39:33 -0500 Subject: [PATCH 003/127] Added note about running to README. --- README | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README b/README index 57f30c5..648fabd 100644 --- a/README +++ b/README @@ -8,6 +8,8 @@ http://www.try-clojure.org Many thanks to Licenser for giving me a place to put it. +If you want to run it, refer to https://github.com/Raynes/tryclojure/wiki/Running-Tryclojure + ## License Licensed under the same thing Clojure is licensed under: the EPL, of which you can find a copy at the root of this directory. From dde32c9557ab61377b8db713859c3afd7eca1c77 Mon Sep 17 00:00:00 2001 From: Raynes Date: Fri, 5 Nov 2010 13:40:12 -0500 Subject: [PATCH 004/127] Added an example .policy file. --- example.policy | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 example.policy diff --git a/example.policy b/example.policy new file mode 100644 index 0000000..dc85222 --- /dev/null +++ b/example.policy @@ -0,0 +1,3 @@ +grant { + permission java.security.AllPermission; +}; From 41b8b8affb9415e4c91c408f63008c2f76ba4f5f Mon Sep 17 00:00:00 2001 From: Andrew Gwozdziewycz Date: Fri, 5 Nov 2010 17:17:39 -0400 Subject: [PATCH 005/127] * added rounded corners :) --- resources/public/css/tryclojure.css | 29 +++++++++++++++++++---------- src/tryclojure/core.clj | 5 +++-- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/resources/public/css/tryclojure.css b/resources/public/css/tryclojure.css index cf20489..a79b83e 100644 --- a/resources/public/css/tryclojure.css +++ b/resources/public/css/tryclojure.css @@ -36,19 +36,22 @@ body { } #console { + height: 320px; + background: #efefef; margin-top: 24px; + border-radius: 5px; + -moz-border-radius: 5px; + margin-left: auto; + margin-right: auto; } #console div.jquery-console-inner { - width:595px; + width:580px; height:300px; - background:#efefef; - padding-left: 3px; - padding-top: 3px; + margin: 10px 10px; + padding: 10px 0; overflow:auto; text-align:left; - border: 1px solid black; - background: transparent; } #console div.jquery-console-message-value { color:#0066FF; @@ -59,7 +62,7 @@ body { color:#444; font-family:monospace; } #console div.jquery-console-focus span.jquery-console-cursor { - background:#333; color:#eee; font-weight:bold + background:#333; color:#eee; font-weight:bold; } #console div.jquery-console-message-error { color:#ef0505; font-family:sans-serif; font-weight:bold; @@ -70,7 +73,7 @@ body { padding:0.1em; } #console span.jquery-console-prompt-label { - font-weight:bold + font-weight:bold; } .bottom { @@ -101,6 +104,8 @@ div#changer { font-size: 16px; font-weight: bold; background: #90b4fe; + border-radius: 5px; + -moz-border-radius: 5px; } #buttons a.last { margin-right: 0; @@ -112,11 +117,15 @@ div#changer { #changer { margin-top: 10px; - padding: 10px; + padding-top: 5px; background: #EDF1FA; + border-radius: 5px; + -moz-border-radius: 5px; } #changer p { + margin-left: 10px; + margin-right: 10px; background: transparent; line-height: 1.3em; font-family: Arial, serif; @@ -124,7 +133,7 @@ div#changer { color: #555; } -div#tuttext { +div#tuttext { } div.continue { diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index a1ca960..14b7851 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -114,7 +114,8 @@ [:div#content [:div#header [:h1 - [:span.logo-try "Try"] + [:span.logo-try "Try"] + " " [:span.logo-clojure "Clo" [:em "j"] "ure"]]] [:div#container [:div#console.console] @@ -124,7 +125,7 @@ [:a#about.buttons.last "about"]] [:div#changer [:p.bottom - "Welcome to TryClojure. Above, you have a Clojure REPL. You can type expressions and see " + "Welcome to Try Clojure. Above, you have a Clojure REPL. You can type expressions and see " "their results right here in your browser. We also have a brief tutorial to give you a " "taste of Clojure. Try it out!"]]] [:div.footer From 94cd516eb06ca860ea36af75b6136b9553b39711 Mon Sep 17 00:00:00 2001 From: apgwoz Date: Sun, 7 Nov 2010 09:30:51 -0500 Subject: [PATCH 006/127] * more style changes, TryClojure becomes Try Clojure --- resources/public/css/tryclojure.css | 159 ++++++++-------------------- src/tryclojure/core.clj | 5 +- 2 files changed, 49 insertions(+), 115 deletions(-) diff --git a/resources/public/css/tryclojure.css b/resources/public/css/tryclojure.css index a79b83e..dec771b 100644 --- a/resources/public/css/tryclojure.css +++ b/resources/public/css/tryclojure.css @@ -5,18 +5,20 @@ body { #wrapper { text-align: left; - width: 600px; + width: 620px; margin: 0 auto; + border-radius: 5px; + border: 5px solid #eee; } #header { background: #fff; margin-top: 24px; + margin: 10px; } #header h1 { height: 72px; - width: 72px; line-height: 72px; background: url(/resources/public/clojure-logo.png) no-repeat; margin: 0; @@ -29,25 +31,26 @@ body { #header h1 span.logo-try { color: #63b132; + display:inline; } #header h1 span.logo-clojure { color: #5881d8; + display:inline } #console { - height: 320px; - background: #efefef; - margin-top: 24px; + height: 220px; + background: #eee; + margin: 10px; border-radius: 5px; -moz-border-radius: 5px; - margin-left: auto; - margin-right: auto; + border: 1px solid #aaa; } #console div.jquery-console-inner { width:580px; - height:300px; + height:200px; margin: 10px 10px; padding: 10px 0; overflow:auto; @@ -85,10 +88,6 @@ body { table.bottom { width: 100%; border: 1px solid black; } -div#changer { - width: 100%; - height: 100%; -} #buttons { background: transparent; @@ -116,11 +115,12 @@ div#changer { } #changer { - margin-top: 10px; - padding-top: 5px; + margin: 10px; + padding: 1px 0; background: #EDF1FA; border-radius: 5px; -moz-border-radius: 5px; + border: 1px solid #90b4fe; } #changer p { @@ -149,9 +149,9 @@ div.footer { /* Coderay alpha style */ .code { - background-color: #f8f8f8; - border: 1px solid silver; -/* font-family: 'Courier New', 'Terminal', monospace; */ + #999; + border-radius: 2px; + -moz-border-radius: 1px; color: #000; } .code pre { margin: 0px; } @@ -167,105 +167,40 @@ table.code td { padding: 2px 4px; vertical-align: top; } color: gray; text-align: right; } -.code .line_numbers a:target, .code .no a:target { color: blue; } -.code .line_numbers .highlighted, .code .no .highlighted { color: red; } -.code .no { padding: 0px 4px; } -.code .code { width: 100%; } -.code .code pre { overflow: auto; } - -.debug { color:white ! important; background:blue ! important; } - -.an { color:#007 } -.at { color:#f08 } -.av { color:#700 } -.bi { color:#509; font-weight:bold } -.c { color:#888; } -.c .dl { color:#444; } -.c .ch { color:#444; } - -.ch { color:#04D } -.ch .k { color:#04D } -.ch .dl { color:#039 } - -.cl { color:#B06; font-weight:bold } -.cm { color:#A08; font-weight:bold } -.co { color:#036; font-weight:bold } -.cr { color:#0A0 } -.cv { color:#369 } -.de { color:#B0B; } -.df { color:#099; font-weight:bold } -.di { color:#088; font-weight:bold } -.dl { color:black } -.do { color:#970 } -.dt { color:#34b } -.ds { color:#D42; font-weight:bold } -.e { color:#666; font-weight:bold } -.en { color:#800; font-weight:bold } -.er { color:#F00; background-color:#FAA } -.ex { color:#C00; font-weight:bold } -.fl { color:#60E; font-weight:bold } -.fu { color:#06B; font-weight:bold } -.gv { color:#d70; font-weight:bold } -.hx { color:#058; font-weight:bold } -.i { color:#00D; font-weight:bold } -.ic { color:#B44; font-weight:bold } - -.il { background-color: hsla(0,0%,0%,0.1); color: black } -.il .idl { font-weight: bold; color: #666 } -.idl { font-weight: bold; color: #666; } -.im { color:#f00; } -.in { color:#B2B; font-weight:bold } -.iv { color:#33B } -.la { color:#970; font-weight:bold } -.lv { color:#963 } -.ns { color:#707; font-weight:bold } -.oc { color:#40E; font-weight:bold } -.op { } -.pc { color:#058; font-weight:bold } -.pd { color:#369; font-weight:bold } -.pp { color:#579; } -.ps { color:#00C; font-weight:bold } -.pt { color:#074; font-weight:bold } -.r, .kw { color:#080; font-weight:bold } - -.ke { color: #808; } -.ke .dl { color: #606; } -.ke .ch { color: #80f; } -.vl { color: #088; } - -.rx { background-color:hsla(300,100%,50%,0.1); color:#808 } -.rx .k { } -.rx .dl { color:#404 } -.rx .mod { color:#C2C } -.rx .fu { color:#404; font-weight: bold } +/* keywords */ +.code span.r { + color: #0000ff; + font-weight: bold; +} -.s { color: #D20; } -.s .k { } -.s .ch { color: #b0b; } -.s .dl { color: #710; } +/* symbols */ +.code span.sy { + color: #318495; +} -.sh { background-color:hsla(120,100%,50%,0.1); color:#2B2 } -.sh .k { } -.sh .dl { color:#161 } +/* strings */ +.code span.s { + color: #666666; +} -.sy { color:#A60 } -.sy .k { color:#A60 } -.sy .dl { color:#630 } +/* paren */ +.code span.of { + color: #70727E; + font-weight: bold; +} -.ta { color:#070 } -.ts { color:#D70; font-weight:bold } -.ty { color:#339; font-weight:bold } -.v { color:#036 } -.xt { color:#444 } +/* comment */ +.code span.c { + color: #ccc; +} -.ins { background: #afa; } -.del { background: #faa; } -.chg { color: #aaf; background: #007; } -.head { color: #f8f; background: #505 } -.head .filename { color: white; } +/* operator */ +.code span.cl { + +} -.ins .ins { color: #080; font-weight:bold } -.del .del { color: #800; font-weight:bold } -.chg .chg { color: #66f; } -.head .head { color: #f4f; } \ No newline at end of file +/* number */ +.code span.i { + color: #ff0000; +} \ No newline at end of file diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 14b7851..cfa2cfd 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -108,14 +108,13 @@ (include-js "/resources/public/javascript/jquery-1.4.2.min.js" "/resources/public/javascript/jquery.console.js" "/resources/public/javascript/tryclojure.js") - [:title "TryClojure"]] + [:title "Try Clojure"]] [:body [:div#wrapper [:div#content [:div#header [:h1 - [:span.logo-try "Try"] - " " + [:span.logo-try "Try"] " " [:span.logo-clojure "Clo" [:em "j"] "ure"]]] [:div#container [:div#console.console] From 46e0066bb2e239e443b3c8c6e50fe51c87065184 Mon Sep 17 00:00:00 2001 From: apgwoz Date: Sun, 7 Nov 2010 17:59:56 -0500 Subject: [PATCH 007/127] * more tweaks --- resources/public/css/tryclojure.css | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/resources/public/css/tryclojure.css b/resources/public/css/tryclojure.css index dec771b..b7fb73e 100644 --- a/resources/public/css/tryclojure.css +++ b/resources/public/css/tryclojure.css @@ -52,7 +52,6 @@ body { width:580px; height:200px; margin: 10px 10px; - padding: 10px 0; overflow:auto; text-align:left; } @@ -117,10 +116,10 @@ table.bottom { #changer { margin: 10px; padding: 1px 0; - background: #EDF1FA; + background: #EAF2F5; border-radius: 5px; -moz-border-radius: 5px; - border: 1px solid #90b4fe; + border: 1px solid #BEDCE7; } #changer p { From f41df6c983b599c1b8bf8d98d432b765778e3849 Mon Sep 17 00:00:00 2001 From: apgwoz Date: Sun, 7 Nov 2010 18:57:51 -0500 Subject: [PATCH 008/127] * syntax colors --- resources/public/css/tryclojure.css | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/resources/public/css/tryclojure.css b/resources/public/css/tryclojure.css index b7fb73e..82fbbff 100644 --- a/resources/public/css/tryclojure.css +++ b/resources/public/css/tryclojure.css @@ -146,12 +146,10 @@ div.footer { } /* Coderay alpha style */ - .code { - #999; - border-radius: 2px; - -moz-border-radius: 1px; - color: #000; + border-radius: 2px; + -moz-border-radius: 1px; + color: #000; } .code pre { margin: 0px; } @@ -168,38 +166,38 @@ table.code td { padding: 2px 4px; vertical-align: top; } } /* keywords */ -.code span.r { +#changer .code span.r { color: #0000ff; font-weight: bold; } /* symbols */ -.code span.sy { +#changer .code span.sy { color: #318495; } /* strings */ -.code span.s { - color: #666666; +#changer .code span.s { + color: #008800; } /* paren */ -.code span.of { - color: #70727E; +#changer .code span.of { + color: #222; font-weight: bold; } /* comment */ -.code span.c { +#changer .code span.c { color: #ccc; } /* operator */ -.code span.cl { +#changer .code span.cl { } /* number */ -.code span.i { +#changer .code span.i { color: #ff0000; -} \ No newline at end of file +} From db70ee88758daceaf3291e2d81d97a21c6fa0d94 Mon Sep 17 00:00:00 2001 From: Raynes Date: Sun, 7 Nov 2010 18:10:09 -0600 Subject: [PATCH 009/127] Added credit. --- README | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README b/README index 648fabd..a03a859 100644 --- a/README +++ b/README @@ -10,6 +10,10 @@ Many thanks to Licenser for giving me a place to put it. If you want to run it, refer to https://github.com/Raynes/tryclojure/wiki/Running-Tryclojure +## Credits + +apgwoz: Design + ## License Licensed under the same thing Clojure is licensed under: the EPL, of which you can find a copy at the root of this directory. From 4348d2c13cbdbadd7de34c4a194385bbda08997b Mon Sep 17 00:00:00 2001 From: Raynes Date: Sun, 7 Nov 2010 18:15:09 -0600 Subject: [PATCH 010/127] And contributors. --- src/tryclojure/core.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index cfa2cfd..00a575a 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -128,7 +128,7 @@ "their results right here in your browser. We also have a brief tutorial to give you a " "taste of Clojure. Try it out!"]]] [:div.footer - [:p.bottom "©2010 Anthony Simpson (Raynes)"] + [:p.bottom "©2010 Anthony Simpson (Raynes) and contributors"] [:p.bottom "Domain and hosting kindly provided by " (link-to "http://blog.licenser.net" "Heinz N. Gies") "."]]] [:script {:type "text/javascript"} From ea78113301cd9bf83de3071bc6b8e9dda803df13 Mon Sep 17 00:00:00 2001 From: Raynes Date: Sat, 13 Nov 2010 03:13:18 -0600 Subject: [PATCH 011/127] Running off of clojail now. --- project.clj | 1 + src/tryclojure/core.clj | 70 +++++++++++++---------------------------- 2 files changed, 22 insertions(+), 49 deletions(-) diff --git a/project.clj b/project.clj index 262ac11..dd5a3f5 100644 --- a/project.clj +++ b/project.clj @@ -5,6 +5,7 @@ [net.cgrand/moustache "1.0.0-SNAPSHOT"] [ring "0.3.3"] [commons-lang/commons-lang "2.5"] + [clojail "0.2.0-SNAPSHOT"] [org.clojars.rayne/clj-sandbox "0.4.2-SNAPSHOT"] [org.clojars.rayne/clj-highlight "1.1.1-SNAPSHOT"] [hiccup "0.2.7"]] diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 00a575a..5909995 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -5,64 +5,36 @@ [ring.middleware reload stacktrace params file session] net.cgrand.moustache [clojure.stacktrace :only [root-cause]] - tryclojure.tutorial) - (:require [net.licenser.sandbox :as sb]) + [clojail core testers] + tryclojure.tutorial + [clojure.set :only [difference]]) (:import java.io.StringWriter java.util.concurrent.TimeoutException)) -(sb/enable-security-manager) +(def sb-tester (difference secure-tester #{'def})) -(def sandbox-tester - (new-tester - (whitelist) - (blacklist - (function-matcher 'alter-var-root 'intern 'eval 'catch - 'load-string 'load-reader 'clojure.core/addMethod - 'ns-resolve)))) - -(def my-obj-tester - (extend-tester sb/default-obj-tester - (whitelist))) - -(def state-tester - (new-tester (whitelist (constantly '(true))) - (blacklist (function-matcher 'def 'def* - 'ensure 'ref-set 'alter 'commute - 'swap! 'compare-and-set!)))) +(def state-tester #{'def 'def* 'ensure 'ref-set 'alter 'commute 'swap! 'compare-and-set!}) (defn has-state? [form] - (not (state-tester form nil))) + (check-form form state-tester)) (defn execute-text [txt history] - (let [sc (sb/new-sandbox-compiler :tester sandbox-tester - :timeout 1000) + (let [sb (sandbox sb-tester :timeout 3000) + history (if (= 5 (count history)) (drop 1 history) history) result (try - (loop [history history] - (when (not (empty? history)) - (do - ((sc (first history))) - (recur (next history))))) - (let [form (binding [*read-eval* false] (read-string txt))] - (with-open [writer (java.io.StringWriter.)] - (let [r (pr-str ((sc form) {'*out* writer}))] - [(str (.replace (escape-html writer) "\n" "
") (code (str r))) - (if (has-state? form) (conj history form) history)]))) - (catch OutOfMemoryError _ ["Out of memory error was thrown. Cleaning up all defs." nil]) - (catch TimeoutException _ ["Execution Timed Out!" history]) - (catch SecurityException e - [(if (.startsWith - (.getMessage e) - "Code did not pass sandbox guidelines: ") - (str e - "

This error was caused because you tried to use a function that " - " isn't whitelisted in the sandbox. The sandbox's whitelist is probably missing some" - " useful functions that should be whitelisted. If you think the function you tried to " - "use is safe and should be whitelisted, please file an issue at " - "http://github.com/Raynes/tryclojure/issues or mention it to Raynes on the " - "#clojure or #clojure-casual IRC channels on the FreeNode network.") - (str (root-cause e))) - history]) - (catch Exception e [(str (root-cause e)) history]))] + (loop [history history] + (when (not (empty? history)) + (do + (sb (first history)) + (recur (next history))))) + (let [form (binding [*read-eval* false] (read-string txt))] + (with-open [writer (java.io.StringWriter.)] + (let [r (pr-str (sb form {#'*out* writer}))] + [(str (.replace (escape-html writer) "\n" "
") (code (str r))) + (if (has-state? form) (conj history form) history)]))) + (catch OutOfMemoryError _ ["Out of memory error was thrown. Cleaning up all defs." nil]) + (catch TimeoutException _ ["Execution Timed Out!" history]) + (catch Exception e [(str (root-cause e)) history]))] result)) (def links From 88988893749c8373d835b435d30b4fa2f9ed9266 Mon Sep 17 00:00:00 2001 From: Raynes Date: Sat, 13 Nov 2010 03:23:29 -0600 Subject: [PATCH 012/127] Changed the about page. --- src/tryclojure/core.clj | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 5909995..9730ec0 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -54,14 +54,12 @@ " You can find the site's source and such on it's " (link-to "http://github.com/Raynes/tryclojure" "github") " page."] [:p.bottom - "This website is mostly going to be useful for beginners. All of the code that is ran is ran" - " server side. This means it *has* to be sandboxed. This also means that this web REPL is NOT" - " ever going to be as useful as a normal REPL ran on your computer. I'm considering throwing up" - " another site that runs a pretty Clojure REPL through a typical Java applet. That one would be" - " client-side, and would not be sandboxed."] + "Please note that this REPL is sandboxed, so you wont be able to do everything in it " + "that you would in a local unsandboxed REPL. Keep in mind that this site is designed for " + "beginners to try out Clojure and not necessarily as a general-purpose server-side REPL."] [:p.bottom "TryClojure is written in Clojure and JavaScript, powered by " - (link-to "http://github.com/Licenser/clj-sandbox" "clj-sandbox") + (link-to "http://github.com/Raynes/clojail" "clojail") " and Chris Done's " (link-to "http://github.com/chrisdone/jquery-console" "jquery-console")] [:p.bottom @@ -69,8 +67,7 @@ " for lots of help with everything ranging from Gimp, to straight up CSS and HTML design tips."] [:p.bottom "Also thanks to my buddy Heinz (Licenser) for all the help with this site. This site is hosted on" - " his server, with his domain name. He completely funds this project and it wouldn't be possible without" - " him. Not to mention his wonderful clj-sandbox and clj-highlight libraries make this site possible!"])) + " his server, with his domain name. He completely funds this project and it wouldn't be possible without him."])) (def fire-html (html From 86613393709d2891af8845da7adedb492d9aebe4 Mon Sep 17 00:00:00 2001 From: Raynes Date: Thu, 30 Dec 2010 20:11:45 -0600 Subject: [PATCH 013/127] Update clojails dep. --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index dd5a3f5..b4cd644 100644 --- a/project.clj +++ b/project.clj @@ -5,7 +5,7 @@ [net.cgrand/moustache "1.0.0-SNAPSHOT"] [ring "0.3.3"] [commons-lang/commons-lang "2.5"] - [clojail "0.2.0-SNAPSHOT"] + [clojail "0.2.2-SNAPSHOT"] [org.clojars.rayne/clj-sandbox "0.4.2-SNAPSHOT"] [org.clojars.rayne/clj-highlight "1.1.1-SNAPSHOT"] [hiccup "0.2.7"]] From 024895ceaf14b13abb9a10639f9dc65549232a05 Mon Sep 17 00:00:00 2001 From: Raynes Date: Wed, 19 Jan 2011 11:02:07 -0600 Subject: [PATCH 014/127] Exclude buggy dependency and update clojail dep. --- project.clj | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/project.clj b/project.clj index b4cd644..1dd384f 100644 --- a/project.clj +++ b/project.clj @@ -2,12 +2,11 @@ :description "A simple web-based Clojure REPL for trying out Clojure without having to install it." :dependencies [[org.clojure/clojure "1.2.0"] [org.clojure/clojure-contrib "1.2.0"] - [net.cgrand/moustache "1.0.0-SNAPSHOT"] + [net.cgrand/moustache "1.0.0-SNAPSHOT" :exclusions [org.clojure/clojure]] [ring "0.3.3"] [commons-lang/commons-lang "2.5"] - [clojail "0.2.2-SNAPSHOT"] - [org.clojars.rayne/clj-sandbox "0.4.2-SNAPSHOT"] + [clojail "0.3.4-SNAPSHOT"] [org.clojars.rayne/clj-highlight "1.1.1-SNAPSHOT"] [hiccup "0.2.7"]] :dev-dependencies [[swank-clojure "1.2.1"] - [ring/ring-devel "0.3.3"]]) + [ring/ring-devel "0.3.3"]]) \ No newline at end of file From cfbe84077a0e254a101401cc17f2ff33df8d3dbf Mon Sep 17 00:00:00 2001 From: Raynes Date: Sat, 22 Jan 2011 19:03:09 -0600 Subject: [PATCH 015/127] Remove references to remove dependency. --- src/tryclojure/core.clj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 9730ec0..8b0cfc7 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -1,6 +1,5 @@ (ns tryclojure.core (:use ring.adapter.jetty - [net.licenser.sandbox tester matcher] [hiccup core form-helpers page-helpers] [ring.middleware reload stacktrace params file session] net.cgrand.moustache From d2acd09cf1586443b5dafbcc6f0ccdaba440b7f6 Mon Sep 17 00:00:00 2001 From: mefesto Date: Sun, 23 Jan 2011 23:58:30 -0500 Subject: [PATCH 016/127] Added basic support for an interactive tutorial. tryclojure.js contains a pages array where tutorial pages can be added. When the user types 'tutorial' at the console it will render page 1 which expects them to enter a specific form. core.clj was updated with a /eval.json handler. This handler returns a json object with the expression and result. This json object is used by the pages code to determine if the user correctly answered the tutorial page. The management of history was moved to the middleware function 'wrap-post-history. This way your handler can add anything it wants to the history and this middleware will ensure that only state-modifying forms remain. Current history max is 5 items. --- resources/public/css/tryclojure.css | 6 ++ resources/public/javascript/tryclojure.js | 122 +++++++++++++++++++--- src/tryclojure/core.clj | 84 ++++++++++----- 3 files changed, 170 insertions(+), 42 deletions(-) diff --git a/resources/public/css/tryclojure.css b/resources/public/css/tryclojure.css index 82fbbff..8c4d14e 100644 --- a/resources/public/css/tryclojure.css +++ b/resources/public/css/tryclojure.css @@ -122,6 +122,12 @@ table.bottom { border: 1px solid #BEDCE7; } +#changer h3 { + color: #555; + margin-left: 10px; + margin-right: 10px; +} + #changer p { margin-left: 10px; margin-right: 10px; diff --git a/resources/public/javascript/tryclojure.js b/resources/public/javascript/tryclojure.js index 624f046..12c1b58 100644 --- a/resources/public/javascript/tryclojure.js +++ b/resources/public/javascript/tryclojure.js @@ -1,3 +1,42 @@ +var pageNum = -1; +var page = null; +var pages = [ + { + text: "

Welcome to the first page of the interactive tutorial!

\n" + + "

In order to move on to the next page please type (+ 1 2).

", + verify: function(data) { + // strictly validate by expression and result + // otherwise the user could just type '3' to move on. + return (data.expr == "(+ 1 2)" && data.result == "3"); + } + }, + { + text: "

This is page two!

\n" + + "

Wow you did it! Looks like you have a talent for this!

\n" + + "

I'm going to have to try something harder for you. In order to make it to " + + "the next page type: (map inc [1 2 3 4]).

\n", + verify: function(data) { + // being lazy and just verifying on result + return (data.result == "(2 3 4 5)"); + } + }, + { + text: "

Third and final page!

\n" + + "

Your intelligence is too great for this lame tutorial. It's time for you to " + + "contribute to some open source Clojure projects. Have fun.

", + verify: function(data) { return false; } + } +]; + +function showPage(n) { + var res = pages[n]; + if (res) { + $("#changer").html(res.text); + pageNum = n; + page = res; + } +} + function setupLink(url) { return function(e) { $("#changer").load(url, function(data) { $("#changer").html(data); }); } } @@ -12,25 +51,78 @@ function getStep(n, controller) { $("#tuttext").load("tutorial", { step: n }, function() { setupExamples(controller); }); } +function eval_clojure(code) { + var data; + $.ajax({ + url: "eval.json", + data: { expr : code }, + async: false, + success: function(res) { data = res; } + }); + return data; +} + +function doCommand(input, report) { + switch (input) { + case 'tutorial': + showPage(0); + report(); + return true; + case 'back': + if (pageNum > 0) { + showPage(pageNum - 1); + report(); + return true; + } else { + return false; + } + case 'restart': + if (pageNum > 0) { + showPage(0); + report(); + return true; + } else { + return false; + } + default: + return false; + } +} + +function onValidate(input) { + return (input != ""); +} + +function onHandle(line, report) { + var input = line.trim(); + + // handle commands + if (doCommand(input, report)) return; + + // perform evaluation + var data = eval_clojure(input); + + // handle error + if (data.error) { + return [{msg: data.message, className: "jquery-console-message-error"}]; + } + + // handle page + if (page && page.verify(data)) { + showPage(pageNum + 1); + } + + // display expr results + return [{msg: data.result, className: "jquery-console-message-value"}]; +} + $(document).ready( function() { var controller = $("#console").console({ - promptLabel: 'Clojure> ', - commandValidate:function(line){ - if (line == "") return false; - else return true; - }, - commandHandle:function(line){ - var data; - jQuery.ajax({ - url: "magics" + "?code=" + encodeURIComponent(line), - success: function(result) {data = result;}, - async: false - }); - return [{msg: data, - className:"jquery-console-message-value"}]; - }, welcomeMessage:'Enter some Clojure code, and it will be evaluated.', + promptLabel: 'Clojure> ', + commandValidate: onValidate, + commandHandle: onHandle, autofocus:true, animateScroll:true, promptHistory:true diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 8b0cfc7..59fc7e7 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -6,7 +6,8 @@ [clojure.stacktrace :only [root-cause]] [clojail core testers] tryclojure.tutorial - [clojure.set :only [difference]]) + [clojure.set :only [difference]]) + (:require [clojure.contrib.json :as json]) (:import java.io.StringWriter java.util.concurrent.TimeoutException)) @@ -17,24 +18,28 @@ (defn has-state? [form] (check-form form state-tester)) -(defn execute-text [txt history] - (let [sb (sandbox sb-tester :timeout 3000) - history (if (= 5 (count history)) (drop 1 history) history) - result (try - (loop [history history] - (when (not (empty? history)) - (do - (sb (first history)) - (recur (next history))))) - (let [form (binding [*read-eval* false] (read-string txt))] - (with-open [writer (java.io.StringWriter.)] - (let [r (pr-str (sb form {#'*out* writer}))] - [(str (.replace (escape-html writer) "\n" "
") (code (str r))) - (if (has-state? form) (conj history form) history)]))) - (catch OutOfMemoryError _ ["Out of memory error was thrown. Cleaning up all defs." nil]) - (catch TimeoutException _ ["Execution Timed Out!" history]) - (catch Exception e [(str (root-cause e)) history]))] - result)) +(defn eval-form [form sbox] + (with-open [out (java.io.StringWriter.)] + {:expr form + :result (sbox form {#'*out* out})})) + +(defn eval-string [expr sbox] + (let [form (binding [*read-eval* false] (read-string expr))] + (eval-form form sbox))) + +(defn eval-request [{params :params {history :history} :session}] + (let [sbox (sandbox sb-tester :timeout 3000)] + (try + ;; re-eval history forms + (doseq [form history] (eval-form form sbox)) + ;; eval request parameter + (eval-string (params "expr") sbox) + (catch OutOfMemoryError _ + {:error true :message "Out of memory error was thrown. Cleaning up all defs."}) + (catch TimeoutException _ + {:error true :message "Execution Timed Out!"}) + (catch Exception e + {:error true :message (.getMessage (root-cause e))})))) (def links (html (unordered-list @@ -115,13 +120,6 @@ :session session :body fire-html}) -(defn div-handler [{qparams :query-params session :session}] - (let [[result history] (execute-text (qparams "code") (or (:history session) []))] - {:status 200 - :headers {"Content-Type" "text/txt"} - :session {:history history} - :body result})) - (defn about-handler [{session :session}] {:status 200 :headers {"Content-Type" "text/html"} @@ -140,9 +138,41 @@ :session session :body (get-tutorial (formps "step"))}) +(def ^{:private true} + eval-response-defaults + {:status 200 + :headers {"Content-Type" "application/json"}}) + +(defn eval-handler [request] + (let [{:keys [expr result error] :as res} (eval-request request) + history (get-in request [:session :history] [])] + (if error + (merge eval-response-defaults + {:session (:session request) + :body (json/json-str res)}) + (merge eval-response-defaults + {:session {:history (conj history expr)} + :body (json/json-str {:expr (pr-str expr) + :result (pr-str result)})})))) + +(defn- max-history [max history] + (if (> (count history) max) + (drop 1 history) + history)) + +(defn wrap-post-history [handler] + (fn [request] + (let [response (handler request)] + (->> (get-in response [:session :history] []) + (max-history 5) + (filter has-state?) + (vec) + (assoc-in response [:session :history]))))) + (def clojureroutes (app (wrap-session) + (wrap-post-history) ;(wrap-reload '(tryclojure.core tryclojure.tutorial)) (wrap-file (System/getProperty "user.dir")) (wrap-params) @@ -150,7 +180,7 @@ ["tutorial"] tutorial-handler ["links"] link-handler ["about"] about-handler - ["magics"] div-handler + ["eval.json"] eval-handler [""] handler)) (defn tryclj [] (run-jetty #'clojureroutes {:port 8801})) From c21a97366f1dee5686503e4d9ee687aa623d7972 Mon Sep 17 00:00:00 2001 From: mefesto Date: Mon, 24 Jan 2011 00:10:40 -0500 Subject: [PATCH 017/127] Added html escaping on the javascript side. --- resources/public/javascript/tryclojure.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/resources/public/javascript/tryclojure.js b/resources/public/javascript/tryclojure.js index 12c1b58..419d4aa 100644 --- a/resources/public/javascript/tryclojure.js +++ b/resources/public/javascript/tryclojure.js @@ -62,6 +62,14 @@ function eval_clojure(code) { return data; } +function html_escape(val) { + var result = val; + result = result.replace(/\n/g, "
"); + result = result.replace(/[<]/g, "<"); + result = result.replace(/[>]/g, ">"); + return result; +} + function doCommand(input, report) { switch (input) { case 'tutorial': @@ -104,7 +112,7 @@ function onHandle(line, report) { // handle error if (data.error) { - return [{msg: data.message, className: "jquery-console-message-error"}]; + return [{msg: html_escape(data.message), className: "jquery-console-message-error"}]; } // handle page @@ -113,7 +121,7 @@ function onHandle(line, report) { } // display expr results - return [{msg: data.result, className: "jquery-console-message-value"}]; + return [{msg: html_escape(data.result), className: "jquery-console-message-value"}]; } $(document).ready( From dde66a776e9a6e54e92908f7fd3be2045357d408 Mon Sep 17 00:00:00 2001 From: mefesto Date: Mon, 24 Jan 2011 00:32:26 -0500 Subject: [PATCH 018/127] Added instructions for accessing the tutorial on the homepage. Added some css for code tags in div#changer > p. --- resources/public/css/tryclojure.css | 7 +++++++ src/tryclojure/core.clj | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/resources/public/css/tryclojure.css b/resources/public/css/tryclojure.css index 8c4d14e..42b6ee7 100644 --- a/resources/public/css/tryclojure.css +++ b/resources/public/css/tryclojure.css @@ -138,6 +138,13 @@ table.bottom { color: #555; } +#changer p code { + background-color: #eee; + border: 1px solid #aaa; + font-family: courier, monospace; + padding: 0.1em 0.25em 0.1em 0.25em; +} + div#tuttext { } diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 59fc7e7..0cf5c53 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -99,7 +99,7 @@ [:p.bottom "Welcome to Try Clojure. Above, you have a Clojure REPL. You can type expressions and see " "their results right here in your browser. We also have a brief tutorial to give you a " - "taste of Clojure. Try it out!"]]] + "taste of Clojure. Try it out by typing tutorial in the console!"]]] [:div.footer [:p.bottom "©2010 Anthony Simpson (Raynes) and contributors"] [:p.bottom "Domain and hosting kindly provided by " From 1917d55025b740458a4768c2c57b66ecb5df5c13 Mon Sep 17 00:00:00 2001 From: mefesto Date: Mon, 24 Jan 2011 01:05:58 -0500 Subject: [PATCH 019/127] Updated the tutorial button to start the tutorial. --- resources/public/javascript/tryclojure.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/resources/public/javascript/tryclojure.js b/resources/public/javascript/tryclojure.js index 419d4aa..c5a4d02 100644 --- a/resources/public/javascript/tryclojure.js +++ b/resources/public/javascript/tryclojure.js @@ -138,6 +138,10 @@ $(document).ready( $("#about").click(setupLink("about")); $("#links").click(setupLink("links")); + $("#tutorial").click(function(e) { + showPage(0); + }); + /* $("#tutorial").click(function(e) { $("#changer").load("tutorial", {step: 0}, function(data) { var step = 1; @@ -153,4 +157,5 @@ $(document).ready( }); }); }); + */ }); \ No newline at end of file From 2f1a02f3e544d1ec32e22321c42e26718bd0b228 Mon Sep 17 00:00:00 2001 From: mefesto Date: Mon, 24 Jan 2011 21:49:17 -0500 Subject: [PATCH 020/127] Make code blocks in the tutorial clickable. --- resources/public/javascript/tryclojure.js | 86 ++++++++++++----------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/resources/public/javascript/tryclojure.js b/resources/public/javascript/tryclojure.js index c5a4d02..3643ff3 100644 --- a/resources/public/javascript/tryclojure.js +++ b/resources/public/javascript/tryclojure.js @@ -3,7 +3,7 @@ var page = null; var pages = [ { text: "

Welcome to the first page of the interactive tutorial!

\n" + - "

In order to move on to the next page please type (+ 1 2).

", + "

In order to move on to the next page please type (+ 1 2).

", verify: function(data) { // strictly validate by expression and result // otherwise the user could just type '3' to move on. @@ -12,9 +12,9 @@ var pages = [ }, { text: "

This is page two!

\n" + - "

Wow you did it! Looks like you have a talent for this!

\n" + - "

I'm going to have to try something harder for you. In order to make it to " + - "the next page type: (map inc [1 2 3 4]).

\n", + "

Wow you did it! Looks like you have a talent for this!

\n" + + "

I'm going to have to try something harder for you. In order to make it to " + + "the next page type: (map inc [1 2 3 4]).

\n", verify: function(data) { // being lazy and just verifying on result return (data.result == "(2 3 4 5)"); @@ -22,8 +22,8 @@ var pages = [ }, { text: "

Third and final page!

\n" + - "

Your intelligence is too great for this lame tutorial. It's time for you to " + - "contribute to some open source Clojure projects. Have fun.

", + "

Your intelligence is too great for this lame tutorial. It's time for you to " + + "contribute to some open source Clojure projects. Have fun.

", verify: function(data) { return false; } } ]; @@ -31,9 +31,10 @@ var pages = [ function showPage(n) { var res = pages[n]; if (res) { - $("#changer").html(res.text); pageNum = n; page = res; + $("#changer").html(res.text); + changerUpdated(); } } @@ -43,7 +44,7 @@ function setupLink(url) { function setupExamples(controller) { $(".code").click(function(e) { - controller.promptText($(this).text()); + controller.promptText($(this).text()); }); } @@ -124,38 +125,39 @@ function onHandle(line, report) { return [{msg: html_escape(data.result), className: "jquery-console-message-value"}]; } -$(document).ready( - function() { - var controller = $("#console").console({ - welcomeMessage:'Enter some Clojure code, and it will be evaluated.', - promptLabel: 'Clojure> ', - commandValidate: onValidate, - commandHandle: onHandle, - autofocus:true, - animateScroll:true, - promptHistory:true - }); - - $("#about").click(setupLink("about")); - $("#links").click(setupLink("links")); - $("#tutorial").click(function(e) { - showPage(0); +/** + * This should be called anytime the changer div is updated so it can rebind event listeners. + * Currently this is just to make the code elements clickable. + */ +function changerUpdated() { + $("#changer code").each(function() { + $(this).css("cursor", "pointer"); + $(this).attr("title", "Click to insert '" + $(this).text() + "' into the console."); + $(this).click(function(e) { + controller.promptText($(this).text()); + controller.inner.click(); }); - /* - $("#tutorial").click(function(e) { - $("#changer").load("tutorial", {step: 0}, function(data) { - var step = 1; - $("#continue").click(function(e) { - if(step < 6 ) { step += 1; } - getStep(step, controller); - $("#tuttext").scrollTop(0); - }); - $("#back").click(function(e) { - if(step > 1) { step -= 1; } - getStep(step, controller); - $("#tuttext").scrollTop(0); - }); - }); - }); - */ - }); \ No newline at end of file + }); +} + +var controller; + +$(document).ready(function() { + controller = $("#console").console({ + welcomeMessage:'Enter some Clojure code, and it will be evaluated.', + promptLabel: 'Clojure> ', + commandValidate: onValidate, + commandHandle: onHandle, + autofocus:true, + animateScroll:true, + promptHistory:true + }); + + $("#about").click(setupLink("about")); + $("#links").click(setupLink("links")); + $("#tutorial").click(function(e) { + showPage(0); + }); + + changerUpdated(); +}); From 21ad5ef09b9f207963575887c56b6f7a3e5ef57d Mon Sep 17 00:00:00 2001 From: mefesto Date: Mon, 24 Jan 2011 21:59:45 -0500 Subject: [PATCH 021/127] Added a fade out/in transition effect. --- resources/public/javascript/tryclojure.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/resources/public/javascript/tryclojure.js b/resources/public/javascript/tryclojure.js index 3643ff3..ab6886f 100644 --- a/resources/public/javascript/tryclojure.js +++ b/resources/public/javascript/tryclojure.js @@ -33,8 +33,13 @@ function showPage(n) { if (res) { pageNum = n; page = res; - $("#changer").html(res.text); - changerUpdated(); + + var block = $("#changer"); + block.fadeOut(function(e) { + block.html(res.text); + block.fadeIn(); + changerUpdated(); + }); } } From 0490e8a78ad8101d7ff981a1e3cb9a28ff4ad187 Mon Sep 17 00:00:00 2001 From: mefesto Date: Tue, 25 Jan 2011 00:55:12 -0500 Subject: [PATCH 022/127] Ignore temp files: *~ --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d9148e9..5fcc507 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ pom.xml *jar lib -classes \ No newline at end of file +classes +*~ From a743a4c69133542198088e19a24a757ebcacec91 Mon Sep 17 00:00:00 2001 From: mefesto Date: Tue, 25 Jan 2011 00:55:57 -0500 Subject: [PATCH 023/127] Updated tutorial with content from tutorial.clj Minor css tweaks for #changer content. --- resources/public/css/tryclojure.css | 9 +-- resources/public/javascript/tryclojure.js | 48 ++++++------ resources/public/tutorial/page1.html | 25 ++++++ resources/public/tutorial/page2.html | 56 ++++++++++++++ resources/public/tutorial/page3.html | 35 +++++++++ resources/public/tutorial/page4.html | 54 +++++++++++++ resources/public/tutorial/page5.html | 94 +++++++++++++++++++++++ 7 files changed, 293 insertions(+), 28 deletions(-) create mode 100644 resources/public/tutorial/page1.html create mode 100644 resources/public/tutorial/page2.html create mode 100644 resources/public/tutorial/page3.html create mode 100644 resources/public/tutorial/page4.html create mode 100644 resources/public/tutorial/page5.html diff --git a/resources/public/css/tryclojure.css b/resources/public/css/tryclojure.css index 42b6ee7..58801f8 100644 --- a/resources/public/css/tryclojure.css +++ b/resources/public/css/tryclojure.css @@ -115,7 +115,7 @@ table.bottom { #changer { margin: 10px; - padding: 1px 0; + padding: 0.25em 0.5em 0.25em 0.5em; background: #EAF2F5; border-radius: 5px; -moz-border-radius: 5px; @@ -124,13 +124,9 @@ table.bottom { #changer h3 { color: #555; - margin-left: 10px; - margin-right: 10px; } #changer p { - margin-left: 10px; - margin-right: 10px; background: transparent; line-height: 1.3em; font-family: Arial, serif; @@ -138,9 +134,10 @@ table.bottom { color: #555; } -#changer p code { +#changer code { background-color: #eee; border: 1px solid #aaa; + color: #555; font-family: courier, monospace; padding: 0.1em 0.25em 0.1em 0.25em; } diff --git a/resources/public/javascript/tryclojure.js b/resources/public/javascript/tryclojure.js index ab6886f..ef62847 100644 --- a/resources/public/javascript/tryclojure.js +++ b/resources/public/javascript/tryclojure.js @@ -2,28 +2,23 @@ var pageNum = -1; var page = null; var pages = [ { - text: "

Welcome to the first page of the interactive tutorial!

\n" + - "

In order to move on to the next page please type (+ 1 2).

", - verify: function(data) { - // strictly validate by expression and result - // otherwise the user could just type '3' to move on. - return (data.expr == "(+ 1 2)" && data.result == "3"); - } + url: "/resources/public/tutorial/page1.html", + verify: function(data) { return false; } }, { - text: "

This is page two!

\n" + - "

Wow you did it! Looks like you have a talent for this!

\n" + - "

I'm going to have to try something harder for you. In order to make it to " + - "the next page type: (map inc [1 2 3 4]).

\n", - verify: function(data) { - // being lazy and just verifying on result - return (data.result == "(2 3 4 5)"); - } + url: "/resources/public/tutorial/page2.html", + verify: function(data) { return false; } }, { - text: "

Third and final page!

\n" + - "

Your intelligence is too great for this lame tutorial. It's time for you to " + - "contribute to some open source Clojure projects. Have fun.

", + url: "/resources/public/tutorial/page3.html", + verify: function(data) { return false; } + }, + { + url: "/resources/public/tutorial/page4.html", + verify: function(data) { return false; } + }, + { + url: "/resources/public/tutorial/page5.html", verify: function(data) { return false; } } ]; @@ -36,9 +31,10 @@ function showPage(n) { var block = $("#changer"); block.fadeOut(function(e) { - block.html(res.text); - block.fadeIn(); - changerUpdated(); + block.load(res.url, function() { + block.fadeIn(); + changerUpdated(); + }); }); } } @@ -90,6 +86,14 @@ function doCommand(input, report) { } else { return false; } + case 'next': + if (pageNum >= 0 && pageNum < pages.length - 1) { + showPage(pageNum + 1); + report(); + return true; + } else { + return false; + } case 'restart': if (pageNum > 0) { showPage(0); @@ -135,7 +139,7 @@ function onHandle(line, report) { * Currently this is just to make the code elements clickable. */ function changerUpdated() { - $("#changer code").each(function() { + $("#changer code.expr").each(function() { $(this).css("cursor", "pointer"); $(this).attr("title", "Click to insert '" + $(this).text() + "' into the console."); $(this).click(function(e) { diff --git a/resources/public/tutorial/page1.html b/resources/public/tutorial/page1.html new file mode 100644 index 0000000..5c0fffc --- /dev/null +++ b/resources/public/tutorial/page1.html @@ -0,0 +1,25 @@ +

+ This tutorial is intended for people who have never used Clojure before but have experience + with other programming languages. This is not meant to be a comprehensive tutorial, but instead + intended to get you started with Clojure -- and hopefully interested enough to continue. + Please bear in mind that this tutorial is evolving, so check back frequently for new additions. +

+ Above, you have your REPL. Please try the examples as we go along, and by all means experiment + with concepts independently. Should you refuse, you will promptly be IP banned from this + website, and a teddy bear will eat your candies. ;-) +

+ Don't make teddy eat your candy. +

+ +

+ All of the examples (pretty colored text) are clickable. If you click them, they will be copied + into the REPL prompt above. This is for convenience, so you don't have to type tedious things + like a backwards string (you'll see). Copy and pasting is not allowed in the REPL, because I and + the creator of JQuery-console, which powers the REPL above, believe that copying and pasting isn't + so great for learning. While these examples are clickable, I urge you to type out the examples by + hand. If you type them out, they will sink in better. Enjoy the tutorial. +

+ +

+ Hit the pretty little 'Next' button to continue for the love of Pete! +

diff --git a/resources/public/tutorial/page2.html b/resources/public/tutorial/page2.html new file mode 100644 index 0000000..7928b0c --- /dev/null +++ b/resources/public/tutorial/page2.html @@ -0,0 +1,56 @@ +

+ Let's jump right in there and write some Clojure. Let's start out with the familiar + 'Hello, World!'. People seem to love it so much. Type the following into the REPL: +

+ +(println "!dlrow ,olleH") + +

+ Something isn't right here! (I'm not talking about the fact that this website's lack of panache). + It seems that 'Hello World' has been mangled! It's okay though, because we're smart and we can + figure out how to remedy this situation. If you're wondering why it printed our gibberish AND + 'nil' it's because 'nil' (which essentially means 'nothing') is println's return value. Let's + experiment until we figure out what went wrong. It seems that the reverse + should fix things -- so type the following into the REPL: +

+ +(reverse "!dlrow ,olleH") + +

+ Hmm. It did indeed set it right, but this isn't what we were looking for! That doesn't even look + like a string. Reverse took our string, reversed it, and now we have a sequence of characters. + You can tell this because each character has a '\\' (backslash in layman's terms) preceeding it. + Clojure uses those to denote Character (big 'C' character) literals. We need a string though! + What do we do when we need a string, but we have something else that isn't stringy (a highly + technical term)? No, we don't go to the shop and buy string cheese (not to disparage string + cheese mind you), but instead we use the lovely str function! Try this: +

+ +(str (reverse "!dlrow ,olleH")) + +

+ Ooh! So close. It appears that the str function is making the entire sequence a string! + Sequences can be made to look like strings. str works on any number of arguments. You can pass + str any number of arguments and it will call Java's toString method on each of them to change + them into a string, and then it will concatenate them all into one mammoth string. Unfortunately, + all of our characters are stuck in this sequence! How can we get them out of the sequence, and + pass them all to str? Do not fear, young Clojurian, for I have the answer. We use the + apply function. apply takes a function and a sequence, and 'unrolls' + the sequence, passing all of its elements to the function as arguments. Basically, it takes the + sequence and dumps its elements into the argument list of the supplied function; which is exactly + what we need. Let's see if it works... Try this: +

+ +(apply str (reverse "!dlrow ,olleH")) + +

+ Ka-chow! There we go. Impressive. I'm proud of you, I really am. Now, for the final test. Let's + tell the world just how much they mean to us by making the world rotate in the other direction! + Let's do this: +

+ +(println (apply str (reverse "!dlrow ,olleH"))) + +

+ Congratulations, you have passed step one. Press Next to continue. +

diff --git a/resources/public/tutorial/page3.html b/resources/public/tutorial/page3.html new file mode 100644 index 0000000..ef739eb --- /dev/null +++ b/resources/public/tutorial/page3.html @@ -0,0 +1,35 @@ +

+ Alright. We have greeted the world. Now what? Math? Clojure is great at math. Thanks + to the uniformity of prefix notation, we don't have to worry about precedence rules; + which we kinda loathe anyway. This goes before that, and that goes before that other + thing... blah! In Clojure, mathematical operators work like any other function. The + common operators are +, -, *, and /. Let's try them out: +

+ +

(+ 2 2)

+

(- 3 2)

+

(* 5 5)

+

(/ 4 3)

+ +

+ So, that was great. But that last one doesn't quite look right. The problem is that Clojure + has a built-in Ratio type. You can confirm this by doing this: (class (/ 4 3)). +

+ +

+ So how do we perform division that we are familiar with? If you want decimal division, use + a floating-point literal for either the numerator or denominator: + (/ 4.0 3). If you want to stick to integer math, you can use the + quot function, or the rem function to get the remainder. +

+ +

+ All of the math functions take an arbitrary number of arguments. This means you can do stuff + like this: (+ 1 2 3 4 5 6). Go ahead, try it out. You know you want to. Play + around with the math functions for a while. I'll wait. Go ahead. +

+ +

+ That's the end of step two. Press Next to continue to step three, where we'll play with + sequences. +

diff --git a/resources/public/tutorial/page4.html b/resources/public/tutorial/page4.html new file mode 100644 index 0000000..0aecf04 --- /dev/null +++ b/resources/public/tutorial/page4.html @@ -0,0 +1,54 @@ +

+ Now that we've covered the basics of prefix math, let's take some time to play with sequences. +

+ +

+ In Clojure, the term 'sequence' is an abstraction that many data structures implement. This + abstraction allows any function that works on a 'seq' (pronounced seek) to work on any data + structure that is seqable. This means all Clojure and Java collections, strings, and even + I/O streams. +

+ +

+ In isolation this doesn't make sense, so let's play with vectors. A literal vector looks like + this: [1 2 3 "four" 5 6.0]. Vectors are the most commonly used collection in + Clojure, along with maps. Let's see if we can square all of the elements of a vector: + (map (fn [x] (* x x)) [1 2 3 4 5]). Try that out. The map function + takes a function that takes a single argument and returns a value, which it 'applies' to each + element of its second argument, a sequence. The resulting sequence is then returned. +

+ +

+ We have introduced something new here -- the anonymous function. Anonymous functions are used + quite heavily in Clojure code. They are created using fn. After fn, you supply + a vector of arguments that the function takes, and then the body of the function that uses those + arguments to compute a value. Clojure has short hand for anonymous functions as well. The above + anonymous function can be rewritten using a shorthand form: #(* % %). When you + use this shorthand, the function arguments are accessed like so: % or %1 for the first + argument, %2 for the second argument, %3 for the third and so on. Our code looks like this: + (map #(* % %) [1 2 3 4 5]) +

+ +

+ Another highly important collection is the hashmap. A literal hashmap looks like this: + {:key "value" :key2 3 :key4 [3 4 2 1]}. The keys in this map are something + called 'keywords'. You can tell that they are keywords because they start with a colon ':' character. + They're like symbols that resolve to themselves. In idiomatic Clojure code, keywords are often + used as map keys. You can use many objects as map keys, including integers, vectors, strings, + and other maps. +

+ +

+ To get a value at a key in a map, we can do this: ({:key1 :val1} :key1). I bet + you're all lolwut right now, aren't you? In Clojure, hashmaps are functions that take a single + argument, which should be a key and they lookup the key within themselves and return the value + at the key or nil if the key doesn't exist. Another neat trick for maps with keywords for keys + is this: (:key1 {:key1 :val1}). Keywords are also functions. They just look + themselves up in the supplied map. +

+ +

+ Clojure has all sorts of other data structures as well. Sets, lists, queues, zippers, etc. + We'll talk about some of those later on. For now, let's move on to step four. Press Next to + continue. +

diff --git a/resources/public/tutorial/page5.html b/resources/public/tutorial/page5.html new file mode 100644 index 0000000..ee73be1 --- /dev/null +++ b/resources/public/tutorial/page5.html @@ -0,0 +1,94 @@ +

+ We've learned a little about Clojure's sequences, so let's use them to do some stuff. I've been + wondering how vowels are in the word "teddybear". Aren't you wondering the same thing? It's + absolutely agonizing not knowing! We're programmers, you and I, so we shouldn't have to count + those vowels ourselves. Indeed, we don't. We can use Clojure to count them for us! +

+ +

+ Okay, we'll start with the string "teddybear". We're going to be using this string a lot, and we + don't want to have to keep typing it over and over again, do we? Luckily, Clojure can help us here. + Type this into the REPL: (def teddy "teddybear"). What def does is pretty + simple: it simply gives a name to a value so that we can refer to it by that name later. Dont' worry + about what the REPL printed when you typed that, it's just trying to show you exactly where the var is + mapped (in this case, a sandbox generated namespace). We need to make sure it worked. Type this + into the REPL: teddy. Cool huh? +

+ +

+ Now that we are armed with a loaded teddybear, we can start figuring out how to find out the number + of vowels. For this, we need to introduce a new collection type: sets. A literal set looks like this: + #{3 4 5 "x" \y}. A set can hold anything, but it can't hold any two of the same + thing. In a set, there can be no duplicate objects. Another important fact about sets is that they, like + maps, are also functions. A set is a function that takes an argument and looks inside itself to see + if that same object is inside of it. If this is true, it returns the object, or returns nil. Let's + try this out for ourselves: +

+ +

(#{1 2 3} 3)

+

(#{"abc" \e} \e)

+

(#{3 4 \x} 5)

+ +

+ Okay, so how is this useful? It really isn't. Not alone, anyway. However, when it's combined with + other sequence functions, it can be used to make a really elegant solution to a problem like this. +

+ +

+ Okay, so we have a way to test if a character is a vowel. We can simply do this: + (#{\a \e \i \o \u} \e). Since we're going to be using the set of vowels a lot, + go ahead and give it a name in the REPL: (def vowels #{\a \e \i \o \u}). Now we + can use vowels to refer to the set of vowels. +

+ +

+ Now, we have a way to find out if a character is a vowel, now we just need a way to remove + everything that isn't a vowel from our teddybear string. I have just the function! We need + filter. Filter takes what is called a 'predicate', that is, a function that returns + true or false, and it applies this function to each element of a sequence in turn. If the predicate + function returns false or nil for an element, that element is removed from the sequence. If the + predicate function returns anything that isn't false or nil for an element, that element is left + alone. In Clojure, anything that isn't false or nil is considered a true value. +

+ +

+ Let's try filter out a bit. Let's try to filter out all odd numbers from a sequence of numbers. + Clojure has a function called odd? that we can use. Putting a question mark at the end + is a Clojure naming convention for functions that are predicates (return either true or false). Try it + out in the REPL: +

+ +

(odd? 1)

+

(odd? 2)

+ +

+ Okay, now we need a sequence of numbers. We could type these out by hand, but that's tedious, and as + Clojure programmers, we do not tolerate 'tedious'. We can use Clojure's range function to generate + these numbers for us. range, if given one integer, will generate a sequence of numbers + from 0 to the integer that you passed to it. Try this: (range 10). We have a + sequence of numbers from 0 to 9. Apparently, the upper-bound (the number you passed to range) is + 'exclusive', meaning it's not included in the resulting range. If we really wanted a sequence of + numbers from 0 to 10, we'd do this: (range 11). +

+ +

+ We don't really want the zero, so let's give range a lower-bound, along with an upper-bound. It can + take two arguments: (range 1 11). There we go! We have all of the numbers from + 1 to 10. Now, let's use filter and the odd? function to get all of the odd numbers out of the sequence. + Try this: (filter odd? (range 1 11)). Ka-chow! That's magic, isn't it? Work of + art, that is. +

+ +

+ Now that we know how to use filter, we can now use it to filter out all of the vowels in our teddy + string. We have our string defined in teddy, and our set of vowels in vowels, so we should try this: + (filter vowels teddy). That was easy enough. Now we have a sequence of + characters. But we still need to count them! Luckily, Clojure has just the function for that: + count! It takes a sequence and counts (whoda thunk it) the number of elements in the + sequence. Let's try it out: (count (filter vowels teddy)). Yeehaw! You did it! + Once again, you never cease to amaze me. You're catching on quickly. +

+ +

+ That's the end of step 4. Press Next to move on. +

From 287fdacab864dd11416d5448036793530a5b9dc1 Mon Sep 17 00:00:00 2001 From: Raynes Date: Wed, 13 Apr 2011 23:08:07 -0500 Subject: [PATCH 024/127] Remove the "click" nonsense. --- resources/public/tutorial/page1.html | 16 ++++++++++++- resources/public/tutorial/page2.html | 12 +++++----- resources/public/tutorial/page3.html | 16 ++++++------- resources/public/tutorial/page4.html | 14 +++++------ resources/public/tutorial/page5.html | 36 ++++++++++++++-------------- 5 files changed, 54 insertions(+), 40 deletions(-) diff --git a/resources/public/tutorial/page1.html b/resources/public/tutorial/page1.html index 5c0fffc..d01d961 100644 --- a/resources/public/tutorial/page1.html +++ b/resources/public/tutorial/page1.html @@ -1,3 +1,13 @@ +

+ I am in the process of rewriting this tutorial and, of course, adding content. This tutorial + was thrown together rather quickly and hasn't been touched in quite some time. I ask for + patience while I get everything working smoothly. I have a lot going on in my life including + a book and a new job. Furthermore, I don't even really know Javascript, so anything JavaScript + related moves extremely slowly. Working on that. ;) +

+ +
+

This tutorial is intended for people who have never used Clojure before but have experience with other programming languages. This is not meant to be a comprehensive tutorial, but instead @@ -21,5 +31,9 @@

- Hit the pretty little 'Next' button to continue for the love of Pete! + You can type 'next' to move forward and 'back' to go back to the previous step. +

+ +

+ Type 'next' in the REPL to continue!

diff --git a/resources/public/tutorial/page2.html b/resources/public/tutorial/page2.html index 7928b0c..7c3a7dc 100644 --- a/resources/public/tutorial/page2.html +++ b/resources/public/tutorial/page2.html @@ -3,7 +3,7 @@ 'Hello, World!'. People seem to love it so much. Type the following into the REPL:

-(println "!dlrow ,olleH") +(println "!dlrow ,olleH")

Something isn't right here! (I'm not talking about the fact that this website's lack of panache). @@ -14,7 +14,7 @@ should fix things -- so type the following into the REPL:

-(reverse "!dlrow ,olleH") +(reverse "!dlrow ,olleH")

Hmm. It did indeed set it right, but this isn't what we were looking for! That doesn't even look @@ -26,7 +26,7 @@ cheese mind you), but instead we use the lovely str function! Try this:

-(str (reverse "!dlrow ,olleH")) +(str (reverse "!dlrow ,olleH"))

Ooh! So close. It appears that the str function is making the entire sequence a string! @@ -41,7 +41,7 @@ what we need. Let's see if it works... Try this:

-(apply str (reverse "!dlrow ,olleH")) +(apply str (reverse "!dlrow ,olleH"))

Ka-chow! There we go. Impressive. I'm proud of you, I really am. Now, for the final test. Let's @@ -49,8 +49,8 @@ Let's do this:

-(println (apply str (reverse "!dlrow ,olleH"))) +(println (apply str (reverse "!dlrow ,olleH")))

- Congratulations, you have passed step one. Press Next to continue. + Congratulations, you have passed step one. Type next to continue.

diff --git a/resources/public/tutorial/page3.html b/resources/public/tutorial/page3.html index ef739eb..a6eea47 100644 --- a/resources/public/tutorial/page3.html +++ b/resources/public/tutorial/page3.html @@ -6,30 +6,30 @@ common operators are +, -, *, and /. Let's try them out:

-

(+ 2 2)

-

(- 3 2)

-

(* 5 5)

-

(/ 4 3)

+

(+ 2 2)

+

(- 3 2)

+

(* 5 5)

+

(/ 4 3)

So, that was great. But that last one doesn't quite look right. The problem is that Clojure - has a built-in Ratio type. You can confirm this by doing this: (class (/ 4 3)). + has a built-in Ratio type. You can confirm this by doing this: (class (/ 4 3)).

So how do we perform division that we are familiar with? If you want decimal division, use a floating-point literal for either the numerator or denominator: - (/ 4.0 3). If you want to stick to integer math, you can use the + (/ 4.0 3). If you want to stick to integer math, you can use the quot function, or the rem function to get the remainder.

All of the math functions take an arbitrary number of arguments. This means you can do stuff - like this: (+ 1 2 3 4 5 6). Go ahead, try it out. You know you want to. Play + like this: (+ 1 2 3 4 5 6). Go ahead, try it out. You know you want to. Play around with the math functions for a while. I'll wait. Go ahead.

- That's the end of step two. Press Next to continue to step three, where we'll play with + That's the end of step two. Type next to continue to step three, where we'll play with sequences.

diff --git a/resources/public/tutorial/page4.html b/resources/public/tutorial/page4.html index 0aecf04..509d075 100644 --- a/resources/public/tutorial/page4.html +++ b/resources/public/tutorial/page4.html @@ -11,9 +11,9 @@

In isolation this doesn't make sense, so let's play with vectors. A literal vector looks like - this: [1 2 3 "four" 5 6.0]. Vectors are the most commonly used collection in + this: [1 2 3 "four" 5 6.0]. Vectors are the most commonly used collection in Clojure, along with maps. Let's see if we can square all of the elements of a vector: - (map (fn [x] (* x x)) [1 2 3 4 5]). Try that out. The map function + (map (fn [x] (* x x)) [1 2 3 4 5]). Try that out. The map function takes a function that takes a single argument and returns a value, which it 'applies' to each element of its second argument, a sequence. The resulting sequence is then returned.

@@ -26,12 +26,12 @@ anonymous function can be rewritten using a shorthand form: #(* % %). When you use this shorthand, the function arguments are accessed like so: % or %1 for the first argument, %2 for the second argument, %3 for the third and so on. Our code looks like this: - (map #(* % %) [1 2 3 4 5]) + (map #(* % %) [1 2 3 4 5])

Another highly important collection is the hashmap. A literal hashmap looks like this: - {:key "value" :key2 3 :key4 [3 4 2 1]}. The keys in this map are something + {:key "value" :key2 3 :key4 [3 4 2 1]}. The keys in this map are something called 'keywords'. You can tell that they are keywords because they start with a colon ':' character. They're like symbols that resolve to themselves. In idiomatic Clojure code, keywords are often used as map keys. You can use many objects as map keys, including integers, vectors, strings, @@ -39,16 +39,16 @@

- To get a value at a key in a map, we can do this: ({:key1 :val1} :key1). I bet + To get a value at a key in a map, we can do this: ({:key1 :val1} :key1). I bet you're all lolwut right now, aren't you? In Clojure, hashmaps are functions that take a single argument, which should be a key and they lookup the key within themselves and return the value at the key or nil if the key doesn't exist. Another neat trick for maps with keywords for keys - is this: (:key1 {:key1 :val1}). Keywords are also functions. They just look + is this: (:key1 {:key1 :val1}). Keywords are also functions. They just look themselves up in the supplied map.

Clojure has all sorts of other data structures as well. Sets, lists, queues, zippers, etc. - We'll talk about some of those later on. For now, let's move on to step four. Press Next to + We'll talk about some of those later on. For now, let's move on to step four. Type next to continue.

diff --git a/resources/public/tutorial/page5.html b/resources/public/tutorial/page5.html index ee73be1..78fbce7 100644 --- a/resources/public/tutorial/page5.html +++ b/resources/public/tutorial/page5.html @@ -8,26 +8,26 @@

Okay, we'll start with the string "teddybear". We're going to be using this string a lot, and we don't want to have to keep typing it over and over again, do we? Luckily, Clojure can help us here. - Type this into the REPL: (def teddy "teddybear"). What def does is pretty + Type this into the REPL: (def teddy "teddybear"). What def does is pretty simple: it simply gives a name to a value so that we can refer to it by that name later. Dont' worry about what the REPL printed when you typed that, it's just trying to show you exactly where the var is mapped (in this case, a sandbox generated namespace). We need to make sure it worked. Type this - into the REPL: teddy. Cool huh? + into the REPL: teddy. Cool huh?

Now that we are armed with a loaded teddybear, we can start figuring out how to find out the number of vowels. For this, we need to introduce a new collection type: sets. A literal set looks like this: - #{3 4 5 "x" \y}. A set can hold anything, but it can't hold any two of the same + #{3 4 5 "x" \y}. A set can hold anything, but it can't hold any two of the same thing. In a set, there can be no duplicate objects. Another important fact about sets is that they, like maps, are also functions. A set is a function that takes an argument and looks inside itself to see if that same object is inside of it. If this is true, it returns the object, or returns nil. Let's try this out for ourselves:

-

(#{1 2 3} 3)

-

(#{"abc" \e} \e)

-

(#{3 4 \x} 5)

+

(#{1 2 3} 3)

+

(#{"abc" \e} \e)

+

(#{3 4 \x} 5)

Okay, so how is this useful? It really isn't. Not alone, anyway. However, when it's combined with @@ -36,9 +36,9 @@

Okay, so we have a way to test if a character is a vowel. We can simply do this: - (#{\a \e \i \o \u} \e). Since we're going to be using the set of vowels a lot, - go ahead and give it a name in the REPL: (def vowels #{\a \e \i \o \u}). Now we - can use vowels to refer to the set of vowels. + (#{\a \e \i \o \u} \e). Since we're going to be using the set of vowels a lot, + go ahead and give it a name in the REPL: (def vowels #{\a \e \i \o \u}). Now we + can use vowels to refer to the set of vowels.

@@ -58,37 +58,37 @@ out in the REPL:

-

(odd? 1)

-

(odd? 2)

+

(odd? 1)

+

(odd? 2)

Okay, now we need a sequence of numbers. We could type these out by hand, but that's tedious, and as Clojure programmers, we do not tolerate 'tedious'. We can use Clojure's range function to generate these numbers for us. range, if given one integer, will generate a sequence of numbers - from 0 to the integer that you passed to it. Try this: (range 10). We have a + from 0 to the integer that you passed to it. Try this: (range 10). We have a sequence of numbers from 0 to 9. Apparently, the upper-bound (the number you passed to range) is 'exclusive', meaning it's not included in the resulting range. If we really wanted a sequence of - numbers from 0 to 10, we'd do this: (range 11). + numbers from 0 to 10, we'd do this: (range 11).

We don't really want the zero, so let's give range a lower-bound, along with an upper-bound. It can - take two arguments: (range 1 11). There we go! We have all of the numbers from + take two arguments: (range 1 11). There we go! We have all of the numbers from 1 to 10. Now, let's use filter and the odd? function to get all of the odd numbers out of the sequence. - Try this: (filter odd? (range 1 11)). Ka-chow! That's magic, isn't it? Work of + Try this: (filter odd? (range 1 11)). Ka-chow! That's magic, isn't it? Work of art, that is.

Now that we know how to use filter, we can now use it to filter out all of the vowels in our teddy string. We have our string defined in teddy, and our set of vowels in vowels, so we should try this: - (filter vowels teddy). That was easy enough. Now we have a sequence of + (filter vowels teddy). That was easy enough. Now we have a sequence of characters. But we still need to count them! Luckily, Clojure has just the function for that: count! It takes a sequence and counts (whoda thunk it) the number of elements in the - sequence. Let's try it out: (count (filter vowels teddy)). Yeehaw! You did it! + sequence. Let's try it out: (count (filter vowels teddy)). Yeehaw! You did it! Once again, you never cease to amaze me. You're catching on quickly.

- That's the end of step 4. Press Next to move on. + That's the end of step 4. Type next to move on.

From 7f703fcc85bc6460718a0ee7a94b9fa4930115f4 Mon Sep 17 00:00:00 2001 From: Raynes Date: Wed, 13 Apr 2011 23:08:19 -0500 Subject: [PATCH 025/127] Get rid of pointless stuff. --- src/tryclojure/core.clj | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 0cf5c53..6069fb6 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -92,7 +92,6 @@ [:div#container [:div#console.console] [:div#buttons - [:a#tutorial.buttons "tutorial"] [:a#links.buttons "links"] [:a#about.buttons.last "about"]] [:div#changer @@ -101,18 +100,9 @@ "their results right here in your browser. We also have a brief tutorial to give you a " "taste of Clojure. Try it out by typing tutorial in the console!"]]] [:div.footer - [:p.bottom "©2010 Anthony Simpson (Raynes) and contributors"] - [:p.bottom "Domain and hosting kindly provided by " - (link-to "http://blog.licenser.net" "Heinz N. Gies") "."]]] - [:script {:type "text/javascript"} - "var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-552543-3']); _gaq.push(['_trackPageview']); - (function() { - var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; - ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; - var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); - })(); -"] -]])) + [:p.bottom "©2011 Anthony Grimes (Raynes) and contributors"] + [:p.bottom "Domain kindly paid for by " + (link-to "http://blog.licenser.net" "Heinz N. Gies")]]]]])) (defn handler [{session :session}] {:status 200 From 7006fc24d60bd4cfec3ce9cbaaa887396b1e4aaa Mon Sep 17 00:00:00 2001 From: Raynes Date: Wed, 13 Apr 2011 23:14:11 -0500 Subject: [PATCH 026/127] Too many newlines. --- resources/public/tutorial/page1.html | 2 -- 1 file changed, 2 deletions(-) diff --git a/resources/public/tutorial/page1.html b/resources/public/tutorial/page1.html index d01d961..cef275b 100644 --- a/resources/public/tutorial/page1.html +++ b/resources/public/tutorial/page1.html @@ -6,8 +6,6 @@ related moves extremely slowly. Working on that. ;)

-
-

This tutorial is intended for people who have never used Clojure before but have experience with other programming languages. This is not meant to be a comprehensive tutorial, but instead From e8c1581aba80ccaa19811f3f76f173c8a2a496e2 Mon Sep 17 00:00:00 2001 From: Raynes Date: Wed, 13 Apr 2011 23:14:25 -0500 Subject: [PATCH 027/127] Update deps. --- project.clj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/project.clj b/project.clj index 1dd384f..6675e6b 100644 --- a/project.clj +++ b/project.clj @@ -5,8 +5,7 @@ [net.cgrand/moustache "1.0.0-SNAPSHOT" :exclusions [org.clojure/clojure]] [ring "0.3.3"] [commons-lang/commons-lang "2.5"] - [clojail "0.3.4-SNAPSHOT"] - [org.clojars.rayne/clj-highlight "1.1.1-SNAPSHOT"] + [clojail "0.4.0-SNAPSHOT"] [hiccup "0.2.7"]] :dev-dependencies [[swank-clojure "1.2.1"] [ring/ring-devel "0.3.3"]]) \ No newline at end of file From 7bad33803a07711ee67ab0fd70ab1e319cfeeab7 Mon Sep 17 00:00:00 2001 From: Raynes Date: Wed, 13 Apr 2011 23:14:59 -0500 Subject: [PATCH 028/127] No moar tutorial.clj --- src/tryclojure/core.clj | 8 -- src/tryclojure/tutorial.clj | 214 ------------------------------------ 2 files changed, 222 deletions(-) delete mode 100644 src/tryclojure/tutorial.clj diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 6069fb6..9a7334e 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -5,7 +5,6 @@ net.cgrand.moustache [clojure.stacktrace :only [root-cause]] [clojail core testers] - tryclojure.tutorial [clojure.set :only [difference]]) (:require [clojure.contrib.json :as json]) (:import java.io.StringWriter @@ -122,12 +121,6 @@ :session session :body links}) -(defn tutorial-handler [{formps :form-params session :session :as req}] - {:status 200 - :headers {"Content-Type" "text/html"} - :session session - :body (get-tutorial (formps "step"))}) - (def ^{:private true} eval-response-defaults {:status 200 @@ -167,7 +160,6 @@ (wrap-file (System/getProperty "user.dir")) (wrap-params) (wrap-stacktrace) - ["tutorial"] tutorial-handler ["links"] link-handler ["about"] about-handler ["eval.json"] eval-handler diff --git a/src/tryclojure/tutorial.clj b/src/tryclojure/tutorial.clj deleted file mode 100644 index 79f5839..0000000 --- a/src/tryclojure/tutorial.clj +++ /dev/null @@ -1,214 +0,0 @@ -(ns tryclojure.tutorial - (:use hiccup.core - clj-highlight.core - clj-highlight.syntax.clojure - clj-highlight.output.hiccup)) - -(def clj-highlighter (highlighter clj-syntax (to-hiccup) html-escape-mangler newline-to-br-mangler)) - -(defn code [code] - (html (clj-highlighter code))) - -(def tutorial0-text - (html - [:p.bottom - "This tutorial is intended for people who have never used Clojure before but have experience with " - " other programming languages. This is not meant to be a comprehensive tutorial" - ", but instead intended to get you started with Clojure -- and hopefully interested enough to continue. " - "Please bear in mind that this tutorial is evolving, so check back frequently for new additions." [:br] [:br] - "Above, you have your REPL. Please try the examples as we go along, and by all means experiment" - " with concepts indpendently. Should you refuse, you will promptly be IP banned from this website, and a teddy " - "bear will eat your candies. ;-)"[:br] [:br] "Don't make teddy eat your candy."] - [:p.bottom - "All of the examples (pretty colored text) are clickable. If you click them, they will be copied" - " into the REPL prompt above. This is for convenience, so you don't have to type tedious things like a " - "backwards string (you'll see). Copy and pasting is not allowed in the REPL, because I and the creator of " - "JQuery-console, which powers the REPL above, believe that copying and pasting isn't so great for learning." - " While these examples are clickable, I urge you to type out the examples by hand. If you type them out, " - "they will sink in better. Enjoy the tutorial."] - [:p.bottom "Hit the pretty little 'Next' button to continue for the love of Pete!"])) - -(def tutorial1-text - (html - [:p.bottom - "Let's jump right in there and write some Clojure. Let's start out with the familiar 'Hello, World!' People " - "seem to love it so much. Type the following into the REPL: "] - (code "(println \"!dlrow ,olleH\")") - [:p.bottom - "Something isn't right here! (I'm not talking about the fact that this website's lack of panache) " - "It seems that 'Hello World' has been mangled! It's okay though, because we're smart " - "and we can figure out how to remedy this situation. If you're wondering why it printed our gibberish AND 'nil' " - "it's because 'nil' (which essentially means 'nothing') is println's return value." - "Let's experiment until we figure out what went wrong. " - "It seems that the " (code "reverse") " should fix things -- so type the following into the REPL: "] - (code "(reverse \"!dlrow ,olleH\")") - [:p.bottom - "Hmm. It did indeed set it right, but this isn't what we were looking for! That doesn't even look like a string. " - "Reverse took our string, reversed it, and now we have a sequence of characters. You can tell this because each " - "character has a '\\' (backslash in layman's terms) preceeding it. Clojure uses those to denote Character (big 'C' character) literals. " - "We need a string though! " - "What do we do when we need a string, but we have something else that isn't stringy (a highly technical term)? No, we don't go to the shop " - "and buy string cheese (not to disparage string cheese mind you), but instead we use the lovely " (code "str") " function! Try this: "] - (code "(str (reverse \"!dlrow ,olleH\"))") - [:p.bottom - "Ooh! So close. It appears that the str function is making the entire sequence a string! Sequences can be made to " - " look like strings. str works on any number of arguments. You can pass str any number of arguments and it will " - "call Java's toString method on each of them to change them into a string, and then it will concatenate them " - "all into one mammoth string. Unfortunately, all of our characters are stuck in this sequence! How can we get them out " - "of the sequence, and pass them all to str? Do not fear, young Clojurian, for I have the answer. We use the " - (code "apply") " function. " (code "apply") " takes a function and a sequence, and 'unrolls' the sequence, " - "passing all of its elements to the function as arguments. Basically, it takes the sequence " - "and dumps its elements into the argument list of the supplied function; which is exactly what we need. " - "Let's see if it works... try this: "] - (code "(apply str (reverse \"!dlrow ,olleH\"))") - [:p.bottom - "Ka-chow! There we go. Impressive. I'm proud of you, I really am. Now, for the final test. Let's tell the world " - "just how much they mean to us by making the world rotate in the other direction! Let's do this: "] - (code "(println (apply str (reverse \"!dlrow ,olleH\")))") - [:p.bottom - "Congratulations, you have passed step one. Press Next to continue."])) - -(def tutorial2-text - (html - [:p.bottom - "Alright. We have greeted the world. Now what? Math? Clojure is great at math. Thanks to the uniformity of " - "prefix notation, we don't have to worry about precedence rules; which we kinda loathe anyway. This goes before that, and that goes before that other thing... blah! In Clojure, mathematical operators work like any other function." - " The common operators are +, -, *, and /. Let's try them out: "] - (code "(+ 2 2)\n") - (code "(- 3 2)\n") - (code "(* 5 5)\n") - (code "(/ 4 3)\n") - [:p.bottom - "So, that was great. But that last one doesn't quite look right. The problem is that Clojure has a built in " - "Ratio type. You can confirm this by doing this: " (code "(class (/ 4 3))") "."] - [:p.bottom - "So how do we perform divison that we are familiar with? If you want decimal division, use a floating-point literal for either " - "the numerator or denominator: " (code "(/ 4.0 3)") ". If you want to stick to integer math, you can use the " (code "quot") " function, " - "or the " (code "rem") " function to get the remainder."] - [:p.bottom - "All of the math functions take an arbitrary number of arguments. This means you can do stuff like this: " - (code "(+ 1 2 3 4 5 6)") ". Go ahead, try it out. You know you want to. Play around with the math functions for " - "a while. I'll wait. Go ahead."] - [:p.bottom "That's the end of step two. Press Next to continue to step three, where we'll play with sequences."])) - -(def tutorial3-text - (html - [:p.bottom - "Now that we've covered the basics of prefix math, let's take some time to play with sequences."] - [:p.bottom - "In Clojure, the term 'sequence' is an abstraction that many data structures " - "implement. This abstraction allows any function that works on a 'seq' (pronounced seek) to work on any data " - "structure that is seqable. This means all Clojure and Java collections, strings, and even I/O streams."] - [:p.bottom - "In isolation this doesn't make sense, so let's play with vectors. A literal vector looks like this: " (code "[1 2 3 \"four\" 5 6.0]") ". Vectors are the " - "most commonly used collection in Clojure, along with maps. Let's see if we can square all of the elements " - "of a vector: " (code "(map (fn [x] (* x x)) [1 2 3 4 5])") ". Try that out. The " (code "map") " function takes " - "a function that takes a single argument and returns a value, which it 'applies' to each element of its second argument, a sequence. " - "The resulting sequence is then returned."] - [:p.bottom - "We have introduced something new here -- the anonymous function. Anonymous functions are used quite heavily in Clojure code. " - "The are created using " (code "fn") ". After fn, you supply a vector of arguments that the " - "function takes, and then the body of the function that uses those arguments to computer a value. Clojure has " - "short hand for anonymous functions as well. The above anonymous function can be rewritten using a shorthand form: " - (code "#(+ % %)") ". When you use this shorthand, the function arguments are accessed like so: % or %1 for the " - "first argument, %2 for the second argument, %3 for the third and so on. Our code now looks like this: " - (code "(map #(* % %) [1 2 3 4 5])")] - [:p.bottom - "Another highly important collection is the hashmap. A literal hashmap looks like this: " - (code "{:key \"value\" :key2 3 :key4 [3 4 2 1]}") ". The keys in this map are something called 'keywords'. " - "You can tell that they are keywords because they start with a colon ':' character. They're like symbols that " - "resolve to themselves. In idiomatic Clojure code, keywords are often used as map keys. You can use many objects as map keys, " - "including integers, vectors, strings, and other maps."] - [:p.bottom - "To get a value at a key in a map, we can do this: " (code "({:key1 :val} :key1)") ". I bet you're all lolwut " - "right now, aren't you? In Clojure, hashmaps are functions that take a single argument, which should be a key " - "and they lookup the key within themselves and return the value at the key or nil if the key doesn't exist. " - "Another neat trick for maps with keywords for keys is this: " (code "(:key1 {:key1 :val})") ". Keywords are " - "also functions. They just look themselves up in the supplied map."] - [:p.bottom - "Clojure has all sorts of other data structures as well. Sets, lists, queues, zippers, etc. We'll talk about some of " - "those later on. For now, lets move on to step four. Press Next to continue."])) - -(def tutorial4-text - (html - [:p.bottom - "We've learned a little about Clojure's sequences, so let's use them to do some stuff. I've been wondering how " - "vowels are in the word \"teddybear\". Aren't you wondering the same thing? It's absolutely agonizing not " - "knowing! We're programmers, you and I, so we shouldn't have to count those vowels ourselves. Indeed, we don't " - "We can use Clojure to count them for us!"] - [:p.bottom - "Okay, we'll start with the string \"teddybear\". We're going to be using this string a lot, and we don't want " - "to have to keep typing it over and over again, do we? Luckily, Clojure can help us here. Type this into the " - "REPL: " (code "(def teddy \"teddybear\")") ". What def does is pretty simple: it simply gives a name to a " - "value so that we can refer to it by that name later. Don't worry about what the REPL printed when you typed " - "that, it's just trying to show you exactly where the var is mapped (in this case, a sandbox generated namespace. " - "We need to make sure it worked. Type this into the REPL: " (code "teddy") ". Cool huh?"] - [:p.bottom - "Now that we are armed with a loaded teddybear, we can start figuring out how to find out the number of vowels. " - "For this, we need to introduce a new collection type: sets. A literal set looks like this: " (code "#{3 4 5 \"x\" \\y}") - ". A set can hold anything, but it can't hold any two of the same thing. In a set, there can be no duplicate objects. " - "Another important fact about sets is that they, like maps, are also functions. A set is a function that takes " - "an argument and looks inside itself to see if that same object is inside of it. If this is true, it returns the " - "object, or returns nil. Let's try this out for ourselves: "] - (code "(#{1 2 3} 3)\n") - (code "(#{\"abc\" \\e} \\e)\n") - (code "(#{3 4 \\x} 5)") - [:p.bottom - "Okay, so how is this useful? It really isn't. Not alone, anyway. However, when it's combined with other sequence " - "functions, it can be used to make a really elegant solution to a problem like this."] - [:p.bottom - "Okay, so we have a way to test if a character is a vowel. We can simply do this: " (code "(#{\\a \\e \\i \\o \\u} e)") - ". Since we're going to be using the set of vowels a lot, go ahead and give it a name in the REPL: " - (code "(def vowels #{\\a \\e \\i \\o \\u})") ". Now we can use " (code "vowels") " to refer to the set of vowels. "] - [:p.bottom - "Now, we have a way to find out if a character is a vowel, now we just need a way to remove everything that isn't " - "a vowel from our teddybear string. I have just the function! We need " (code "filter") ". Filter takes what " - "is called a 'predicate', that is, a function that returns true or false, and it applies this function to each " - "element of a sequence in turn. If the predicate function returns false or nil for an element, that element is " - "removed from the sequence. If the predicate function returns anything that isn't false or nil for an element, " - "that element is left alone. In Clojure, anything that isn't false or nil is considered a true value."] - [:p.bottom - "Let's try filter out a bit. Let's try to filter out all odd numbers from a sequence of numbers. Clojure has " - "a function called " (code "odd?") " that we can use. Putting a question mark at the end is a Clojure naming convention " - "for functions that are predicates (return either true or false). Try it out in the REPL:"] - (code "(odd? 1)\n") - (code "(odd? 2)") - [:p.bottom - "Okay, now we need a sequence of numbers. We could type these out by hand, but that's tedious, and as Clojure " - "programmers, we do not tolerate 'tedious'. We can use Clojure's range function to generate these numbers for us. " - (code "range") ", if given one integer, will generate a sequence of numbers from 0 to the integer that you passed to " - "it. Try this: " (code "(range 10)") ". We have a sequence of numbers from 0 to 9. Apparently, the upper-bound " - "(the number you passed to range) is 'inclusive', meaning it's not included in the resulting range. If we really " - "wanted a sequence of numbers from 0 to 10, we'd do this: " (code "(range 11)") "."] - [:p.bottom - "We don't really want the zero, so let's give range a lower-bound, along with an upper-bound. It can take two " - "arguments: " (code "(range 1 11)") ". There we go! We have all of the numbers from 1 to 10. Now, let's use " - "filter and the odd? function to get all of the odd numbers out of the sequence. Try this: " - (code "(filter odd? (range 1 11))") ". Ka-chow! That's magic, isn't it? Work of art, that is."] - [:p.bottom - "Now that we know how to use filter, we can now use it to filter out all of the vowels in our teddy string. " - "We have our string defined in teddy, and our set of vowels in vowels, so we should try this: " - (code "(filter vowels teddy)") - ". That was easy enough. Now we have a sequence of characters. But we still need to count them! Luckily, " - "Clojure has just the function for that: " (code "count") "! It takes a sequence and counts (whoda thunk it) the " - "number of elements in the sequence. Let's try it out: " (code "(count (filter vowels teddy))") ". Yeehaw!" - " You did it! Once again, you never cease to amaze me. You're catching on quickly."] - [:p.bottom "That's the end of step 4. Press Next to move on."])) - -(def tutorial - (html - [:div#tuttext - tutorial0-text] - [:div.continue - [:input#back {:type "button" :value "Back"}] - [:input#continue {:type "button" :value "Next"}]])) - -(defn get-tutorial [step] - (condp = step - "0" tutorial - "1" tutorial0-text - "2" tutorial1-text - "3" tutorial2-text - "4" tutorial3-text - "5" tutorial4-text - "6" "TODO!")) \ No newline at end of file From 09c56e0e20a8da4a1e123ae5d00a70defc736b51 Mon Sep 17 00:00:00 2001 From: Raynes Date: Wed, 13 Apr 2011 23:24:14 -0500 Subject: [PATCH 029/127] New version of jquery-console. --- resources/public/javascript/jquery.console.js | 413 ++++++++++++------ 1 file changed, 287 insertions(+), 126 deletions(-) diff --git a/resources/public/javascript/jquery.console.js b/resources/public/javascript/jquery.console.js index aae45ce..769bc51 100644 --- a/resources/public/javascript/jquery.console.js +++ b/resources/public/javascript/jquery.console.js @@ -1,7 +1,7 @@ // JQuery Console 1.0 // Sun Feb 21 20:28:47 GMT 2010 // -// Copyright 2010 Chris Done. All rights reserved. +// Copyright 2010 Chris Done, Simon David Pratt. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions @@ -10,61 +10,109 @@ // 1. Redistributions of source code must retain the above // copyright notice, this list of conditions and the following // disclaimer. - +// // 2. Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials // provided with the distribution. // -// THIS SOFTWARE IS PROVIDED BY CHRIS DONE ``AS IS'' AND ANY EXPRESS -// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL CHRIS DONE OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT -// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -// DAMAGE. - -// The views and conclusions contained in the software and -// documentation are those of the authors and should not be -// interpreted as representing official policies, either expressed or -// implied, of Chris Done. -// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + // TESTED ON // Internet Explorer 6 // Opera 10.01 // Chromium 4.0.237.0 (Ubuntu build 31094) -// Firefox 3.5.8 +// Firefox 3.5.8, 3.6.2 (Mac) +// Safari 4.0.5 (6531.22.7) (Mac) +// Google Chrome 5.0.375.55 (Mac) (function($){ $.fn.console = function(config){ //////////////////////////////////////////////////////////////////////// // Constants // Some are enums, data types, others just for optimisation - var keyCodes = { left:37,right:39,up:38,down:40,back:8,del:46, - end:35,start:36,ret:13 }; + var keyCodes = { + // left + 37: moveBackward, + // right + 39: moveForward, + // up + 38: previousHistory, + // down + 40: nextHistory, + // backspace + 8: backDelete, + // delete + 46: forwardDelete, + // end + 35: moveToEnd, + // start + 36: moveToStart, + // return + 13: commandTrigger, + // tab + 18: doNothing + }; + var ctrlCodes = { + // C-a + 65: moveToStart, + // C-e + 69: moveToEnd, + // C-d + 68: forwardDelete, + // C-n + 78: nextHistory, + // C-p + 80: previousHistory, + // C-b + 66: moveBackward, + // C-f + 70: moveForward, + // C-k + 75: deleteUntilEnd + }; + var altCodes = { + // M-f + 70: moveToNextWord, + // M-b + 66: moveToPreviousWord, + // M-d + 68: deleteNextWord + }; var cursor = ' '; // Opera only works with this character, not or ­, // but IE6 displays this character, which is bad, so just use // it on Opera. - var wbr = $.browser.opera? '​' : ''; + var wbr = $.browser.opera? '' : '­'; //////////////////////////////////////////////////////////////////////// // Globals var container = $(this); var inner = $('

'); - var typer = $(''); + // erjiang: changed this from a text input to a textarea so we + // can get pasted newlines + var typer = $(''); // Prompt var promptBox; var prompt; var promptLabel = config && config.promptLabel? config.promptLabel : "> "; + var continuedPromptLabel = config && config.continuedPromptLabel? + config.continuedPromptLabel : "> "; var column = 0; var promptText = ''; var restoreText = ''; + var continuedText = ''; // Prompt history stack var history = []; var ringn = 0; @@ -76,6 +124,10 @@ // variable below to ignore the keypress event if the keydown // event succeeds. var cancelKeyPress = 0; + // When this value is false, the prompt will not respond to input + var acceptInput = true; + // When this value is true, the command has been canceled + var cancelCommand = false; // External exports object var extern = {}; @@ -98,18 +150,21 @@ },100); } extern.inner = inner; + extern.typer = typer; extern.scrollToBottom = scrollToBottom; })(); //////////////////////////////////////////////////////////////////////// // Reset terminal extern.reset = function(){ - var welcome = true; + var welcome = (typeof config.welcomeMessage != 'undefined'); inner.parent().fadeOut(function(){ inner.find('div').each(function(){ - if (!welcome) + if (!welcome) { $(this).remove(); - welcome = false; + } else { + welcome = false; + } }); newPromptBox(); inner.parent().fadeIn(function(){ @@ -152,9 +207,13 @@ function newPromptBox() { column = 0; promptText = ''; + ringn = 0; // Reset the position of the history ring + enableInput(); promptBox = $('
'); var label = $(''); - promptBox.append(label.text(promptLabel).show()); + var labelText = extern.continuedPrompt? continuedPromptLabel : promptLabel; + promptBox.append(label.text(labelText).show()); + label.html(label.html().replace(' ',' ')); prompt = $(''); promptBox.append(prompt); inner.append(promptBox); @@ -177,7 +236,21 @@ inner.removeClass('jquery-console-focus'); inner.addClass('jquery-console-nofocus'); }); - + + //////////////////////////////////////////////////////////////////////// + // Bind to the paste event of the input box so we know when we + // get pasted data + typer.bind('paste', function(e) { + // wipe typer input clean just in case + typer.val(""); + // this timeout is required because the onpaste event is + // fired *before* the text is actually pasted + setTimeout(function() { + typer.consoleInsert(typer.val()); + typer.val(""); + }, 0); + }); + //////////////////////////////////////////////////////////////////////// // Handle key hit before translation // For picking up control characters like up/left/down/right @@ -185,19 +258,41 @@ typer.keydown(function(e){ cancelKeyPress = 0; var keyCode = e.keyCode; - if (isControlCharacter(keyCode)) { - cancelKeyPress = keyCode; - if (!typer.consoleControl(keyCode)) { - return false; - } - } + // C-c: cancel the execution + if(e.ctrlKey && keyCode == 67) { + cancelKeyPress = keyCode; + cancelExecution(); + return false; + } + if (acceptInput) { + if (keyCode in keyCodes) { + cancelKeyPress = keyCode; + (keyCodes[keyCode])(); + return false; + } else if (e.ctrlKey && keyCode in ctrlCodes) { + cancelKeyPress = keyCode; + (ctrlCodes[keyCode])(); + return false; + } else if (e.altKey && keyCode in altCodes) { + cancelKeyPress = keyCode; + (altCodes[keyCode])(); + return false; + } + } }); //////////////////////////////////////////////////////////////////////// // Handle key press typer.keypress(function(e){ var keyCode = e.keyCode || e.which; - if (cancelKeyPress != keyCode && keyCode >= 32){ + if (isIgnorableKey(e)) { + return false; + } + // // C-v: don't insert on paste event + if (e.ctrlKey && String.fromCharCode(keyCode).toLowerCase() == 'v') { + return true; + } + if (acceptInput && cancelKeyPress != keyCode && keyCode >= 32){ if (cancelKeyPress) return false; if (typeof config.charInsertTrigger == 'undefined' || (typeof config.charInsertTrigger == 'function' && @@ -207,72 +302,10 @@ if ($.browser.webkit) return false; }); - // Is a keycode a contorl character? - // E.g. up, down, left, right, backspc, return, etc. - function isControlCharacter(keyCode){ - // TODO: Make more precise/fast. - return ( - (keyCode >= keyCodes.left && keyCode <= keyCodes.down) - || keyCode == keyCodes.back || keyCode == keyCodes.del - || keyCode == keyCodes.end || keyCode == keyCodes.start - || keyCode == keyCodes.ret - ); - }; - - //////////////////////////////////////////////////////////////////////// - // Handle console control keys - // E.g. up, down, left, right, backspc, return, etc. - typer.consoleControl = function(keyCode){ - switch (keyCode){ - case keyCodes.left:{ - moveColumn(-1); - updatePromptDisplay(); - return false; - break; - } - case keyCodes.right:{ - moveColumn(1); - updatePromptDisplay(); - return false; - break; - } - case keyCodes.back:{ - if (moveColumn(-1)){ - deleteCharAtPos(); - updatePromptDisplay(); - } - return false; - break; - } - case keyCodes.del:{ - if (deleteCharAtPos()) - updatePromptDisplay(); - return false; - break; - } - case keyCodes.end:{ - if (moveColumn(promptText.length-column)) - updatePromptDisplay(); - return false; - break; - } - case keyCodes.start:{ - if (moveColumn(-column)) - updatePromptDisplay(); - return false; - break; - } - case keyCodes.ret:{ - commandTrigger(); return false; - } - case keyCodes.up:{ - rotateHistory(-1); return false; - } - case keyCodes.down:{ - rotateHistory(1); return false; - } - default: //alert("Unknown control character: " + keyCode); - } + function isIgnorableKey(e) { + // for now just filter alt+tab that we receive on some platforms when + // user switches windows (goes away from the browser) + return ((e.keyCode == keyCodes.tab || e.keyCode == 192) && e.altKey); }; //////////////////////////////////////////////////////////////////////// @@ -294,14 +327,20 @@ } else if (column == 0) { column = promptText.length; } - } else if (config.historyColumnAtEnd) { - column = promptText.length; } else { - column = 0; + column = promptText.length; } updatePromptDisplay(); }; + function previousHistory() { + rotateHistory(-1); + }; + + function nextHistory() { + rotateHistory(1); + }; + // Add something to the history ring function addToHistory(line){ history.push(line); @@ -310,7 +349,7 @@ // Delete the character at the current position function deleteCharAtPos(){ - if (promptText != ''){ + if (column < promptText.length){ promptText = promptText.substring(0,column) + promptText.substring(column+1); @@ -319,6 +358,41 @@ } else return false; }; + function backDelete() { + if (moveColumn(-1)){ + deleteCharAtPos(); + updatePromptDisplay(); + } + }; + + function forwardDelete() { + if (deleteCharAtPos()) + updatePromptDisplay(); + }; + + function deleteUntilEnd() { + while(deleteCharAtPos()) { + updatePromptDisplay(); + } + }; + + function deleteNextWord() { + // A word is defined within this context as a series of alphanumeric + // characters. + // Delete up to the next alphanumeric character + while(column < promptText.length && + !isCharAlphanumeric(promptText[column])) { + deleteCharAtPos(); + updatePromptDisplay(); + } + // Then, delete until the next non-alphanumeric character + while(column < promptText.length && + isCharAlphanumeric(promptText[column])) { + deleteCharAtPos(); + updatePromptDisplay(); + } + }; + //////////////////////////////////////////////////////////////////////// // Validate command and trigger it if valid, or show a validation error function commandTrigger() { @@ -342,35 +416,59 @@ inner.attr({ scrollTop: inner.attr("scrollHeight") });; }; + function cancelExecution() { + if(typeof config.cancelHandle == 'function') { + config.cancelHandle(); + } + } + //////////////////////////////////////////////////////////////////////// // Handle a command function handleCommand() { if (typeof config.commandHandle == 'function') { - var ret = config.commandHandle(promptText,function(msgs){ + disableInput(); + addToHistory(promptText); + var text = promptText; + if (extern.continuedPrompt) { + if (continuedText) + continuedText += '\n' + promptText; + else continuedText = promptText; + } else continuedText = undefined; + if (continuedText) text = continuedText; + var ret = config.commandHandle(text,function(msgs){ commandResult(msgs); }); + if (extern.continuedPrompt && !continuedText) + continuedText = promptText; if (typeof ret == 'boolean') { if (ret) { // Command succeeded without a result. - addToHistory(promptText); commandResult(); } else { - addToHistory(promptText); commandResult('Command failed.', "jquery-console-message-error"); } } else if (typeof ret == "string") { - addToHistory(promptText); commandResult(ret,"jquery-console-message-success"); - } else if (typeof ret == 'undefined') { - addToHistory(promptText); - } else if (ret.length) { - addToHistory(promptText); + } else if (typeof ret == 'object' && ret.length) { commandResult(ret); + } else if (extern.continuedPrompt) { + commandResult(); } } }; + //////////////////////////////////////////////////////////////////////// + // Disable input + function disableInput() { + acceptInput = false; + }; + + // Enable input + function enableInput() { + acceptInput = true; + } + //////////////////////////////////////////////////////////////////////// // Reset the prompt in invalid command function commandResult(msg,className) { @@ -392,20 +490,22 @@ function message(msg,className) { var mesg = $('
'); if (className) mesg.addClass(className); - mesg.html(msg).hide(); + mesg.filledText(msg).hide(); inner.append(mesg); mesg.show(); }; //////////////////////////////////////////////////////////////////////// // Handle normal character insertion - typer.consoleInsert = function(keyCode){ + // data can either be a number, which will be interpreted as the + // numeric value of a single character, or a string + typer.consoleInsert = function(data){ // TODO: remove redundant indirection - var char = String.fromCharCode(keyCode); + var text = isNaN(data) ? data : String.fromCharCode(data); var before = promptText.substring(0,column); var after = promptText.substring(column); - promptText = before + char + after; - moveColumn(1); + promptText = before + text + after; + moveColumn(text.length); restoreText = promptText; updatePromptDisplay(); }; @@ -419,17 +519,77 @@ return true; } else return false; }; - - extern.promptText = function(text){ + + function moveForward() { + if(moveColumn(1)) { + updatePromptDisplay(); + return true; + } + return false; + }; + + function moveBackward() { + if(moveColumn(-1)) { + updatePromptDisplay(); + return true; + } + return false; + }; + + function moveToStart() { + if (moveColumn(-column)) + updatePromptDisplay(); + }; + + function moveToEnd() { + if (moveColumn(promptText.length-column)) + updatePromptDisplay(); + }; + + function moveToNextWord() { + while(column < promptText.length && + !isCharAlphanumeric(promptText[column]) && + moveForward()) { + } + while(column < promptText.length && + isCharAlphanumeric(promptText[column]) && + moveForward()) { + } + }; + + function moveToPreviousWord() { + // Move backward until we find the first alphanumeric + while(column -1 >= 0 && + !isCharAlphanumeric(promptText[column-1]) && + moveBackward()) { + } + // Move until we find the first non-alphanumeric + while(column -1 >= 0 && + isCharAlphanumeric(promptText[column-1]) && + moveBackward()) { + } + }; + + function isCharAlphanumeric(charToTest) { + if(typeof charToTest == 'string') { + var code = charToTest.charCodeAt(); + return (code >= 'A'.charCodeAt() && code <= 'Z'.charCodeAt()) || + (code >= 'a'.charCodeAt() && code <= 'z'.charCodeAt()) || + (code >= '0'.charCodeAt() && code <= '9'.charCodeAt()); + } + return false; + }; + + function doNothing() {}; + + extern.promptText = function(text){ if (text) { promptText = text; - if (column > promptText.length) - column = promptText.length; + column = promptText.length; updatePromptDisplay(); } return promptText; }; - //////////////////////////////////////////////////////////////////////// // Update the prompt display @@ -471,7 +631,8 @@ .replace(/&]{10})/g,'$1­' + wbr) + .replace(/\n/g,'
') + .replace(/([^<>&]{10})/g,'$1' + wbr) ); }; From 2cadd92f46d7a87c48a8d9d7c8495132c2ed5acc Mon Sep 17 00:00:00 2001 From: Raynes Date: Wed, 13 Apr 2011 23:47:03 -0500 Subject: [PATCH 030/127] Fix output. --- src/tryclojure/core.clj | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 9a7334e..fbb9eb4 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -19,8 +19,9 @@ (defn eval-form [form sbox] (with-open [out (java.io.StringWriter.)] - {:expr form - :result (sbox form {#'*out* out})})) + (let [result (sbox form {#'*out* out})] + {:expr form + :result [out result]}))) (defn eval-string [expr sbox] (let [form (binding [*read-eval* false] (read-string expr))] @@ -128,7 +129,8 @@ (defn eval-handler [request] (let [{:keys [expr result error] :as res} (eval-request request) - history (get-in request [:session :history] [])] + history (get-in request [:session :history] []) + [out res] result] (if error (merge eval-response-defaults {:session (:session request) @@ -136,7 +138,7 @@ (merge eval-response-defaults {:session {:history (conj history expr)} :body (json/json-str {:expr (pr-str expr) - :result (pr-str result)})})))) + :result (str out (pr-str res))})})))) (defn- max-history [max history] (if (> (count history) max) From 3e435014cec964444665de3b47d29d33f3fa44c2 Mon Sep 17 00:00:00 2001 From: Raynes Date: Wed, 13 Apr 2011 23:47:11 -0500 Subject: [PATCH 031/127] Updates. --- resources/public/tutorial/page1.html | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/resources/public/tutorial/page1.html b/resources/public/tutorial/page1.html index cef275b..0661a97 100644 --- a/resources/public/tutorial/page1.html +++ b/resources/public/tutorial/page1.html @@ -7,31 +7,13 @@

- This tutorial is intended for people who have never used Clojure before but have experience - with other programming languages. This is not meant to be a comprehensive tutorial, but instead - intended to get you started with Clojure -- and hopefully interested enough to continue. - Please bear in mind that this tutorial is evolving, so check back frequently for new additions. -

Above, you have your REPL. Please try the examples as we go along, and by all means experiment with concepts independently. Should you refuse, you will promptly be IP banned from this - website, and a teddy bear will eat your candies. ;-) -

- Don't make teddy eat your candy. + website, and a teddy bear will eat your candies. ctrl+v to paste and ctrl+c to copy like you + would normally expect, but you can't paste with your browser's right-click menu.

- All of the examples (pretty colored text) are clickable. If you click them, they will be copied - into the REPL prompt above. This is for convenience, so you don't have to type tedious things - like a backwards string (you'll see). Copy and pasting is not allowed in the REPL, because I and - the creator of JQuery-console, which powers the REPL above, believe that copying and pasting isn't - so great for learning. While these examples are clickable, I urge you to type out the examples by - hand. If you type them out, they will sink in better. Enjoy the tutorial. -

- -

- You can type 'next' to move forward and 'back' to go back to the previous step. -

- -

- Type 'next' in the REPL to continue! + You can type 'next' to move forward and 'back' to go back to the previous step. Go ahead and + type 'next' to begin the tutorial!

From f870988af6d9dea135d2bad00fafbd515c42c1ea Mon Sep 17 00:00:00 2001 From: Raynes Date: Wed, 13 Apr 2011 23:47:24 -0500 Subject: [PATCH 032/127] Not sure why we're doing this. Bet I find out in the worst possible way. --- resources/public/javascript/tryclojure.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/public/javascript/tryclojure.js b/resources/public/javascript/tryclojure.js index ef62847..6166c0f 100644 --- a/resources/public/javascript/tryclojure.js +++ b/resources/public/javascript/tryclojure.js @@ -122,7 +122,7 @@ function onHandle(line, report) { // handle error if (data.error) { - return [{msg: html_escape(data.message), className: "jquery-console-message-error"}]; + return [{msg: data.message, className: "jquery-console-message-error"}]; } // handle page @@ -131,7 +131,7 @@ function onHandle(line, report) { } // display expr results - return [{msg: html_escape(data.result), className: "jquery-console-message-value"}]; + return [{msg: data.result, className: "jquery-console-message-value"}]; } /** From 9ea1288f99e8cd8a8447bc680328838280669f2d Mon Sep 17 00:00:00 2001 From: Raynes Date: Thu, 14 Apr 2011 00:09:09 -0500 Subject: [PATCH 033/127] Fixed bugzors where errors would log up the console. --- src/tryclojure/core.clj | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index fbb9eb4..514355d 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -39,6 +39,7 @@ (catch TimeoutException _ {:error true :message "Execution Timed Out!"}) (catch Exception e + (println e) {:error true :message (.getMessage (root-cause e))})))) (def links @@ -128,17 +129,17 @@ :headers {"Content-Type" "application/json"}}) (defn eval-handler [request] - (let [{:keys [expr result error] :as res} (eval-request request) - history (get-in request [:session :history] []) - [out res] result] + (let [{:keys [expr result error message] :as res} (eval-request request) + history (get-in request [:session :history] [])] (if error (merge eval-response-defaults {:session (:session request) :body (json/json-str res)}) - (merge eval-response-defaults - {:session {:history (conj history expr)} - :body (json/json-str {:expr (pr-str expr) - :result (str out (pr-str res))})})))) + (let [[out res] result] + (merge eval-response-defaults + {:session {:history (conj history expr)} + :body (json/json-str {:expr (pr-str expr) + :result (str out (pr-str res))})}))))) (defn- max-history [max history] (if (> (count history) max) @@ -151,7 +152,7 @@ (->> (get-in response [:session :history] []) (max-history 5) (filter has-state?) - (vec) + vec (assoc-in response [:session :history]))))) (def clojureroutes From 2af23ddb3805342324a7afeed6cce267a9b65032 Mon Sep 17 00:00:00 2001 From: Raynes Date: Thu, 14 Apr 2011 00:10:09 -0500 Subject: [PATCH 034/127] Remove debug println. --- src/tryclojure/core.clj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 514355d..a2edf0d 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -39,7 +39,6 @@ (catch TimeoutException _ {:error true :message "Execution Timed Out!"}) (catch Exception e - (println e) {:error true :message (.getMessage (root-cause e))})))) (def links From d53a8d28066b80ea77622fd1b485b58d6ca00709 Mon Sep 17 00:00:00 2001 From: Raynes Date: Thu, 14 Apr 2011 09:13:07 -0500 Subject: [PATCH 035/127] Mention restart. --- resources/public/tutorial/page1.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/public/tutorial/page1.html b/resources/public/tutorial/page1.html index 0661a97..255504f 100644 --- a/resources/public/tutorial/page1.html +++ b/resources/public/tutorial/page1.html @@ -14,6 +14,6 @@

- You can type 'next' to move forward and 'back' to go back to the previous step. Go ahead and - type 'next' to begin the tutorial! + You can type 'next' to move forward, 'back' to go back to the previous step, and 'restart' to + restart the tutorial. Go ahead and type 'next' to begin

From 12d6529249c869896f0d93264790ded110e748aa Mon Sep 17 00:00:00 2001 From: Raynes Date: Thu, 14 Apr 2011 10:39:12 -0500 Subject: [PATCH 036/127] Rewrite entire tutorial from scratch. --- resources/public/javascript/tryclojure.js | 32 +++++++- resources/public/tutorial/page10.html | 30 +++++++ resources/public/tutorial/page11.html | 8 ++ resources/public/tutorial/page2.html | 56 +------------ resources/public/tutorial/page3.html | 35 ++------ resources/public/tutorial/page4.html | 56 ++----------- resources/public/tutorial/page5.html | 97 ++--------------------- resources/public/tutorial/page6.html | 7 ++ resources/public/tutorial/page7.html | 12 +++ resources/public/tutorial/page8.html | 17 ++++ resources/public/tutorial/page9.html | 12 +++ 11 files changed, 135 insertions(+), 227 deletions(-) create mode 100644 resources/public/tutorial/page10.html create mode 100644 resources/public/tutorial/page11.html create mode 100644 resources/public/tutorial/page6.html create mode 100644 resources/public/tutorial/page7.html create mode 100644 resources/public/tutorial/page8.html create mode 100644 resources/public/tutorial/page9.html diff --git a/resources/public/javascript/tryclojure.js b/resources/public/javascript/tryclojure.js index 6166c0f..d89d79d 100644 --- a/resources/public/javascript/tryclojure.js +++ b/resources/public/javascript/tryclojure.js @@ -7,19 +7,43 @@ var pages = [ }, { url: "/resources/public/tutorial/page2.html", - verify: function(data) { return false; } + verify: function(data) { return data.expr == "(+ 3 3)"; } }, { url: "/resources/public/tutorial/page3.html", - verify: function(data) { return false; } + verify: function(data) { return data.expr == "(/ 10 3)"; } }, { url: "/resources/public/tutorial/page4.html", - verify: function(data) { return false; } + verify: function(data) { return data.expr == "(/ 10 3.0)"; } }, { url: "/resources/public/tutorial/page5.html", - verify: function(data) { return false; } + verify: function(data) { return data.expr == "(+ 1 2 3 4 5 6)"; } + } + { + url: "/resources/public/tutorial/page6.html", + verify: function data { return data.expr == "(defn square [x] (* x x))"; } + } + { + url: "/resources/public/tutorial/page7.html", + verify: function data { return data.expr == "(square 10)"; } + } + { + url: "/resources/public/tutorial/page8.html", + verify: function data { return data.expr == "((fn [x] (* x x)) 10)"; } + } + { + url: "/resources/public/tutorial/page9.html", + verify: function data { return data.expr == "(def square (fn [x] (* x x)))"; } + } + { + url: "/resources/public/tutorial/page10.html", + verify: function data { return data.expr == "(map inc [1 2 3 4])"; } + } + { + url: "/resources/public/tutorial/page11.html", + verify: function data { return false; } } ]; diff --git a/resources/public/tutorial/page10.html b/resources/public/tutorial/page10.html new file mode 100644 index 0000000..2b739c7 --- /dev/null +++ b/resources/public/tutorial/page10.html @@ -0,0 +1,30 @@ +

Awesome. Now you can call this function just like we called the old square function.

+ +

+ Clojure has a great set of immutable and persistent data structures. Vectors, lists, maps, sets, + I get chills. +

+ +

+ Vectors: [1 2 3 4]
+ Maps: {:foo "bar" 3 4}
+ Lists: '(1 2 3 4)
+ Sets: #{1 2 3 4}
+ Vectors and lists are sequential and ordered collections. You'll see vectors used much more than lists. + Maps are typical hash-maps. Unordered collections indexed by keys. The keys can be any object. Here, + we've used a keyword, :foo as a key. Keywords make excellent keys. We also used a number. + Sets are mathematical sets. +

+ +

+ Clojure's collections are one of the most imporant parts of Clojure. Being a functional language, Clojure + encourages immutability and as little state as possible. Therefore, instead of for loops mutating variables + and such, most of the time you'll see higher order functions doing transformations on immutable data and + returning new collections rather than ever modifying the old one. +

+ +

+ A prime example of this is map. We can use map, a higher order function (which is a function + that takes functions as arguments or returns functions), to 'map' a function to every element of a sequence. + Let's use this to increment each number in a vector. Type (map inc [1 2 3 4]) to continue. +

diff --git a/resources/public/tutorial/page11.html b/resources/public/tutorial/page11.html new file mode 100644 index 0000000..da67d0a --- /dev/null +++ b/resources/public/tutorial/page11.html @@ -0,0 +1,8 @@ +

Excellent work.

+ +

+ Well, that's all there is right now. This tutorial is still a work in progress, and I'm working on more + steps. If anybody wants to contribute, you can find a link to the Github repository on the 'about' page. + Furthermore, if you're just a brand new Clojure developer looking for some learning experiences, check + out the 'links' page. You'll find links to some tutorials and such there. +

diff --git a/resources/public/tutorial/page2.html b/resources/public/tutorial/page2.html index 7c3a7dc..8a8809d 100644 --- a/resources/public/tutorial/page2.html +++ b/resources/public/tutorial/page2.html @@ -1,56 +1,6 @@

- Let's jump right in there and write some Clojure. Let's start out with the familiar - 'Hello, World!'. People seem to love it so much. Type the following into the REPL: + I bet you're curious to find out what Clojure code looks like, aren't you? Sure you are. + Well, I've give you a hint: it's made up of lists. Let's do some arithmetic.

-(println "!dlrow ,olleH") - -

- Something isn't right here! (I'm not talking about the fact that this website's lack of panache). - It seems that 'Hello World' has been mangled! It's okay though, because we're smart and we can - figure out how to remedy this situation. If you're wondering why it printed our gibberish AND - 'nil' it's because 'nil' (which essentially means 'nothing') is println's return value. Let's - experiment until we figure out what went wrong. It seems that the reverse - should fix things -- so type the following into the REPL: -

- -(reverse "!dlrow ,olleH") - -

- Hmm. It did indeed set it right, but this isn't what we were looking for! That doesn't even look - like a string. Reverse took our string, reversed it, and now we have a sequence of characters. - You can tell this because each character has a '\\' (backslash in layman's terms) preceeding it. - Clojure uses those to denote Character (big 'C' character) literals. We need a string though! - What do we do when we need a string, but we have something else that isn't stringy (a highly - technical term)? No, we don't go to the shop and buy string cheese (not to disparage string - cheese mind you), but instead we use the lovely str function! Try this: -

- -(str (reverse "!dlrow ,olleH")) - -

- Ooh! So close. It appears that the str function is making the entire sequence a string! - Sequences can be made to look like strings. str works on any number of arguments. You can pass - str any number of arguments and it will call Java's toString method on each of them to change - them into a string, and then it will concatenate them all into one mammoth string. Unfortunately, - all of our characters are stuck in this sequence! How can we get them out of the sequence, and - pass them all to str? Do not fear, young Clojurian, for I have the answer. We use the - apply function. apply takes a function and a sequence, and 'unrolls' - the sequence, passing all of its elements to the function as arguments. Basically, it takes the - sequence and dumps its elements into the argument list of the supplied function; which is exactly - what we need. Let's see if it works... Try this: -

- -(apply str (reverse "!dlrow ,olleH")) - -

- Ka-chow! There we go. Impressive. I'm proud of you, I really am. Now, for the final test. Let's - tell the world just how much they mean to us by making the world rotate in the other direction! - Let's do this: -

- -(println (apply str (reverse "!dlrow ,olleH"))) - -

- Congratulations, you have passed step one. Type next to continue. -

+(+ 3 3) diff --git a/resources/public/tutorial/page3.html b/resources/public/tutorial/page3.html index a6eea47..7c417cb 100644 --- a/resources/public/tutorial/page3.html +++ b/resources/public/tutorial/page3.html @@ -1,35 +1,12 @@

- Alright. We have greeted the world. Now what? Math? Clojure is great at math. Thanks - to the uniformity of prefix notation, we don't have to worry about precedence rules; - which we kinda loathe anyway. This goes before that, and that goes before that other - thing... blah! In Clojure, mathematical operators work like any other function. The - common operators are +, -, *, and /. Let's try them out: + Excellent work! There are other arithmetic funcions.

- -

(+ 2 2)

-

(- 3 2)

-

(* 5 5)

-

(/ 4 3)

+* +- +/

- So, that was great. But that last one doesn't quite look right. The problem is that Clojure - has a built-in Ratio type. You can confirm this by doing this: (class (/ 4 3)). + Try a them out. Save / for last (don't worry, I have a reason!).

-

- So how do we perform division that we are familiar with? If you want decimal division, use - a floating-point literal for either the numerator or denominator: - (/ 4.0 3). If you want to stick to integer math, you can use the - quot function, or the rem function to get the remainder. -

- -

- All of the math functions take an arbitrary number of arguments. This means you can do stuff - like this: (+ 1 2 3 4 5 6). Go ahead, try it out. You know you want to. Play - around with the math functions for a while. I'll wait. Go ahead. -

- -

- That's the end of step two. Type next to continue to step three, where we'll play with - sequences. -

+

Once you're finished playing around, try (/ 10 3).

diff --git a/resources/public/tutorial/page4.html b/resources/public/tutorial/page4.html index 509d075..85baae8 100644 --- a/resources/public/tutorial/page4.html +++ b/resources/public/tutorial/page4.html @@ -1,54 +1,8 @@

- Now that we've covered the basics of prefix math, let's take some time to play with sequences. + I bet that caught you by surprise, didn't it? Don't fear! Clojure simply has a built in Rational type. + We can prove that we've got a rational by running (type (/ 10 3)). Rationals are more + concise and precise than floating point numbers. However, we can force Clojure to do floating point + division by just making one of our numbers floating point.

-

- In Clojure, the term 'sequence' is an abstraction that many data structures implement. This - abstraction allows any function that works on a 'seq' (pronounced seek) to work on any data - structure that is seqable. This means all Clojure and Java collections, strings, and even - I/O streams. -

- -

- In isolation this doesn't make sense, so let's play with vectors. A literal vector looks like - this: [1 2 3 "four" 5 6.0]. Vectors are the most commonly used collection in - Clojure, along with maps. Let's see if we can square all of the elements of a vector: - (map (fn [x] (* x x)) [1 2 3 4 5]). Try that out. The map function - takes a function that takes a single argument and returns a value, which it 'applies' to each - element of its second argument, a sequence. The resulting sequence is then returned. -

- -

- We have introduced something new here -- the anonymous function. Anonymous functions are used - quite heavily in Clojure code. They are created using fn. After fn, you supply - a vector of arguments that the function takes, and then the body of the function that uses those - arguments to compute a value. Clojure has short hand for anonymous functions as well. The above - anonymous function can be rewritten using a shorthand form: #(* % %). When you - use this shorthand, the function arguments are accessed like so: % or %1 for the first - argument, %2 for the second argument, %3 for the third and so on. Our code looks like this: - (map #(* % %) [1 2 3 4 5]) -

- -

- Another highly important collection is the hashmap. A literal hashmap looks like this: - {:key "value" :key2 3 :key4 [3 4 2 1]}. The keys in this map are something - called 'keywords'. You can tell that they are keywords because they start with a colon ':' character. - They're like symbols that resolve to themselves. In idiomatic Clojure code, keywords are often - used as map keys. You can use many objects as map keys, including integers, vectors, strings, - and other maps. -

- -

- To get a value at a key in a map, we can do this: ({:key1 :val1} :key1). I bet - you're all lolwut right now, aren't you? In Clojure, hashmaps are functions that take a single - argument, which should be a key and they lookup the key within themselves and return the value - at the key or nil if the key doesn't exist. Another neat trick for maps with keywords for keys - is this: (:key1 {:key1 :val1}). Keywords are also functions. They just look - themselves up in the supplied map. -

- -

- Clojure has all sorts of other data structures as well. Sets, lists, queues, zippers, etc. - We'll talk about some of those later on. For now, let's move on to step four. Type next to - continue. -

+

Type (/ 10 3.0) to continue.

diff --git a/resources/public/tutorial/page5.html b/resources/public/tutorial/page5.html index 78fbce7..18101c4 100644 --- a/resources/public/tutorial/page5.html +++ b/resources/public/tutorial/page5.html @@ -1,94 +1,11 @@ -

- We've learned a little about Clojure's sequences, so let's use them to do some stuff. I've been - wondering how vowels are in the word "teddybear". Aren't you wondering the same thing? It's - absolutely agonizing not knowing! We're programmers, you and I, so we shouldn't have to count - those vowels ourselves. Indeed, we don't. We can use Clojure to count them for us! -

- -

- Okay, we'll start with the string "teddybear". We're going to be using this string a lot, and we - don't want to have to keep typing it over and over again, do we? Luckily, Clojure can help us here. - Type this into the REPL: (def teddy "teddybear"). What def does is pretty - simple: it simply gives a name to a value so that we can refer to it by that name later. Dont' worry - about what the REPL printed when you typed that, it's just trying to show you exactly where the var is - mapped (in this case, a sandbox generated namespace). We need to make sure it worked. Type this - into the REPL: teddy. Cool huh? -

- -

- Now that we are armed with a loaded teddybear, we can start figuring out how to find out the number - of vowels. For this, we need to introduce a new collection type: sets. A literal set looks like this: - #{3 4 5 "x" \y}. A set can hold anything, but it can't hold any two of the same - thing. In a set, there can be no duplicate objects. Another important fact about sets is that they, like - maps, are also functions. A set is a function that takes an argument and looks inside itself to see - if that same object is inside of it. If this is true, it returns the object, or returns nil. Let's - try this out for ourselves: -

- -

(#{1 2 3} 3)

-

(#{"abc" \e} \e)

-

(#{3 4 \x} 5)

- -

- Okay, so how is this useful? It really isn't. Not alone, anyway. However, when it's combined with - other sequence functions, it can be used to make a really elegant solution to a problem like this. -

- -

- Okay, so we have a way to test if a character is a vowel. We can simply do this: - (#{\a \e \i \o \u} \e). Since we're going to be using the set of vowels a lot, - go ahead and give it a name in the REPL: (def vowels #{\a \e \i \o \u}). Now we - can use vowels to refer to the set of vowels. -

+

Awesome!

- Now, we have a way to find out if a character is a vowel, now we just need a way to remove - everything that isn't a vowel from our teddybear string. I have just the function! We need - filter. Filter takes what is called a 'predicate', that is, a function that returns - true or false, and it applies this function to each element of a sequence in turn. If the predicate - function returns false or nil for an element, that element is removed from the sequence. If the - predicate function returns anything that isn't false or nil for an element, that element is left - alone. In Clojure, anything that isn't false or nil is considered a true value. + Another neat thing about Clojure is that functions can take an arbitrary number of arguments. + Functions are allowed to specify a 'catch-all' to put an optional and arbitrary number of arguments + into. Because of this, a lot of Clojure's core functions have interesting capabilities. For example + the arithmetic functions we've already played with are not limited to two arguments! No sir, they can + take any number of arguments they wish.

-

- Let's try filter out a bit. Let's try to filter out all odd numbers from a sequence of numbers. - Clojure has a function called odd? that we can use. Putting a question mark at the end - is a Clojure naming convention for functions that are predicates (return either true or false). Try it - out in the REPL: -

- -

(odd? 1)

-

(odd? 2)

- -

- Okay, now we need a sequence of numbers. We could type these out by hand, but that's tedious, and as - Clojure programmers, we do not tolerate 'tedious'. We can use Clojure's range function to generate - these numbers for us. range, if given one integer, will generate a sequence of numbers - from 0 to the integer that you passed to it. Try this: (range 10). We have a - sequence of numbers from 0 to 9. Apparently, the upper-bound (the number you passed to range) is - 'exclusive', meaning it's not included in the resulting range. If we really wanted a sequence of - numbers from 0 to 10, we'd do this: (range 11). -

- -

- We don't really want the zero, so let's give range a lower-bound, along with an upper-bound. It can - take two arguments: (range 1 11). There we go! We have all of the numbers from - 1 to 10. Now, let's use filter and the odd? function to get all of the odd numbers out of the sequence. - Try this: (filter odd? (range 1 11)). Ka-chow! That's magic, isn't it? Work of - art, that is. -

- -

- Now that we know how to use filter, we can now use it to filter out all of the vowels in our teddy - string. We have our string defined in teddy, and our set of vowels in vowels, so we should try this: - (filter vowels teddy). That was easy enough. Now we have a sequence of - characters. But we still need to count them! Luckily, Clojure has just the function for that: - count! It takes a sequence and counts (whoda thunk it) the number of elements in the - sequence. Let's try it out: (count (filter vowels teddy)). Yeehaw! You did it! - Once again, you never cease to amaze me. You're catching on quickly. -

- -

- That's the end of step 4. Type next to move on. -

+

Try it out. Type (+ 1 2 3 4 5 6) to continue.

diff --git a/resources/public/tutorial/page6.html b/resources/public/tutorial/page6.html new file mode 100644 index 0000000..b006997 --- /dev/null +++ b/resources/public/tutorial/page6.html @@ -0,0 +1,7 @@ +

Alright, that's enough math. Let's do some fun stuff, like write functions.

+ +

+ You can define functions in Clojure with defn +

+ +

Type (defn square [x] (* x x)) to continue!

diff --git a/resources/public/tutorial/page7.html b/resources/public/tutorial/page7.html new file mode 100644 index 0000000..151f964 --- /dev/null +++ b/resources/public/tutorial/page7.html @@ -0,0 +1,12 @@ +

Oh boy! We wrote our very own function! It's a lovely one, isn't it?

+ +

+ Our square function takes a single argument -- a number -- and squares it. +

+ +

+ Take a look at how our defn form looks. First comes the name of the function, then the argument list + (which is a vector, and not an actual list), then the body of the function. +

+ +

But wait! We don't even know if our function works or not! Let's try it out. Type (square 10).

diff --git a/resources/public/tutorial/page8.html b/resources/public/tutorial/page8.html new file mode 100644 index 0000000..50f919e --- /dev/null +++ b/resources/public/tutorial/page8.html @@ -0,0 +1,17 @@ +

Yay! It works!

+ +

+ You know, Clojure is a functional programming language. As such, it has first-class and + anonymous functions. Let's write our square function as an anonymous function. +

+(fn [x] (* x x)) +

+ If you run this in the REPL above (as you should), you'll note that some very weird and + cryptic thing is printed. Functions are just normal values like a number, a string, or + anything else. The cryptic thing is simply how they look when printed. +

+ +

+ Our anonymous function isn't very useful if we don't call it. Let's do it. Type + ((fn [x] (* x x)) 10) +

diff --git a/resources/public/tutorial/page9.html b/resources/public/tutorial/page9.html new file mode 100644 index 0000000..4fe93e4 --- /dev/null +++ b/resources/public/tutorial/page9.html @@ -0,0 +1,12 @@ +

+ Yay! Notice how we called our anonymous function? We just wrapped the function in parentheses, + placing it as the first element in this new list and passing it arguments just like we did earlier + with the arithmetic functions. Awesome, huh? +

+ +

+ You may not know this, but defn is actually just a bit of sugar around def + and fn to create named functions. We can create named functions without defn +

+ +

Type (def square (fn [x] (* x x))) to continue.

From d8631b95923b448c4e554dfdedbeb7c0a0e5b472 Mon Sep 17 00:00:00 2001 From: Raynes Date: Thu, 14 Apr 2011 10:39:25 -0500 Subject: [PATCH 037/127] Update link to Getting Started pages. --- src/tryclojure/core.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index a2edf0d..fe6db05 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -44,7 +44,7 @@ (def links (html (unordered-list [(link-to "http://clojure.org" "The official Clojure website") - (link-to "http://www.assembla.com/wiki/show/clojure/Getting_Started" "Getting started with Clojure") + (link-to "http://dev.clojure.org/display/doc/Getting+Started" "Getting started with Clojure") (link-to "http://groups.google.com/group/clojure" "Clojure mailing list") (link-to "http://java.ociweb.com/mark/clojure/article.html" "A comprehensive Clojure tutorial") (link-to "http://joyofclojure.com/" "The Joy of Clojure: a book by Michael Fogus and Chris Houser") From e7d3d7bb311cf1b1630686756223b12e716765f5 Mon Sep 17 00:00:00 2001 From: Raynes Date: Thu, 14 Apr 2011 10:53:05 -0500 Subject: [PATCH 038/127] Fix stupid. --- resources/public/javascript/tryclojure.js | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/resources/public/javascript/tryclojure.js b/resources/public/javascript/tryclojure.js index d89d79d..0cc4902 100644 --- a/resources/public/javascript/tryclojure.js +++ b/resources/public/javascript/tryclojure.js @@ -20,30 +20,30 @@ var pages = [ { url: "/resources/public/tutorial/page5.html", verify: function(data) { return data.expr == "(+ 1 2 3 4 5 6)"; } - } + }, { url: "/resources/public/tutorial/page6.html", - verify: function data { return data.expr == "(defn square [x] (* x x))"; } - } + verify: function (data) { return data.expr == "(defn square [x] (* x x))"; } + }, { url: "/resources/public/tutorial/page7.html", - verify: function data { return data.expr == "(square 10)"; } - } + verify: function (data) { return data.expr == "(square 10)"; } + }, { url: "/resources/public/tutorial/page8.html", - verify: function data { return data.expr == "((fn [x] (* x x)) 10)"; } - } + verify: function (data) { return data.expr == "((fn [x] (* x x)) 10)"; } + }, { url: "/resources/public/tutorial/page9.html", - verify: function data { return data.expr == "(def square (fn [x] (* x x)))"; } - } + verify: function (data) { return data.expr == "(def square (fn [x] (* x x)))"; } + }, { url: "/resources/public/tutorial/page10.html", - verify: function data { return data.expr == "(map inc [1 2 3 4])"; } - } + verify: function (data) { return data.expr == "(map inc [1 2 3 4])"; } + }, { url: "/resources/public/tutorial/page11.html", - verify: function data { return false; } + verify: function (data) { return false; } } ]; From 4d5b0c3ccb42c33ba0baa3b0f701c27d6b878d72 Mon Sep 17 00:00:00 2001 From: Raynes Date: Thu, 14 Apr 2011 12:18:10 -0500 Subject: [PATCH 039/127] Can't serve tutorial pages using ring's static stuff. Have to do it manually in order to preserve session. --- resources/public/javascript/tryclojure.js | 15 ++--------- src/tryclojure/core.clj | 32 ++++++++++++++--------- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/resources/public/javascript/tryclojure.js b/resources/public/javascript/tryclojure.js index 0cc4902..8259592 100644 --- a/resources/public/javascript/tryclojure.js +++ b/resources/public/javascript/tryclojure.js @@ -2,47 +2,36 @@ var pageNum = -1; var page = null; var pages = [ { - url: "/resources/public/tutorial/page1.html", verify: function(data) { return false; } }, { - url: "/resources/public/tutorial/page2.html", verify: function(data) { return data.expr == "(+ 3 3)"; } }, { - url: "/resources/public/tutorial/page3.html", verify: function(data) { return data.expr == "(/ 10 3)"; } }, { - url: "/resources/public/tutorial/page4.html", verify: function(data) { return data.expr == "(/ 10 3.0)"; } }, { - url: "/resources/public/tutorial/page5.html", verify: function(data) { return data.expr == "(+ 1 2 3 4 5 6)"; } }, { - url: "/resources/public/tutorial/page6.html", verify: function (data) { return data.expr == "(defn square [x] (* x x))"; } }, { - url: "/resources/public/tutorial/page7.html", verify: function (data) { return data.expr == "(square 10)"; } }, { - url: "/resources/public/tutorial/page8.html", verify: function (data) { return data.expr == "((fn [x] (* x x)) 10)"; } }, { - url: "/resources/public/tutorial/page9.html", verify: function (data) { return data.expr == "(def square (fn [x] (* x x)))"; } }, { - url: "/resources/public/tutorial/page10.html", verify: function (data) { return data.expr == "(map inc [1 2 3 4])"; } }, { - url: "/resources/public/tutorial/page11.html", verify: function (data) { return false; } } ]; @@ -55,7 +44,7 @@ function showPage(n) { var block = $("#changer"); block.fadeOut(function(e) { - block.load(res.url, function() { + block.load("/tutorial", { 'n' : n+1 }, function() { block.fadeIn(); changerUpdated(); }); @@ -188,7 +177,7 @@ $(document).ready(function() { $("#about").click(setupLink("about")); $("#links").click(setupLink("links")); - $("#tutorial").click(function(e) { + $("#home").click(function(e) { showPage(0); }); diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index fe6db05..440268a 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -73,6 +73,12 @@ "Also thanks to my buddy Heinz (Licenser) for all the help with this site. This site is hosted on" " his server, with his domain name. He completely funds this project and it wouldn't be possible without him."])) +(def home-text + [:p.bottom + "Welcome to Try Clojure. Above, you have a Clojure REPL. You can type expressions and see " + "their results right here in your browser. We also have a brief tutorial to give you a " + "taste of Clojure. Try it out by typing tutorial in the console!"]) + (def fire-html (html (:html4 doctype) @@ -94,11 +100,7 @@ [:div#buttons [:a#links.buttons "links"] [:a#about.buttons.last "about"]] - [:div#changer - [:p.bottom - "Welcome to Try Clojure. Above, you have a Clojure REPL. You can type expressions and see " - "their results right here in your browser. We also have a brief tutorial to give you a " - "taste of Clojure. Try it out by typing tutorial in the console!"]]] + [:div#changer home-text]] [:div.footer [:p.bottom "©2011 Anthony Grimes (Raynes) and contributors"] [:p.bottom "Domain kindly paid for by " @@ -140,6 +142,12 @@ :body (json/json-str {:expr (pr-str expr) :result (str out (pr-str res))})}))))) +(defn tutorial-handler [{{n "n"} :params session :session :as res}] + {:status 200 + :headers {"Content-Type" "application/html"} + :session session + :body (slurp (str "resources/public/tutorial/page" n ".html"))}) + (defn- max-history [max history] (if (> (count history) max) (drop 1 history) @@ -155,16 +163,16 @@ (assoc-in response [:session :history]))))) (def clojureroutes - (app - (wrap-session) - (wrap-post-history) - ;(wrap-reload '(tryclojure.core tryclojure.tutorial)) - (wrap-file (System/getProperty "user.dir")) - (wrap-params) - (wrap-stacktrace) + (app + (wrap-session) + (wrap-post-history) + (wrap-file (System/getProperty "user.dir")) + (wrap-params) + (wrap-stacktrace) ["links"] link-handler ["about"] about-handler ["eval.json"] eval-handler + ["tutorial"] tutorial-handler [""] handler)) (defn tryclj [] (run-jetty #'clojureroutes {:port 8801})) From a2e6ba1b0e74676739cf970b0e08230722634633 Mon Sep 17 00:00:00 2001 From: Raynes Date: Thu, 14 Apr 2011 12:23:02 -0500 Subject: [PATCH 040/127] Changed page 1 text a bit. --- resources/public/tutorial/page1.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/public/tutorial/page1.html b/resources/public/tutorial/page1.html index 255504f..5029ec3 100644 --- a/resources/public/tutorial/page1.html +++ b/resources/public/tutorial/page1.html @@ -1,8 +1,8 @@

- I am in the process of rewriting this tutorial and, of course, adding content. This tutorial - was thrown together rather quickly and hasn't been touched in quite some time. I ask for + I am in the process of rewriting this tutorial and, of course, adding content. The old tutorial + was thrown together rather quickly and wasn't touched afterwards. I ask for patience while I get everything working smoothly. I have a lot going on in my life including - a book and a new job. Furthermore, I don't even really know Javascript, so anything JavaScript + a book and an upcoming new job. Furthermore, I don't even really know Javascript, so anything JavaScript related moves extremely slowly. Working on that. ;)

From 7f07f7007e9c951e2c503f1af00c34d48787c812 Mon Sep 17 00:00:00 2001 From: Alan Malloy Date: Sat, 16 Apr 2011 10:59:36 -0700 Subject: [PATCH 041/127] Make the tutorial code samples clickable again --- resources/public/tutorial/page10.html | 10 +++++----- resources/public/tutorial/page2.html | 2 +- resources/public/tutorial/page3.html | 2 +- resources/public/tutorial/page4.html | 4 ++-- resources/public/tutorial/page5.html | 2 +- resources/public/tutorial/page6.html | 2 +- resources/public/tutorial/page7.html | 2 +- resources/public/tutorial/page8.html | 4 ++-- resources/public/tutorial/page9.html | 2 +- src/tryclojure/core.clj | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/resources/public/tutorial/page10.html b/resources/public/tutorial/page10.html index 2b739c7..9968bcb 100644 --- a/resources/public/tutorial/page10.html +++ b/resources/public/tutorial/page10.html @@ -6,10 +6,10 @@

- Vectors: [1 2 3 4]
- Maps: {:foo "bar" 3 4}
- Lists: '(1 2 3 4)
- Sets: #{1 2 3 4}
+ Vectors: [1 2 3 4]
+ Maps: {:foo "bar" 3 4}
+ Lists: '(1 2 3 4)
+ Sets: #{1 2 3 4}
Vectors and lists are sequential and ordered collections. You'll see vectors used much more than lists. Maps are typical hash-maps. Unordered collections indexed by keys. The keys can be any object. Here, we've used a keyword, :foo as a key. Keywords make excellent keys. We also used a number. @@ -26,5 +26,5 @@

A prime example of this is map. We can use map, a higher order function (which is a function that takes functions as arguments or returns functions), to 'map' a function to every element of a sequence. - Let's use this to increment each number in a vector. Type (map inc [1 2 3 4]) to continue. + Let's use this to increment each number in a vector. Type (map inc [1 2 3 4]) to continue.

diff --git a/resources/public/tutorial/page2.html b/resources/public/tutorial/page2.html index 8a8809d..acd317f 100644 --- a/resources/public/tutorial/page2.html +++ b/resources/public/tutorial/page2.html @@ -3,4 +3,4 @@ Well, I've give you a hint: it's made up of lists. Let's do some arithmetic.

-(+ 3 3) +(+ 3 3) diff --git a/resources/public/tutorial/page3.html b/resources/public/tutorial/page3.html index 7c417cb..cd9d29b 100644 --- a/resources/public/tutorial/page3.html +++ b/resources/public/tutorial/page3.html @@ -9,4 +9,4 @@ Try a them out. Save / for last (don't worry, I have a reason!).

-

Once you're finished playing around, try (/ 10 3).

+

Once you're finished playing around, try (/ 10 3).

diff --git a/resources/public/tutorial/page4.html b/resources/public/tutorial/page4.html index 85baae8..0d06d50 100644 --- a/resources/public/tutorial/page4.html +++ b/resources/public/tutorial/page4.html @@ -1,8 +1,8 @@

I bet that caught you by surprise, didn't it? Don't fear! Clojure simply has a built in Rational type. - We can prove that we've got a rational by running (type (/ 10 3)). Rationals are more + We can prove that we've got a rational by running (type (/ 10 3)). Rationals are more concise and precise than floating point numbers. However, we can force Clojure to do floating point division by just making one of our numbers floating point.

-

Type (/ 10 3.0) to continue.

+

Type (/ 10 3.0) to continue.

diff --git a/resources/public/tutorial/page5.html b/resources/public/tutorial/page5.html index 18101c4..5f40ebb 100644 --- a/resources/public/tutorial/page5.html +++ b/resources/public/tutorial/page5.html @@ -8,4 +8,4 @@ take any number of arguments they wish.

-

Try it out. Type (+ 1 2 3 4 5 6) to continue.

+

Try it out. Type (+ 1 2 3 4 5 6) to continue.

diff --git a/resources/public/tutorial/page6.html b/resources/public/tutorial/page6.html index b006997..0b55ee3 100644 --- a/resources/public/tutorial/page6.html +++ b/resources/public/tutorial/page6.html @@ -4,4 +4,4 @@ You can define functions in Clojure with defn

-

Type (defn square [x] (* x x)) to continue!

+

Type (defn square [x] (* x x)) to continue!

diff --git a/resources/public/tutorial/page7.html b/resources/public/tutorial/page7.html index 151f964..cf0f574 100644 --- a/resources/public/tutorial/page7.html +++ b/resources/public/tutorial/page7.html @@ -9,4 +9,4 @@ (which is a vector, and not an actual list), then the body of the function.

-

But wait! We don't even know if our function works or not! Let's try it out. Type (square 10).

+

But wait! We don't even know if our function works or not! Let's try it out. Type (square 10).

diff --git a/resources/public/tutorial/page8.html b/resources/public/tutorial/page8.html index 50f919e..075857f 100644 --- a/resources/public/tutorial/page8.html +++ b/resources/public/tutorial/page8.html @@ -4,7 +4,7 @@ You know, Clojure is a functional programming language. As such, it has first-class and anonymous functions. Let's write our square function as an anonymous function.

-(fn [x] (* x x)) +(fn [x] (* x x))

If you run this in the REPL above (as you should), you'll note that some very weird and cryptic thing is printed. Functions are just normal values like a number, a string, or @@ -13,5 +13,5 @@

Our anonymous function isn't very useful if we don't call it. Let's do it. Type - ((fn [x] (* x x)) 10) + ((fn [x] (* x x)) 10)

diff --git a/resources/public/tutorial/page9.html b/resources/public/tutorial/page9.html index 4fe93e4..d6e81da 100644 --- a/resources/public/tutorial/page9.html +++ b/resources/public/tutorial/page9.html @@ -9,4 +9,4 @@ and fn to create named functions. We can create named functions without defn

-

Type (def square (fn [x] (* x x))) to continue.

+

Type (def square (fn [x] (* x x))) to continue.

diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 440268a..3447a44 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -77,7 +77,7 @@ [:p.bottom "Welcome to Try Clojure. Above, you have a Clojure REPL. You can type expressions and see " "their results right here in your browser. We also have a brief tutorial to give you a " - "taste of Clojure. Try it out by typing tutorial in the console!"]) + "taste of Clojure. Try it out by typing tutorial in the console!"]) (def fire-html (html From fda751c24d3ca45c9ed62923e5992ec8fa187b13 Mon Sep 17 00:00:00 2001 From: Alan Malloy Date: Sat, 16 Apr 2011 10:59:54 -0700 Subject: [PATCH 042/127] Add .cake to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5fcc507..9a5d0bb 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ pom.xml lib classes *~ +.cake From d89a89ed717a57e25c6beccf7b717a79503ab7d8 Mon Sep 17 00:00:00 2001 From: Raynes Date: Sat, 16 Apr 2011 18:31:20 -0500 Subject: [PATCH 043/127] Fix a typo and change some text. --- src/tryclojure/core.clj | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 3447a44..4af50e1 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -55,23 +55,18 @@ (def bottom-html (html [:p.bottom "This site is still under construction. I can't promise everything will work correctly." - " You can find the site's source and such on it's " (link-to "http://github.com/Raynes/tryclojure" "github") + " You can find the site's source and such on its " (link-to "http://github.com/Raynes/tryclojure" "github") " page."] [:p.bottom "Please note that this REPL is sandboxed, so you wont be able to do everything in it " "that you would in a local unsandboxed REPL. Keep in mind that this site is designed for " "beginners to try out Clojure and not necessarily as a general-purpose server-side REPL."] [:p.bottom - "TryClojure is written in Clojure and JavaScript, powered by " + "TryClojure is written in Clojure and JavaScript (JQuery), powered by " (link-to "http://github.com/Raynes/clojail" "clojail") " and Chris Done's " (link-to "http://github.com/chrisdone/jquery-console" "jquery-console")] - [:p.bottom - "Huge thanks to " (link-to "http://www.bestinclass.dk/" "Lau Jensen") - " for lots of help with everything ranging from Gimp, to straight up CSS and HTML design tips."] - [:p.bottom - "Also thanks to my buddy Heinz (Licenser) for all the help with this site. This site is hosted on" - " his server, with his domain name. He completely funds this project and it wouldn't be possible without him."])) + [:p.bottom "Design by " (link-to "http://apgwoz.com" "Andrew Gwozdziewyc")])) (def home-text [:p.bottom From 30f3f0166816a8cc69d2415a7fe45030c908fd05 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Sat, 29 Oct 2011 01:14:38 -0500 Subject: [PATCH 044/127] Update deps and move to noir. Running on noir, but sandboxing isn't done yet. def sandboxing has been totally changed. --- project.clj | 11 +- src/tryclojure/core.clj | 238 +++++++++++++++------------------------- 2 files changed, 93 insertions(+), 156 deletions(-) diff --git a/project.clj b/project.clj index 6675e6b..d6e0f5c 100644 --- a/project.clj +++ b/project.clj @@ -1,11 +1,6 @@ (defproject tryclojure "0.1.0-SNAPSHOT" :description "A simple web-based Clojure REPL for trying out Clojure without having to install it." - :dependencies [[org.clojure/clojure "1.2.0"] - [org.clojure/clojure-contrib "1.2.0"] - [net.cgrand/moustache "1.0.0-SNAPSHOT" :exclusions [org.clojure/clojure]] - [ring "0.3.3"] + :dependencies [[org.clojure/clojure "1.3.0"] + [noir "1.2.0"] [commons-lang/commons-lang "2.5"] - [clojail "0.4.0-SNAPSHOT"] - [hiccup "0.2.7"]] - :dev-dependencies [[swank-clojure "1.2.1"] - [ring/ring-devel "0.3.3"]]) \ No newline at end of file + [clojail "0.4.6-beta2"]]) \ No newline at end of file diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 4af50e1..9adbfb0 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -1,21 +1,82 @@ (ns tryclojure.core (:use ring.adapter.jetty - [hiccup core form-helpers page-helpers] + [hiccup form-helpers page-helpers] [ring.middleware reload stacktrace params file session] - net.cgrand.moustache + noir.core + [noir.response :only [json]] [clojure.stacktrace :only [root-cause]] - [clojail core testers] - [clojure.set :only [difference]]) - (:require [clojure.contrib.json :as json]) + [clojail.core :only [sandbox]] + [clojail.testers :only [secure-tester-without-def]]) + (:require [noir.server :as server]) (:import java.io.StringWriter java.util.concurrent.TimeoutException)) -(def sb-tester (difference secure-tester #{'def})) - -(def state-tester #{'def 'def* 'ensure 'ref-set 'alter 'commute 'swap! 'compare-and-set!}) +(defpartial links [] + (unordered-list + [(link-to "http://clojure.org" "The official Clojure website") + (link-to "http://dev.clojure.org/display/doc/Getting+Started" "Getting started with Clojure") + (link-to "http://groups.google.com/group/clojure" "Clojure mailing list") + (link-to "http://java.ociweb.com/mark/clojure/article.html" "A comprehensive Clojure tutorial") + (link-to "http://joyofclojure.com/" "The Joy of Clojure: a book by Michael Fogus and Chris Houser") + (link-to "http://disclojure.org" "Disclojure") + (link-to "http://planet.clojure.in" "Planet Clojure")])) + +(defpartial bottom-html [] + [:p.bottom + "This site is still under construction. I can't promise everything will work correctly." + " You can find the site's source and such on its " (link-to "http://github.com/Raynes/tryclojure" "github") + " page."] + [:p.bottom + "Please note that this REPL is sandboxed, so you wont be able to do everything in it " + "that you would in a local unsandboxed REPL. Keep in mind that this site is designed for " + "beginners to try out Clojure and not necessarily as a general-purpose server-side REPL."] + [:p.bottom + "TryClojure is written in Clojure and JavaScript (JQuery), powered by " + (link-to "https://github.com/flatland/clojail" "clojail") + " and Chris Done's " + (link-to "https://github.com/chrisdone/jquery-console" "jquery-console")] + [:p.bottom "Design by " (link-to "http://apgwoz.com" "Andrew Gwozdziewyc")]) + +(defpartial home-text [] + [:p.bottom + "Welcome to Try Clojure. Above, you have a Clojure REPL. You can type expressions and see " + "their results right here in your browser. We also have a brief tutorial to give you a " + "taste of Clojure. Try it out by typing tutorial in the console!"]) -(defn has-state? [form] - (check-form form state-tester)) +(defpartial fire-html [] + (:html4 doctype) + [:head + (include-css "/resources/public/css/tryclojure.css") + (include-js "/resources/public/javascript/jquery-1.4.2.min.js" + "/resources/public/javascript/jquery.console.js" + "/resources/public/javascript/tryclojure.js") + [:title "Try Clojure"]] + [:body + [:div#wrapper + [:div#content + [:div#header + [:h1 + [:span.logo-try "Try"] " " + [:span.logo-clojure "Clo" [:em "j"] "ure"]]] + [:div#container + [:div#console.console] + [:div#buttons + [:a#links.buttons "links"] + [:a#about.buttons.last "about"]] + [:div#changer (home-text)]] + [:div.footer + [:p.bottom "©2011 Anthony Grimes (Raynes) and contributors"] + [:p.bottom "Domain kindly paid for by " + (link-to "http://blog.licenser.net" "Heinz N. Gies")]]]]]) + +(defpage "/" [] + (fire-html)) + +(defpage "/about" [] + (bottom-html)) + +(defpage "/links" [] + (links)) (defn eval-form [form sbox] (with-open [out (java.io.StringWriter.)] @@ -27,147 +88,28 @@ (let [form (binding [*read-eval* false] (read-string expr))] (eval-form form sbox))) -(defn eval-request [{params :params {history :history} :session}] - (let [sbox (sandbox sb-tester :timeout 3000)] +(defn eval-request [expr] + (let [sbox (sandbox secure-tester-without-def :timeout 3000)] (try - ;; re-eval history forms - (doseq [form history] (eval-form form sbox)) - ;; eval request parameter - (eval-string (params "expr") sbox) - (catch OutOfMemoryError _ - {:error true :message "Out of memory error was thrown. Cleaning up all defs."}) + (eval-string expr sbox) (catch TimeoutException _ {:error true :message "Execution Timed Out!"}) (catch Exception e {:error true :message (.getMessage (root-cause e))})))) -(def links - (html (unordered-list - [(link-to "http://clojure.org" "The official Clojure website") - (link-to "http://dev.clojure.org/display/doc/Getting+Started" "Getting started with Clojure") - (link-to "http://groups.google.com/group/clojure" "Clojure mailing list") - (link-to "http://java.ociweb.com/mark/clojure/article.html" "A comprehensive Clojure tutorial") - (link-to "http://joyofclojure.com/" "The Joy of Clojure: a book by Michael Fogus and Chris Houser") - (link-to "http://www.pragprog.com/titles/shcloj/programming-clojure" "Programming Clojure, a book by Stuart Halloway") - (link-to "http://disclojure.org" "Disclojure") - (link-to "http://planet.clojure.in" "Planet Clojure")]))) - -(def bottom-html - (html [:p.bottom - "This site is still under construction. I can't promise everything will work correctly." - " You can find the site's source and such on its " (link-to "http://github.com/Raynes/tryclojure" "github") - " page."] - [:p.bottom - "Please note that this REPL is sandboxed, so you wont be able to do everything in it " - "that you would in a local unsandboxed REPL. Keep in mind that this site is designed for " - "beginners to try out Clojure and not necessarily as a general-purpose server-side REPL."] - [:p.bottom - "TryClojure is written in Clojure and JavaScript (JQuery), powered by " - (link-to "http://github.com/Raynes/clojail" "clojail") - " and Chris Done's " - (link-to "http://github.com/chrisdone/jquery-console" "jquery-console")] - [:p.bottom "Design by " (link-to "http://apgwoz.com" "Andrew Gwozdziewyc")])) - -(def home-text - [:p.bottom - "Welcome to Try Clojure. Above, you have a Clojure REPL. You can type expressions and see " - "their results right here in your browser. We also have a brief tutorial to give you a " - "taste of Clojure. Try it out by typing tutorial in the console!"]) - -(def fire-html - (html - (:html4 doctype) - [:head - (include-css "/resources/public/css/tryclojure.css") - (include-js "/resources/public/javascript/jquery-1.4.2.min.js" - "/resources/public/javascript/jquery.console.js" - "/resources/public/javascript/tryclojure.js") - [:title "Try Clojure"]] - [:body - [:div#wrapper - [:div#content - [:div#header - [:h1 - [:span.logo-try "Try"] " " - [:span.logo-clojure "Clo" [:em "j"] "ure"]]] - [:div#container - [:div#console.console] - [:div#buttons - [:a#links.buttons "links"] - [:a#about.buttons.last "about"]] - [:div#changer home-text]] - [:div.footer - [:p.bottom "©2011 Anthony Grimes (Raynes) and contributors"] - [:p.bottom "Domain kindly paid for by " - (link-to "http://blog.licenser.net" "Heinz N. Gies")]]]]])) - -(defn handler [{session :session}] - {:status 200 - :headers {"Content-Type" "text/html"} - :session session - :body fire-html}) - -(defn about-handler [{session :session}] - {:status 200 - :headers {"Content-Type" "text/html"} - :session session - :body bottom-html}) - -(defn link-handler [{session :session}] - {:status 200 - :headers {"Content-Type" "text/html"} - :session session - :body links}) - -(def ^{:private true} - eval-response-defaults - {:status 200 - :headers {"Content-Type" "application/json"}}) - -(defn eval-handler [request] - (let [{:keys [expr result error message] :as res} (eval-request request) - history (get-in request [:session :history] [])] - (if error - (merge eval-response-defaults - {:session (:session request) - :body (json/json-str res)}) - (let [[out res] result] - (merge eval-response-defaults - {:session {:history (conj history expr)} - :body (json/json-str {:expr (pr-str expr) - :result (str out (pr-str res))})}))))) - -(defn tutorial-handler [{{n "n"} :params session :session :as res}] - {:status 200 - :headers {"Content-Type" "application/html"} - :session session - :body (slurp (str "resources/public/tutorial/page" n ".html"))}) - -(defn- max-history [max history] - (if (> (count history) max) - (drop 1 history) - history)) - -(defn wrap-post-history [handler] - (fn [request] - (let [response (handler request)] - (->> (get-in response [:session :history] []) - (max-history 5) - (filter has-state?) - vec - (assoc-in response [:session :history]))))) - -(def clojureroutes - (app - (wrap-session) - (wrap-post-history) - (wrap-file (System/getProperty "user.dir")) - (wrap-params) - (wrap-stacktrace) - ["links"] link-handler - ["about"] about-handler - ["eval.json"] eval-handler - ["tutorial"] tutorial-handler - [""] handler)) - -(defn tryclj [] (run-jetty #'clojureroutes {:port 8801})) +(defpage "/eval.json" {:keys [expr]} + (prn "expr") + (json + (let [{:keys [expr result error message] :as res} (eval-request expr)] + (if error + res + (let [[out res] result] + {:expr (pr-str expr) + :result (str out (pr-str res))}))))) + +(server/add-middleware wrap-session) +(server/add-middleware wrap-file (System/getProperty "user.dir")) +(server/add-middleware wrap-params) +(server/add-middleware wrap-stacktrace) + +(defn tryclj [] (server/start 8801)) \ No newline at end of file From 037d842993f190f3dd10a1598530e3c9af93a43e Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Sat, 29 Oct 2011 03:34:02 -0500 Subject: [PATCH 045/127] Sandbox defs and users properly. Update html. --- src/tryclojure/core.clj | 46 +++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 9adbfb0..a6c37f4 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -7,7 +7,8 @@ [clojure.stacktrace :only [root-cause]] [clojail.core :only [sandbox]] [clojail.testers :only [secure-tester-without-def]]) - (:require [noir.server :as server]) + (:require [noir.server :as server] + [noir.session :as session]) (:import java.io.StringWriter java.util.concurrent.TimeoutException)) @@ -23,13 +24,16 @@ (defpartial bottom-html [] [:p.bottom - "This site is still under construction. I can't promise everything will work correctly." - " You can find the site's source and such on its " (link-to "http://github.com/Raynes/tryclojure" "github") + "You can find the site's source and such on its " + (link-to "http://github.com/Raynes/tryclojure" "github") " page."] [:p.bottom "Please note that this REPL is sandboxed, so you wont be able to do everything in it " "that you would in a local unsandboxed REPL. Keep in mind that this site is designed for " "beginners to try out Clojure and not necessarily as a general-purpose server-side REPL."] + [:p.bottom + "One quirk you might run into is that things you bind with def can sometimes disappear. " + "The sandbox wipes defs if you def too many things, so don't be surprised."] [:p.bottom "TryClojure is written in Clojure and JavaScript (JQuery), powered by " (link-to "https://github.com/flatland/clojail" "clojail") @@ -66,8 +70,10 @@ [:div#changer (home-text)]] [:div.footer [:p.bottom "©2011 Anthony Grimes (Raynes) and contributors"] - [:p.bottom "Domain kindly paid for by " - (link-to "http://blog.licenser.net" "Heinz N. Gies")]]]]]) + [:p.bottom + "Built with " + (link-to "http://webnoir.org" "Noir") + "."]]]]]) (defpage "/" [] (fire-html)) @@ -88,17 +94,31 @@ (let [form (binding [*read-eval* false] (read-string expr))] (eval-form form sbox))) +(def sandboxes (atom {:counter 0})) + +(defn add-user [old] + (let [count (inc (:counter old))] + (assoc old + :counter count + count (sandbox secure-tester-without-def :timeout 2000)))) + (defn eval-request [expr] - (let [sbox (sandbox secure-tester-without-def :timeout 3000)] - (try - (eval-string expr sbox) - (catch TimeoutException _ - {:error true :message "Execution Timed Out!"}) - (catch Exception e - {:error true :message (.getMessage (root-cause e))})))) + (try + (eval-string + expr + (do + (if-let [sb (@sandboxes (session/get :sb))] + sb + (let [sbs (swap! sandboxes add-user) + count (:counter sbs)] + (session/put! :sb count) + (sbs count))))) + (catch TimeoutException _ + {:error true :message "Execution Timed Out!"}) + (catch Exception e + {:error true :message (.getMessage (root-cause e))}))) (defpage "/eval.json" {:keys [expr]} - (prn "expr") (json (let [{:keys [expr result error message] :as res} (eval-request expr)] (if error From cf951c876087efbdb8ed2923cdfce415f9afc705 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Sat, 29 Oct 2011 13:27:23 -0500 Subject: [PATCH 046/127] Remove sandboxes every so often. --- src/tryclojure/core.clj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index a6c37f4..9ff56fd 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -112,6 +112,8 @@ (let [sbs (swap! sandboxes add-user) count (:counter sbs)] (session/put! :sb count) + (future (Thread/sleep 900000) + (swap! sandboxes dissoc count)) (sbs count))))) (catch TimeoutException _ {:error true :message "Execution Timed Out!"}) From 391c66bff9bdfc980d56d4a2591c9bb31a956931 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Sat, 29 Oct 2011 13:28:34 -0500 Subject: [PATCH 047/127] Docs. --- src/tryclojure/core.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 9ff56fd..db7c76d 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -33,7 +33,9 @@ "beginners to try out Clojure and not necessarily as a general-purpose server-side REPL."] [:p.bottom "One quirk you might run into is that things you bind with def can sometimes disappear. " - "The sandbox wipes defs if you def too many things, so don't be surprised."] + "The sandbox wipes defs if you def too many things, so don't be surprised. Furthermore, " + "The sandbox will automatically be wiped after 15 minutes and if you evaluate more after that," + "It'll be in an entirely new namespace/sandbox."] [:p.bottom "TryClojure is written in Clojure and JavaScript (JQuery), powered by " (link-to "https://github.com/flatland/clojail" "clojail") From c413a86c4dc34b3c25b8326c254337e7669bfd1b Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Sat, 29 Oct 2011 13:42:42 -0500 Subject: [PATCH 048/127] This middleware is handled by Noir. --- src/tryclojure/core.clj | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index db7c76d..fca3c21 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -131,9 +131,6 @@ {:expr (pr-str expr) :result (str out (pr-str res))}))))) -(server/add-middleware wrap-session) (server/add-middleware wrap-file (System/getProperty "user.dir")) -(server/add-middleware wrap-params) -(server/add-middleware wrap-stacktrace) (defn tryclj [] (server/start 8801)) \ No newline at end of file From bb30233481fa7b56c2c038ed265f6e45218dd683 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Sat, 29 Oct 2011 14:21:32 -0500 Subject: [PATCH 049/127] Fix tutorial. --- src/tryclojure/core.clj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index fca3c21..2b1970e 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -86,6 +86,9 @@ (defpage "/links" [] (links)) +(defpage [:post "/tutorial"] {n :n} + (slurp (str "resources/public/tutorial/page" n ".html"))) + (defn eval-form [form sbox] (with-open [out (java.io.StringWriter.)] (let [result (sbox form {#'*out* out})] From a4eaed851d3a312be1ca726836abb79267a29487 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Sat, 29 Oct 2011 17:28:37 -0500 Subject: [PATCH 050/127] Prevent access to tryclojure.core. --- src/tryclojure/core.clj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 2b1970e..90d12f8 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -101,11 +101,15 @@ (def sandboxes (atom {:counter 0})) +(def try-clojure-tester + (into secure-tester-without-def + #{'tryclojure.core})) + (defn add-user [old] (let [count (inc (:counter old))] (assoc old :counter count - count (sandbox secure-tester-without-def :timeout 2000)))) + count (sandbox try-clojure-tester :timeout 2000)))) (defn eval-request [expr] (try From 0f6ee9794e6a3a48f8ec7406ee2a1c37c0b94e7a Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Sun, 30 Oct 2011 00:30:46 -0500 Subject: [PATCH 051/127] Heroku stuff. --- Procfile | 1 + project.clj | 3 ++- src/tryclojure/core.clj | 4 +++- 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 Procfile diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..09a6c19 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: lein run diff --git a/project.clj b/project.clj index d6e0f5c..a07a12f 100644 --- a/project.clj +++ b/project.clj @@ -3,4 +3,5 @@ :dependencies [[org.clojure/clojure "1.3.0"] [noir "1.2.0"] [commons-lang/commons-lang "2.5"] - [clojail "0.4.6-beta2"]]) \ No newline at end of file + [clojail "0.4.6-beta2"]] + :main tryclojure.core) \ No newline at end of file diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 90d12f8..a1a93cf 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -140,4 +140,6 @@ (server/add-middleware wrap-file (System/getProperty "user.dir")) -(defn tryclj [] (server/start 8801)) \ No newline at end of file +(defn tryclj [] (server/start 8801)) + +(defn -main [& args] (tryclj)) \ No newline at end of file From f71144d0f994b8ff9bbd3a9be9f73145b5241d58 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Sun, 30 Oct 2011 00:47:49 -0500 Subject: [PATCH 052/127] Set port number for heroku. --- src/tryclojure/core.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index a1a93cf..437f7a3 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -140,6 +140,6 @@ (server/add-middleware wrap-file (System/getProperty "user.dir")) -(defn tryclj [] (server/start 8801)) +(defn tryclj [] (server/start 47761)) (defn -main [& args] (tryclj)) \ No newline at end of file From 3e38c2bdffb62070dbbdbf00566ce07e862bd205 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Sun, 30 Oct 2011 00:50:34 -0500 Subject: [PATCH 053/127] Set port *properly* --- src/tryclojure/core.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 437f7a3..cc837bb 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -140,6 +140,6 @@ (server/add-middleware wrap-file (System/getProperty "user.dir")) -(defn tryclj [] (server/start 47761)) +(defn tryclj [] (server/start (Integer. (System/getenv "PORT")))) (defn -main [& args] (tryclj)) \ No newline at end of file From 4b103ef07241250155756c58bbc8b2c2053cd677 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Sun, 30 Oct 2011 00:55:08 -0500 Subject: [PATCH 054/127] Set policy. --- project.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/project.clj b/project.clj index a07a12f..adbb017 100644 --- a/project.clj +++ b/project.clj @@ -4,4 +4,5 @@ [noir "1.2.0"] [commons-lang/commons-lang "2.5"] [clojail "0.4.6-beta2"]] + :jvm-opts ["-Djava.security.policy=example.policy"] :main tryclojure.core) \ No newline at end of file From 2ad398360ae4df54c16d2af9e1ca2a5b1b043f65 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Mon, 31 Oct 2011 00:52:51 -0500 Subject: [PATCH 055/127] Update clojail. --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index a07a12f..77e5e14 100644 --- a/project.clj +++ b/project.clj @@ -3,5 +3,5 @@ :dependencies [[org.clojure/clojure "1.3.0"] [noir "1.2.0"] [commons-lang/commons-lang "2.5"] - [clojail "0.4.6-beta2"]] + [clojail "0.4.6-beta4"]] :main tryclojure.core) \ No newline at end of file From ec04b39acd9fce81035dbdffbdeda39414925d46 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Mon, 31 Oct 2011 02:11:48 -0500 Subject: [PATCH 056/127] Runs everywhere, even on Heroku. --- project.clj | 2 ++ src/tryclojure/core.clj | 11 +++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/project.clj b/project.clj index a1e7341..595314e 100644 --- a/project.clj +++ b/project.clj @@ -4,5 +4,7 @@ [noir "1.2.0"] [commons-lang/commons-lang "2.5"] [clojail "0.4.6-beta4"]] + ;; For lein and Heroku compatibility. If you're using cake, add + ;; this line to .cake/config: jvm.opts = -Djava.security.policy=example.policy :jvm-opts ["-Djava.security.policy=example.policy"] :main tryclojure.core) \ No newline at end of file diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index cc837bb..0093ab8 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -140,6 +140,13 @@ (server/add-middleware wrap-file (System/getProperty "user.dir")) -(defn tryclj [] (server/start (Integer. (System/getenv "PORT")))) +(defn to-port [s] + (when-let [port s] (Long. port))) -(defn -main [& args] (tryclj)) \ No newline at end of file +(defn tryclj [port] + (server/start + (or (to-port port) + (to-port (System/getenv "PORT")) ;; For deploying to Heroku + 8801))) + +(defn -main [& args] (tryclj (first args))) \ No newline at end of file From 4ecb758044a24fe9f9f09941a6be380879cb07d8 Mon Sep 17 00:00:00 2001 From: Max Penet Date: Mon, 31 Oct 2011 15:51:38 +0100 Subject: [PATCH 057/127] fix tryclj arity for when no port is supplied + wrap page content in an HTML tag --- src/tryclojure/core.clj | 64 ++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 0093ab8..399997e 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -13,7 +13,7 @@ java.util.concurrent.TimeoutException)) (defpartial links [] - (unordered-list + (unordered-list [(link-to "http://clojure.org" "The official Clojure website") (link-to "http://dev.clojure.org/display/doc/Getting+Started" "Getting started with Clojure") (link-to "http://groups.google.com/group/clojure" "Clojure mailing list") @@ -37,45 +37,45 @@ "The sandbox will automatically be wiped after 15 minutes and if you evaluate more after that," "It'll be in an entirely new namespace/sandbox."] [:p.bottom - "TryClojure is written in Clojure and JavaScript (JQuery), powered by " + "TryClojure is written in Clojure and JavaScript (JQuery), powered by " (link-to "https://github.com/flatland/clojail" "clojail") " and Chris Done's " (link-to "https://github.com/chrisdone/jquery-console" "jquery-console")] [:p.bottom "Design by " (link-to "http://apgwoz.com" "Andrew Gwozdziewyc")]) (defpartial home-text [] - [:p.bottom + [:p.bottom "Welcome to Try Clojure. Above, you have a Clojure REPL. You can type expressions and see " "their results right here in your browser. We also have a brief tutorial to give you a " "taste of Clojure. Try it out by typing tutorial in the console!"]) -(defpartial fire-html [] - (:html4 doctype) - [:head - (include-css "/resources/public/css/tryclojure.css") - (include-js "/resources/public/javascript/jquery-1.4.2.min.js" - "/resources/public/javascript/jquery.console.js" - "/resources/public/javascript/tryclojure.js") - [:title "Try Clojure"]] - [:body - [:div#wrapper - [:div#content - [:div#header - [:h1 - [:span.logo-try "Try"] " " - [:span.logo-clojure "Clo" [:em "j"] "ure"]]] - [:div#container - [:div#console.console] - [:div#buttons - [:a#links.buttons "links"] - [:a#about.buttons.last "about"]] - [:div#changer (home-text)]] - [:div.footer - [:p.bottom "©2011 Anthony Grimes (Raynes) and contributors"] - [:p.bottom - "Built with " - (link-to "http://webnoir.org" "Noir") - "."]]]]]) +(defpartial fire-html [] + (html4 + [:head + (include-css "/resources/public/css/tryclojure.css") + (include-js "/resources/public/javascript/jquery-1.4.2.min.js" + "/resources/public/javascript/jquery.console.js" + "/resources/public/javascript/tryclojure.js") + [:title "Try Clojure"]] + [:body + [:div#wrapper + [:div#content + [:div#header + [:h1 + [:span.logo-try "Try"] " " + [:span.logo-clojure "Clo" [:em "j"] "ure"]]] + [:div#container + [:div#console.console] + [:div#buttons + [:a#links.buttons "links"] + [:a#about.buttons.last "about"]] + [:div#changer (home-text)]] + [:div.footer + [:p.bottom "©2011 Anthony Grimes (Raynes) and contributors"] + [:p.bottom + "Built with " + (link-to "http://webnoir.org" "Noir") + "."]]]]])) (defpage "/" [] (fire-html)) @@ -143,10 +143,10 @@ (defn to-port [s] (when-let [port s] (Long. port))) -(defn tryclj [port] +(defn tryclj [& [port]] (server/start (or (to-port port) - (to-port (System/getenv "PORT")) ;; For deploying to Heroku + (to-port (System/getenv "PORT")) ;; For deploying to Heroku 8801))) (defn -main [& args] (tryclj (first args))) \ No newline at end of file From 9d3aaa1a97ed83131fb1dd23b7e0c5034691d744 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Mon, 31 Oct 2011 12:07:38 -0500 Subject: [PATCH 058/127] Fix Andrew's name and make the copyright notice a little less pretentious. --- src/tryclojure/core.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 399997e..838bd38 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -41,7 +41,7 @@ (link-to "https://github.com/flatland/clojail" "clojail") " and Chris Done's " (link-to "https://github.com/chrisdone/jquery-console" "jquery-console")] - [:p.bottom "Design by " (link-to "http://apgwoz.com" "Andrew Gwozdziewyc")]) + [:p.bottom "Design by " (link-to "http://apgwoz.com" "Andrew Gwozdziewycz")]) (defpartial home-text [] [:p.bottom @@ -71,7 +71,7 @@ [:a#about.buttons.last "about"]] [:div#changer (home-text)]] [:div.footer - [:p.bottom "©2011 Anthony Grimes (Raynes) and contributors"] + [:p.bottom "©2011 Anthony Grime and numerous contributors"] [:p.bottom "Built with " (link-to "http://webnoir.org" "Noir") From b9ef860575ecbb61d291f4d647a5dbdc318eb70d Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Mon, 31 Oct 2011 12:10:44 -0500 Subject: [PATCH 059/127] Hah, now I'm even misspelling my own name. --- src/tryclojure/core.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 838bd38..ae99eec 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -71,7 +71,7 @@ [:a#about.buttons.last "about"]] [:div#changer (home-text)]] [:div.footer - [:p.bottom "©2011 Anthony Grime and numerous contributors"] + [:p.bottom "©2011 Anthony Grimes and numerous contributors"] [:p.bottom "Built with " (link-to "http://webnoir.org" "Noir") From 5ed40e60b85ca8e90cf40575e523682e34f5e042 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Mon, 31 Oct 2011 14:04:21 -0500 Subject: [PATCH 060/127] Bump clojail beta. --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index 595314e..bf27ca3 100644 --- a/project.clj +++ b/project.clj @@ -3,7 +3,7 @@ :dependencies [[org.clojure/clojure "1.3.0"] [noir "1.2.0"] [commons-lang/commons-lang "2.5"] - [clojail "0.4.6-beta4"]] + [clojail "0.4.6-beta5"]] ;; For lein and Heroku compatibility. If you're using cake, add ;; this line to .cake/config: jvm.opts = -Djava.security.policy=example.policy :jvm-opts ["-Djava.security.policy=example.policy"] From 1f42c0d10eae529fe03304bf746b7868ce004515 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Mon, 31 Oct 2011 15:24:21 -0500 Subject: [PATCH 061/127] Change to-be-written notice. --- resources/public/tutorial/page1.html | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/resources/public/tutorial/page1.html b/resources/public/tutorial/page1.html index 5029ec3..7ffd325 100644 --- a/resources/public/tutorial/page1.html +++ b/resources/public/tutorial/page1.html @@ -1,9 +1,5 @@

- I am in the process of rewriting this tutorial and, of course, adding content. The old tutorial - was thrown together rather quickly and wasn't touched afterwards. I ask for - patience while I get everything working smoothly. I have a lot going on in my life including - a book and an upcoming new job. Furthermore, I don't even really know Javascript, so anything JavaScript - related moves extremely slowly. Working on that. ;) + This tutorial isn't entirely complete yet. More fun content is to be written. Stay tuned.

From e19e3f0935c9057b0fccecaf2624aad4ccca151a Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Mon, 31 Oct 2011 18:58:49 -0500 Subject: [PATCH 062/127] Added a 'home' button, changed some names, and added a github link to the home text. --- resources/public/javascript/tryclojure.js | 4 +-- src/tryclojure/core.clj | 32 +++++++++++++++-------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/resources/public/javascript/tryclojure.js b/resources/public/javascript/tryclojure.js index 8259592..f0d9c26 100644 --- a/resources/public/javascript/tryclojure.js +++ b/resources/public/javascript/tryclojure.js @@ -177,9 +177,7 @@ $(document).ready(function() { $("#about").click(setupLink("about")); $("#links").click(setupLink("links")); - $("#home").click(function(e) { - showPage(0); - }); + $("#home").click(setupLink("home")); changerUpdated(); }); diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index ae99eec..dbe3d6a 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -22,11 +22,9 @@ (link-to "http://disclojure.org" "Disclojure") (link-to "http://planet.clojure.in" "Planet Clojure")])) -(defpartial bottom-html [] - [:p.bottom - "You can find the site's source and such on its " - (link-to "http://github.com/Raynes/tryclojure" "github") - " page."] +(def github-link (link-to "http://github.com/Raynes/tryclojure" "github")) + +(defpartial about-html [] [:p.bottom "Please note that this REPL is sandboxed, so you wont be able to do everything in it " "that you would in a local unsandboxed REPL. Keep in mind that this site is designed for " @@ -36,6 +34,10 @@ "The sandbox wipes defs if you def too many things, so don't be surprised. Furthermore, " "The sandbox will automatically be wiped after 15 minutes and if you evaluate more after that," "It'll be in an entirely new namespace/sandbox."] + [:p.bottom + "You can find the site's source and such on its " + github-link + " page."] [:p.bottom "TryClojure is written in Clojure and JavaScript (JQuery), powered by " (link-to "https://github.com/flatland/clojail" "clojail") @@ -43,13 +45,17 @@ (link-to "https://github.com/chrisdone/jquery-console" "jquery-console")] [:p.bottom "Design by " (link-to "http://apgwoz.com" "Andrew Gwozdziewycz")]) -(defpartial home-text [] +(defpartial home-html [] [:p.bottom "Welcome to Try Clojure. Above, you have a Clojure REPL. You can type expressions and see " "their results right here in your browser. We also have a brief tutorial to give you a " - "taste of Clojure. Try it out by typing tutorial in the console!"]) + "taste of Clojure. Try it out by typing tutorial in the console!"] + [:p.bottom + "Check out the site's source on " + (link-to "http://github.com/Raynes/tryclojure" "github") + "!"]) -(defpartial fire-html [] +(defpartial root-html [] (html4 [:head (include-css "/resources/public/css/tryclojure.css") @@ -67,9 +73,10 @@ [:div#container [:div#console.console] [:div#buttons + [:a#home.buttons "home"] [:a#links.buttons "links"] [:a#about.buttons.last "about"]] - [:div#changer (home-text)]] + [:div#changer (home-html)]] [:div.footer [:p.bottom "©2011 Anthony Grimes and numerous contributors"] [:p.bottom @@ -78,10 +85,13 @@ "."]]]]])) (defpage "/" [] - (fire-html)) + (root-html)) + +(defpage "/home" [] + (home-html)) (defpage "/about" [] - (bottom-html)) + (about-html)) (defpage "/links" [] (links)) From 88629c79c16c0349c49801bf598067874288658c Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Mon, 31 Oct 2011 21:04:46 -0500 Subject: [PATCH 063/127] Update to newest clojail. --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index bf27ca3..370d3fa 100644 --- a/project.clj +++ b/project.clj @@ -3,7 +3,7 @@ :dependencies [[org.clojure/clojure "1.3.0"] [noir "1.2.0"] [commons-lang/commons-lang "2.5"] - [clojail "0.4.6-beta5"]] + [clojail "0.4.6-beta6"]] ;; For lein and Heroku compatibility. If you're using cake, add ;; this line to .cake/config: jvm.opts = -Djava.security.policy=example.policy :jvm-opts ["-Djava.security.policy=example.policy"] From 4953942adf5690cfe117aa689839ec267f95a53c Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Mon, 31 Oct 2011 21:10:12 -0500 Subject: [PATCH 064/127] Improve error messages. --- src/tryclojure/core.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index dbe3d6a..8eeabae 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -137,7 +137,7 @@ (catch TimeoutException _ {:error true :message "Execution Timed Out!"}) (catch Exception e - {:error true :message (.getMessage (root-cause e))}))) + {:error true :message (str (root-cause e))}))) (defpage "/eval.json" {:keys [expr]} (json From 0c7e3b979b11d6dc5e86f529dc1df50add32a9a6 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Mon, 31 Oct 2011 22:29:21 -0500 Subject: [PATCH 065/127] Typo. --- resources/public/tutorial/page3.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/public/tutorial/page3.html b/resources/public/tutorial/page3.html index cd9d29b..387194c 100644 --- a/resources/public/tutorial/page3.html +++ b/resources/public/tutorial/page3.html @@ -6,7 +6,7 @@ /

- Try a them out. Save / for last (don't worry, I have a reason!). + Try them out. Save / for last (don't worry, I have a reason!).

Once you're finished playing around, try (/ 10 3).

From 18640b725e8d1d81cebb11508df977967f6cf447 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Tue, 1 Nov 2011 10:25:12 -0500 Subject: [PATCH 066/127] Uncommafy some stuff. --- resources/public/javascript/tryclojure.js | 2 +- src/tryclojure/core.clj | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/public/javascript/tryclojure.js b/resources/public/javascript/tryclojure.js index f0d9c26..dec3762 100644 --- a/resources/public/javascript/tryclojure.js +++ b/resources/public/javascript/tryclojure.js @@ -166,7 +166,7 @@ var controller; $(document).ready(function() { controller = $("#console").console({ - welcomeMessage:'Enter some Clojure code, and it will be evaluated.', + welcomeMessage:'Enter some Clojure code to be evaluated.', promptLabel: 'Clojure> ', commandValidate: onValidate, commandHandle: onHandle, diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 8eeabae..93bbccb 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -47,9 +47,9 @@ (defpartial home-html [] [:p.bottom - "Welcome to Try Clojure. Above, you have a Clojure REPL. You can type expressions and see " - "their results right here in your browser. We also have a brief tutorial to give you a " - "taste of Clojure. Try it out by typing tutorial in the console!"] + "Welcome to Try Clojure. See that little box up there? That's a Clojure repl. You can type " + "expressions and see their results right here in your browser. We also have a brief tutorial to " + "give you a taste of Clojure. Try it out by typing " [:code.expr "tutorial"] " in the console!"] [:p.bottom "Check out the site's source on " (link-to "http://github.com/Raynes/tryclojure" "github") From ef3b421b84047edd7f659e7653f20aafa1007c89 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Tue, 1 Nov 2011 10:30:54 -0500 Subject: [PATCH 067/127] Updated jquery-console --- resources/public/javascript/jquery.console.js | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/resources/public/javascript/jquery.console.js b/resources/public/javascript/jquery.console.js index 769bc51..a21ee66 100644 --- a/resources/public/javascript/jquery.console.js +++ b/resources/public/javascript/jquery.console.js @@ -19,14 +19,14 @@ // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // TESTED ON @@ -187,7 +187,7 @@ n.remove(); }); },4000); - else if (style=='prompt') { + else if (style=='prompt') { var a = $('
'); n.append(a); focused = false; @@ -236,7 +236,7 @@ inner.removeClass('jquery-console-focus'); inner.addClass('jquery-console-nofocus'); }); - + //////////////////////////////////////////////////////////////////////// // Bind to the paste event of the input box so we know when we // get pasted data @@ -250,7 +250,7 @@ typer.val(""); }, 0); }); - + //////////////////////////////////////////////////////////////////////// // Handle key hit before translation // For picking up control characters like up/left/down/right @@ -280,7 +280,7 @@ } } }); - + //////////////////////////////////////////////////////////////////////// // Handle key press typer.keypress(function(e){ @@ -364,7 +364,7 @@ updatePromptDisplay(); } }; - + function forwardDelete() { if (deleteCharAtPos()) updatePromptDisplay(); @@ -509,7 +509,7 @@ restoreText = promptText; updatePromptDisplay(); }; - + //////////////////////////////////////////////////////////////////////// // Move to another column relative to this one // Negative means go back, positive means go forward. @@ -609,7 +609,7 @@ var before = line.substring(0, column); var current = line.substring(column,column+1); if (current){ - current = + current = '' + htmlEncode(current) + ''; @@ -620,7 +620,7 @@ prompt.html(html); scrollToBottom(); }; - + // Simple HTML encoding // Simply replace '<', '>' and '&' // TODO: Use jQuery's .html() trick, or grab a proper, fast From 8a3abf730d21ab3a8be5d6dcbcabe76293c83754 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Tue, 1 Nov 2011 10:43:54 -0500 Subject: [PATCH 068/127] Added jsonp support. --- src/tryclojure/core.clj | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 93bbccb..ce87c8d 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -139,14 +139,17 @@ (catch Exception e {:error true :message (str (root-cause e))}))) -(defpage "/eval.json" {:keys [expr]} - (json - (let [{:keys [expr result error message] :as res} (eval-request expr)] - (if error - res - (let [[out res] result] - {:expr (pr-str expr) - :result (str out (pr-str res))}))))) +(defpage "/eval.json" {:keys [expr jsonp]} + (update-in + (json + (let [{:keys [expr result error message] :as res} (eval-request expr)] + (if error + res + (let [[out res] result] + {:expr (pr-str expr) + :result (str out (pr-str res))})))) + [:body] + #(if jsonp (str jsonp "(" % ")") %))) (server/add-middleware wrap-file (System/getProperty "user.dir")) From c33d18e37f4c9541e32cca0cc864f9c1906b5ef7 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Tue, 1 Nov 2011 18:02:47 -0500 Subject: [PATCH 069/127] Remove namespaces after 10 minutes. --- src/tryclojure/core.clj | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index ce87c8d..4c731f4 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -119,7 +119,9 @@ (let [count (inc (:counter old))] (assoc old :counter count - count (sandbox try-clojure-tester :timeout 2000)))) + count (sandbox try-clojure-tester + :timeout 2000 + :namespace (symbol (str "sandbox" (rand-int Integer/MAX_VALUE))))))) (defn eval-request [expr] (try @@ -128,12 +130,13 @@ (do (if-let [sb (@sandboxes (session/get :sb))] sb - (let [sbs (swap! sandboxes add-user) - count (:counter sbs)] + (let [{count :counter} (swap! sandboxes add-user)] (session/put! :sb count) - (future (Thread/sleep 900000) - (swap! sandboxes dissoc count)) - (sbs count))))) + (future + (Thread/sleep 600000) + (-> ((get @sandboxes count) '*ns*) str symbol remove-ns) + (swap! sandboxes dissoc count)) + (get @sandboxes count))))) (catch TimeoutException _ {:error true :message "Execution Timed Out!"}) (catch Exception e From acc662a117eed2ee7a2b2ad81a3edef9aaea2d36 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Tue, 1 Nov 2011 21:41:51 -0500 Subject: [PATCH 070/127] Update to the latest clojail release. --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index 370d3fa..b7e0ab4 100644 --- a/project.clj +++ b/project.clj @@ -3,7 +3,7 @@ :dependencies [[org.clojure/clojure "1.3.0"] [noir "1.2.0"] [commons-lang/commons-lang "2.5"] - [clojail "0.4.6-beta6"]] + [clojail "0.5.0-beta2"]] ;; For lein and Heroku compatibility. If you're using cake, add ;; this line to .cake/config: jvm.opts = -Djava.security.policy=example.policy :jvm-opts ["-Djava.security.policy=example.policy"] From 7c535a78b1589dda9f44857805be7fd6af3b7d83 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Wed, 2 Nov 2011 17:10:52 -0500 Subject: [PATCH 071/127] Update clojail. --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index b7e0ab4..a549e81 100644 --- a/project.clj +++ b/project.clj @@ -3,7 +3,7 @@ :dependencies [[org.clojure/clojure "1.3.0"] [noir "1.2.0"] [commons-lang/commons-lang "2.5"] - [clojail "0.5.0-beta2"]] + [clojail "0.5.0-beta3"]] ;; For lein and Heroku compatibility. If you're using cake, add ;; this line to .cake/config: jvm.opts = -Djava.security.policy=example.policy :jvm-opts ["-Djava.security.policy=example.policy"] From 4bcf274e9c675e6f99b413d8f146f3012f71e8ab Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Wed, 2 Nov 2011 23:10:52 -0500 Subject: [PATCH 072/127] Now supports *1, *2, and *3. --- src/tryclojure/core.clj | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 4c731f4..0195df7 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -102,6 +102,7 @@ (defn eval-form [form sbox] (with-open [out (java.io.StringWriter.)] (let [result (sbox form {#'*out* out})] + (sbox (list 'rebind-last-results result)) {:expr form :result [out result]}))) @@ -121,7 +122,12 @@ :counter count count (sandbox try-clojure-tester :timeout 2000 - :namespace (symbol (str "sandbox" (rand-int Integer/MAX_VALUE))))))) + :namespace (symbol (str "sandbox" (rand-int Integer/MAX_VALUE))) + :init '(defn rebind-last-results [last] + (let [core (the-ns 'clojure.core)] + (when (find-var 'clojure.core/*2) (intern core '*3 *2)) + (when (find-var 'clojure.core/*1) (intern core '*2 *1)) + (intern core '*1 last))))))) (defn eval-request [expr] (try From 61c82c460807f09d50590568454ee324fc427874 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Wed, 2 Nov 2011 23:47:32 -0500 Subject: [PATCH 073/127] This was pretty stupid. --- src/tryclojure/core.clj | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 0195df7..4c731f4 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -102,7 +102,6 @@ (defn eval-form [form sbox] (with-open [out (java.io.StringWriter.)] (let [result (sbox form {#'*out* out})] - (sbox (list 'rebind-last-results result)) {:expr form :result [out result]}))) @@ -122,12 +121,7 @@ :counter count count (sandbox try-clojure-tester :timeout 2000 - :namespace (symbol (str "sandbox" (rand-int Integer/MAX_VALUE))) - :init '(defn rebind-last-results [last] - (let [core (the-ns 'clojure.core)] - (when (find-var 'clojure.core/*2) (intern core '*3 *2)) - (when (find-var 'clojure.core/*1) (intern core '*2 *1)) - (intern core '*1 last))))))) + :namespace (symbol (str "sandbox" (rand-int Integer/MAX_VALUE))))))) (defn eval-request [expr] (try From 7cc2348989dec03d797273ab0b7ade0752cd6aec Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Fri, 4 Nov 2011 01:55:15 -0500 Subject: [PATCH 074/127] Set a max heap size. --- project.clj | 2 +- src/tryclojure/core.clj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/project.clj b/project.clj index a549e81..1728dd4 100644 --- a/project.clj +++ b/project.clj @@ -6,5 +6,5 @@ [clojail "0.5.0-beta3"]] ;; For lein and Heroku compatibility. If you're using cake, add ;; this line to .cake/config: jvm.opts = -Djava.security.policy=example.policy - :jvm-opts ["-Djava.security.policy=example.policy"] + :jvm-opts ["-Djava.security.policy=example.policy" #_"-Xmx80M"] :main tryclojure.core) \ No newline at end of file diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 4c731f4..66780c2 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -5,7 +5,7 @@ noir.core [noir.response :only [json]] [clojure.stacktrace :only [root-cause]] - [clojail.core :only [sandbox]] + [clojail.core :only [sandbox eagerly-consume]] [clojail.testers :only [secure-tester-without-def]]) (:require [noir.server :as server] [noir.session :as session]) From ba66cf4086c2d40d361046b77d4fa4ff49a0139e Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Fri, 4 Nov 2011 03:25:15 -0500 Subject: [PATCH 075/127] Store sandboxes in session and avoid race conditions. --- src/tryclojure/core.clj | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 66780c2..7bac698 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -1,7 +1,7 @@ (ns tryclojure.core (:use ring.adapter.jetty [hiccup form-helpers page-helpers] - [ring.middleware reload stacktrace params file session] + ring.middleware.file noir.core [noir.response :only [json]] [clojure.stacktrace :only [root-cause]] @@ -109,34 +109,21 @@ (let [form (binding [*read-eval* false] (read-string expr))] (eval-form form sbox))) -(def sandboxes (atom {:counter 0})) - (def try-clojure-tester (into secure-tester-without-def #{'tryclojure.core})) -(defn add-user [old] - (let [count (inc (:counter old))] - (assoc old - :counter count - count (sandbox try-clojure-tester - :timeout 2000 - :namespace (symbol (str "sandbox" (rand-int Integer/MAX_VALUE))))))) +(defn update-session! [f & args] + (apply swap! session/*noir-session* f args)) + +(defn find-sb [old] + (if-let [sb (get old "sb")] + old + (assoc old "sb" (sandbox try-clojure-tester :timeout 2000)))) (defn eval-request [expr] (try - (eval-string - expr - (do - (if-let [sb (@sandboxes (session/get :sb))] - sb - (let [{count :counter} (swap! sandboxes add-user)] - (session/put! :sb count) - (future - (Thread/sleep 600000) - (-> ((get @sandboxes count) '*ns*) str symbol remove-ns) - (swap! sandboxes dissoc count)) - (get @sandboxes count))))) + (eval-string expr (get (update-session! find-sb) "sb")) (catch TimeoutException _ {:error true :message "Execution Timed Out!"}) (catch Exception e From 9ad3239bbfc023a43a4b7aba99462ab59ee48158 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Fri, 4 Nov 2011 12:52:25 -0500 Subject: [PATCH 076/127] Expire the session -- remove the ns. --- src/tryclojure/core.clj | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 7bac698..5f64601 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -116,10 +116,16 @@ (defn update-session! [f & args] (apply swap! session/*noir-session* f args)) +(defn make-sandbox [] + (sandbox try-clojure-tester + :timeout 2000 + :init '(future (Thread/sleep 600000) + (-> *ns* .getName remove-ns)))) + (defn find-sb [old] (if-let [sb (get old "sb")] old - (assoc old "sb" (sandbox try-clojure-tester :timeout 2000)))) + (assoc old "sb" (make-sandbox)))) (defn eval-request [expr] (try @@ -150,6 +156,7 @@ (server/start (or (to-port port) (to-port (System/getenv "PORT")) ;; For deploying to Heroku - 8801))) + 8801) + {:session-cookie-attrs {:max-age 600}})) (defn -main [& args] (tryclj (first args))) \ No newline at end of file From 251b0965d09b432603c38d0ef5930871a92baff2 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Sat, 5 Nov 2011 23:53:21 -0500 Subject: [PATCH 077/127] Stop being stupid. --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index 1728dd4..2be4630 100644 --- a/project.clj +++ b/project.clj @@ -6,5 +6,5 @@ [clojail "0.5.0-beta3"]] ;; For lein and Heroku compatibility. If you're using cake, add ;; this line to .cake/config: jvm.opts = -Djava.security.policy=example.policy - :jvm-opts ["-Djava.security.policy=example.policy" #_"-Xmx80M"] + :jvm-opts ["-Djava.security.policy=example.policy""-Xmx80M"] :main tryclojure.core) \ No newline at end of file From c3ccdbabb312388be0bb42e268603a0cdf9e898b Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Sun, 6 Nov 2011 20:02:45 -0600 Subject: [PATCH 078/127] Update noir. --- project.clj | 2 +- src/tryclojure/core.clj | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/project.clj b/project.clj index 2be4630..c2a20b1 100644 --- a/project.clj +++ b/project.clj @@ -1,7 +1,7 @@ (defproject tryclojure "0.1.0-SNAPSHOT" :description "A simple web-based Clojure REPL for trying out Clojure without having to install it." :dependencies [[org.clojure/clojure "1.3.0"] - [noir "1.2.0"] + [noir "1.2.1"] [commons-lang/commons-lang "2.5"] [clojail "0.5.0-beta3"]] ;; For lein and Heroku compatibility. If you're using cake, add diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 5f64601..b4b88af 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -1,6 +1,5 @@ (ns tryclojure.core - (:use ring.adapter.jetty - [hiccup form-helpers page-helpers] + (:use [hiccup form-helpers page-helpers] ring.middleware.file noir.core [noir.response :only [json]] @@ -113,9 +112,6 @@ (into secure-tester-without-def #{'tryclojure.core})) -(defn update-session! [f & args] - (apply swap! session/*noir-session* f args)) - (defn make-sandbox [] (sandbox try-clojure-tester :timeout 2000 @@ -129,7 +125,7 @@ (defn eval-request [expr] (try - (eval-string expr (get (update-session! find-sb) "sb")) + (eval-string expr (get (session/swap! find-sb) "sb")) (catch TimeoutException _ {:error true :message "Execution Timed Out!"}) (catch Exception e From 20896b2185a7cd64fcd89f8016cefe67e5af0796 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Thu, 10 Nov 2011 21:24:25 -0500 Subject: [PATCH 079/127] Automatically bring doc into the namespace. --- src/tryclojure/core.clj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index b4b88af..46a6694 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -115,8 +115,9 @@ (defn make-sandbox [] (sandbox try-clojure-tester :timeout 2000 - :init '(future (Thread/sleep 600000) - (-> *ns* .getName remove-ns)))) + :init '(do (use '[clojure.repl :only [doc]]) + (future (Thread/sleep 600000) + (-> *ns* .getName remove-ns))))) (defn find-sb [old] (if-let [sb (get old "sb")] From 50200ec86569dd8c89a08d3ab1ff309d91458d74 Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 13 Nov 2011 19:55:25 +1100 Subject: [PATCH 080/127] Update resources/public/tutorial/page3.html --- resources/public/tutorial/page3.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/public/tutorial/page3.html b/resources/public/tutorial/page3.html index 387194c..925f30e 100644 --- a/resources/public/tutorial/page3.html +++ b/resources/public/tutorial/page3.html @@ -1,5 +1,5 @@

- Excellent work! There are other arithmetic funcions. + Excellent work! There are other arithmetic functions.

* - From 68bb7c1f3b8e929ef88666dbdbd8706a5719cb20 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Sat, 19 Nov 2011 17:09:54 -0600 Subject: [PATCH 081/127] Remove js dependencies and add a task for fetching them. --- project.clj | 7 +- .../public/javascript/jquery-1.4.2.min.js | 154 ----- resources/public/javascript/jquery.console.js | 647 ------------------ src/leiningen/fetch_js.clj | 18 + 4 files changed, 23 insertions(+), 803 deletions(-) delete mode 100644 resources/public/javascript/jquery-1.4.2.min.js delete mode 100644 resources/public/javascript/jquery.console.js create mode 100644 src/leiningen/fetch_js.clj diff --git a/project.clj b/project.clj index c2a20b1..2120500 100644 --- a/project.clj +++ b/project.clj @@ -3,8 +3,11 @@ :dependencies [[org.clojure/clojure "1.3.0"] [noir "1.2.1"] [commons-lang/commons-lang "2.5"] - [clojail "0.5.0-beta3"]] + [clojail "0.5.1"]] ;; For lein and Heroku compatibility. If you're using cake, add ;; this line to .cake/config: jvm.opts = -Djava.security.policy=example.policy :jvm-opts ["-Djava.security.policy=example.policy""-Xmx80M"] - :main tryclojure.core) \ No newline at end of file + :hooks [leiningen.fetch-js] + :main tryclojure.core) + + diff --git a/resources/public/javascript/jquery-1.4.2.min.js b/resources/public/javascript/jquery-1.4.2.min.js deleted file mode 100644 index 7c24308..0000000 --- a/resources/public/javascript/jquery-1.4.2.min.js +++ /dev/null @@ -1,154 +0,0 @@ -/*! - * jQuery JavaScript Library v1.4.2 - * http://jquery.com/ - * - * Copyright 2010, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * Copyright 2010, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * - * Date: Sat Feb 13 22:33:48 2010 -0500 - */ -(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/, -Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&& -(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this, -a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b=== -"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this, -function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b
a"; -var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected, -parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent= -false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n= -s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true, -applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando]; -else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this, -a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b=== -w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i, -cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected= -c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); -a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g, -function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split("."); -k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a), -C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B=0){a.type= -e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&& -f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive; -if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data", -e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a, -"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a, -d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, -e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift(); -t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D|| -g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()}, -CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m, -g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)}, -text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}}, -setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return hl[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h= -h[3];l=0;for(m=h.length;l=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m=== -"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g, -h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&& -q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML=""; -if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="

";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}(); -(function(){var g=s.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}: -function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var j=d;j0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j= -{},i;if(f&&a.length){e=0;for(var o=a.length;e-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a=== -"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode", -d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")? -a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType=== -1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/"},F={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= -c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, -wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, -prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, -this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); -return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja, -""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]); -return this}else{e=0;for(var j=d.length;e0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["", -""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]===""&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e= -c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]? -c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja= -function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter= -Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a, -"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f= -a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b= -a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=//gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!== -"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("
").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this}, -serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), -function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href, -global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&& -e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)? -"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache=== -false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B= -false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since", -c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E|| -d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x); -g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status=== -1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b=== -"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional; -if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration=== -"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]|| -c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start; -this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now= -this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem, -e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b
"; -a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b); -c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a, -d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top- -f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset": -"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in -e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window); diff --git a/resources/public/javascript/jquery.console.js b/resources/public/javascript/jquery.console.js deleted file mode 100644 index a21ee66..0000000 --- a/resources/public/javascript/jquery.console.js +++ /dev/null @@ -1,647 +0,0 @@ -// JQuery Console 1.0 -// Sun Feb 21 20:28:47 GMT 2010 -// -// Copyright 2010 Chris Done, Simon David Pratt. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// 1. Redistributions of source code must retain the above -// copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -// TESTED ON -// Internet Explorer 6 -// Opera 10.01 -// Chromium 4.0.237.0 (Ubuntu build 31094) -// Firefox 3.5.8, 3.6.2 (Mac) -// Safari 4.0.5 (6531.22.7) (Mac) -// Google Chrome 5.0.375.55 (Mac) - -(function($){ - $.fn.console = function(config){ - //////////////////////////////////////////////////////////////////////// - // Constants - // Some are enums, data types, others just for optimisation - var keyCodes = { - // left - 37: moveBackward, - // right - 39: moveForward, - // up - 38: previousHistory, - // down - 40: nextHistory, - // backspace - 8: backDelete, - // delete - 46: forwardDelete, - // end - 35: moveToEnd, - // start - 36: moveToStart, - // return - 13: commandTrigger, - // tab - 18: doNothing - }; - var ctrlCodes = { - // C-a - 65: moveToStart, - // C-e - 69: moveToEnd, - // C-d - 68: forwardDelete, - // C-n - 78: nextHistory, - // C-p - 80: previousHistory, - // C-b - 66: moveBackward, - // C-f - 70: moveForward, - // C-k - 75: deleteUntilEnd - }; - var altCodes = { - // M-f - 70: moveToNextWord, - // M-b - 66: moveToPreviousWord, - // M-d - 68: deleteNextWord - }; - var cursor = ' '; - // Opera only works with this character, not or ­, - // but IE6 displays this character, which is bad, so just use - // it on Opera. - var wbr = $.browser.opera? '' : '­'; - - //////////////////////////////////////////////////////////////////////// - // Globals - var container = $(this); - var inner = $('
'); - // erjiang: changed this from a text input to a textarea so we - // can get pasted newlines - var typer = $(''); - // Prompt - var promptBox; - var prompt; - var promptLabel = config && config.promptLabel? config.promptLabel : "> "; - var continuedPromptLabel = config && config.continuedPromptLabel? - config.continuedPromptLabel : "> "; - var column = 0; - var promptText = ''; - var restoreText = ''; - var continuedText = ''; - // Prompt history stack - var history = []; - var ringn = 0; - // For reasons unknown to The Sword of Michael himself, Opera - // triggers and sends a key character when you hit various - // keys like PgUp, End, etc. So there is no way of knowing - // when a user has typed '#' or End. My solution is in the - // typer.keydown and typer.keypress functions; I use the - // variable below to ignore the keypress event if the keydown - // event succeeds. - var cancelKeyPress = 0; - // When this value is false, the prompt will not respond to input - var acceptInput = true; - // When this value is true, the command has been canceled - var cancelCommand = false; - - // External exports object - var extern = {}; - - //////////////////////////////////////////////////////////////////////// - // Main entry point - (function(){ - container.append(inner); - inner.append(typer); - typer.css({position:'absolute',top:0,left:'-9999px'}); - if (config.welcomeMessage) - message(config.welcomeMessage,'jquery-console-welcome'); - newPromptBox(); - if (config.autofocus) { - inner.addClass('jquery-console-focus'); - typer.focus(); - setTimeout(function(){ - inner.addClass('jquery-console-focus'); - typer.focus(); - },100); - } - extern.inner = inner; - extern.typer = typer; - extern.scrollToBottom = scrollToBottom; - })(); - - //////////////////////////////////////////////////////////////////////// - // Reset terminal - extern.reset = function(){ - var welcome = (typeof config.welcomeMessage != 'undefined'); - inner.parent().fadeOut(function(){ - inner.find('div').each(function(){ - if (!welcome) { - $(this).remove(); - } else { - welcome = false; - } - }); - newPromptBox(); - inner.parent().fadeIn(function(){ - inner.addClass('jquery-console-focus'); - typer.focus(); - }); - }); - }; - - //////////////////////////////////////////////////////////////////////// - // Reset terminal - extern.notice = function(msg,style){ - var n = $('
').append($('
').text(msg)) - .css({visibility:'hidden'}); - container.append(n); - var focused = true; - if (style=='fadeout') - setTimeout(function(){ - n.fadeOut(function(){ - n.remove(); - }); - },4000); - else if (style=='prompt') { - var a = $('
'); - n.append(a); - focused = false; - a.click(function(){ n.fadeOut(function(){ n.remove();inner.css({opacity:1}) }); }); - } - var h = n.height(); - n.css({height:'0px',visibility:'visible'}) - .animate({height:h+'px'},function(){ - if (!focused) inner.css({opacity:0.5}); - }); - n.css('cursor','default'); - return n; - }; - - //////////////////////////////////////////////////////////////////////// - // Make a new prompt box - function newPromptBox() { - column = 0; - promptText = ''; - ringn = 0; // Reset the position of the history ring - enableInput(); - promptBox = $('
'); - var label = $(''); - var labelText = extern.continuedPrompt? continuedPromptLabel : promptLabel; - promptBox.append(label.text(labelText).show()); - label.html(label.html().replace(' ',' ')); - prompt = $(''); - promptBox.append(prompt); - inner.append(promptBox); - updatePromptDisplay(); - }; - - //////////////////////////////////////////////////////////////////////// - // Handle setting focus - container.click(function(){ - inner.addClass('jquery-console-focus'); - inner.removeClass('jquery-console-nofocus'); - typer.focus(); - scrollToBottom(); - return false; - }); - - //////////////////////////////////////////////////////////////////////// - // Handle losing focus - typer.blur(function(){ - inner.removeClass('jquery-console-focus'); - inner.addClass('jquery-console-nofocus'); - }); - - //////////////////////////////////////////////////////////////////////// - // Bind to the paste event of the input box so we know when we - // get pasted data - typer.bind('paste', function(e) { - // wipe typer input clean just in case - typer.val(""); - // this timeout is required because the onpaste event is - // fired *before* the text is actually pasted - setTimeout(function() { - typer.consoleInsert(typer.val()); - typer.val(""); - }, 0); - }); - - //////////////////////////////////////////////////////////////////////// - // Handle key hit before translation - // For picking up control characters like up/left/down/right - - typer.keydown(function(e){ - cancelKeyPress = 0; - var keyCode = e.keyCode; - // C-c: cancel the execution - if(e.ctrlKey && keyCode == 67) { - cancelKeyPress = keyCode; - cancelExecution(); - return false; - } - if (acceptInput) { - if (keyCode in keyCodes) { - cancelKeyPress = keyCode; - (keyCodes[keyCode])(); - return false; - } else if (e.ctrlKey && keyCode in ctrlCodes) { - cancelKeyPress = keyCode; - (ctrlCodes[keyCode])(); - return false; - } else if (e.altKey && keyCode in altCodes) { - cancelKeyPress = keyCode; - (altCodes[keyCode])(); - return false; - } - } - }); - - //////////////////////////////////////////////////////////////////////// - // Handle key press - typer.keypress(function(e){ - var keyCode = e.keyCode || e.which; - if (isIgnorableKey(e)) { - return false; - } - // // C-v: don't insert on paste event - if (e.ctrlKey && String.fromCharCode(keyCode).toLowerCase() == 'v') { - return true; - } - if (acceptInput && cancelKeyPress != keyCode && keyCode >= 32){ - if (cancelKeyPress) return false; - if (typeof config.charInsertTrigger == 'undefined' || - (typeof config.charInsertTrigger == 'function' && - config.charInsertTrigger(keyCode,promptText))) - typer.consoleInsert(keyCode); - } - if ($.browser.webkit) return false; - }); - - function isIgnorableKey(e) { - // for now just filter alt+tab that we receive on some platforms when - // user switches windows (goes away from the browser) - return ((e.keyCode == keyCodes.tab || e.keyCode == 192) && e.altKey); - }; - - //////////////////////////////////////////////////////////////////////// - // Rotate through the command history - function rotateHistory(n){ - if (history.length == 0) return; - ringn += n; - if (ringn < 0) ringn = history.length; - else if (ringn > history.length) ringn = 0; - var prevText = promptText; - if (ringn == 0) { - promptText = restoreText; - } else { - promptText = history[ringn - 1]; - } - if (config.historyPreserveColumn) { - if (promptText.length < column + 1) { - column = promptText.length; - } else if (column == 0) { - column = promptText.length; - } - } else { - column = promptText.length; - } - updatePromptDisplay(); - }; - - function previousHistory() { - rotateHistory(-1); - }; - - function nextHistory() { - rotateHistory(1); - }; - - // Add something to the history ring - function addToHistory(line){ - history.push(line); - restoreText = ''; - }; - - // Delete the character at the current position - function deleteCharAtPos(){ - if (column < promptText.length){ - promptText = - promptText.substring(0,column) + - promptText.substring(column+1); - restoreText = promptText; - return true; - } else return false; - }; - - function backDelete() { - if (moveColumn(-1)){ - deleteCharAtPos(); - updatePromptDisplay(); - } - }; - - function forwardDelete() { - if (deleteCharAtPos()) - updatePromptDisplay(); - }; - - function deleteUntilEnd() { - while(deleteCharAtPos()) { - updatePromptDisplay(); - } - }; - - function deleteNextWord() { - // A word is defined within this context as a series of alphanumeric - // characters. - // Delete up to the next alphanumeric character - while(column < promptText.length && - !isCharAlphanumeric(promptText[column])) { - deleteCharAtPos(); - updatePromptDisplay(); - } - // Then, delete until the next non-alphanumeric character - while(column < promptText.length && - isCharAlphanumeric(promptText[column])) { - deleteCharAtPos(); - updatePromptDisplay(); - } - }; - - //////////////////////////////////////////////////////////////////////// - // Validate command and trigger it if valid, or show a validation error - function commandTrigger() { - var line = promptText; - if (typeof config.commandValidate == 'function') { - var ret = config.commandValidate(line); - if (ret == true || ret == false) { - if (ret) { - handleCommand(); - } - } else { - commandResult(ret,"jquery-console-message-error"); - } - } else { - handleCommand(); - } - }; - - // Scroll to the bottom of the view - function scrollToBottom() { - inner.attr({ scrollTop: inner.attr("scrollHeight") });; - }; - - function cancelExecution() { - if(typeof config.cancelHandle == 'function') { - config.cancelHandle(); - } - } - - //////////////////////////////////////////////////////////////////////// - // Handle a command - function handleCommand() { - if (typeof config.commandHandle == 'function') { - disableInput(); - addToHistory(promptText); - var text = promptText; - if (extern.continuedPrompt) { - if (continuedText) - continuedText += '\n' + promptText; - else continuedText = promptText; - } else continuedText = undefined; - if (continuedText) text = continuedText; - var ret = config.commandHandle(text,function(msgs){ - commandResult(msgs); - }); - if (extern.continuedPrompt && !continuedText) - continuedText = promptText; - if (typeof ret == 'boolean') { - if (ret) { - // Command succeeded without a result. - commandResult(); - } else { - commandResult('Command failed.', - "jquery-console-message-error"); - } - } else if (typeof ret == "string") { - commandResult(ret,"jquery-console-message-success"); - } else if (typeof ret == 'object' && ret.length) { - commandResult(ret); - } else if (extern.continuedPrompt) { - commandResult(); - } - } - }; - - //////////////////////////////////////////////////////////////////////// - // Disable input - function disableInput() { - acceptInput = false; - }; - - // Enable input - function enableInput() { - acceptInput = true; - } - - //////////////////////////////////////////////////////////////////////// - // Reset the prompt in invalid command - function commandResult(msg,className) { - column = -1; - updatePromptDisplay(); - if (typeof msg == 'string') { - message(msg,className); - } else { - for (var x in msg) { - var ret = msg[x]; - message(ret.msg,ret.className); - } - } - newPromptBox(); - }; - - //////////////////////////////////////////////////////////////////////// - // Display a message - function message(msg,className) { - var mesg = $('
'); - if (className) mesg.addClass(className); - mesg.filledText(msg).hide(); - inner.append(mesg); - mesg.show(); - }; - - //////////////////////////////////////////////////////////////////////// - // Handle normal character insertion - // data can either be a number, which will be interpreted as the - // numeric value of a single character, or a string - typer.consoleInsert = function(data){ - // TODO: remove redundant indirection - var text = isNaN(data) ? data : String.fromCharCode(data); - var before = promptText.substring(0,column); - var after = promptText.substring(column); - promptText = before + text + after; - moveColumn(text.length); - restoreText = promptText; - updatePromptDisplay(); - }; - - //////////////////////////////////////////////////////////////////////// - // Move to another column relative to this one - // Negative means go back, positive means go forward. - function moveColumn(n){ - if (column + n >= 0 && column + n <= promptText.length){ - column += n; - return true; - } else return false; - }; - - function moveForward() { - if(moveColumn(1)) { - updatePromptDisplay(); - return true; - } - return false; - }; - - function moveBackward() { - if(moveColumn(-1)) { - updatePromptDisplay(); - return true; - } - return false; - }; - - function moveToStart() { - if (moveColumn(-column)) - updatePromptDisplay(); - }; - - function moveToEnd() { - if (moveColumn(promptText.length-column)) - updatePromptDisplay(); - }; - - function moveToNextWord() { - while(column < promptText.length && - !isCharAlphanumeric(promptText[column]) && - moveForward()) { - } - while(column < promptText.length && - isCharAlphanumeric(promptText[column]) && - moveForward()) { - } - }; - - function moveToPreviousWord() { - // Move backward until we find the first alphanumeric - while(column -1 >= 0 && - !isCharAlphanumeric(promptText[column-1]) && - moveBackward()) { - } - // Move until we find the first non-alphanumeric - while(column -1 >= 0 && - isCharAlphanumeric(promptText[column-1]) && - moveBackward()) { - } - }; - - function isCharAlphanumeric(charToTest) { - if(typeof charToTest == 'string') { - var code = charToTest.charCodeAt(); - return (code >= 'A'.charCodeAt() && code <= 'Z'.charCodeAt()) || - (code >= 'a'.charCodeAt() && code <= 'z'.charCodeAt()) || - (code >= '0'.charCodeAt() && code <= '9'.charCodeAt()); - } - return false; - }; - - function doNothing() {}; - - extern.promptText = function(text){ - if (text) { - promptText = text; - column = promptText.length; - updatePromptDisplay(); - } - return promptText; - }; - - //////////////////////////////////////////////////////////////////////// - // Update the prompt display - function updatePromptDisplay(){ - var line = promptText; - var html = ''; - if (column > 0 && line == ''){ - // When we have an empty line just display a cursor. - html = cursor; - } else if (column == promptText.length){ - // We're at the end of the line, so we need to display - // the text *and* cursor. - html = htmlEncode(line) + cursor; - } else { - // Grab the current character, if there is one, and - // make it the current cursor. - var before = line.substring(0, column); - var current = line.substring(column,column+1); - if (current){ - current = - '' + - htmlEncode(current) + - ''; - } - var after = line.substring(column+1); - html = htmlEncode(before) + current + htmlEncode(after); - } - prompt.html(html); - scrollToBottom(); - }; - - // Simple HTML encoding - // Simply replace '<', '>' and '&' - // TODO: Use jQuery's .html() trick, or grab a proper, fast - // HTML encoder. - function htmlEncode(text){ - return ( - text.replace(/&/g,'&') - .replace(/') - .replace(/([^<>&]{10})/g,'$1' + wbr) - ); - }; - - return extern; - }; - // Simple utility for printing messages - $.fn.filledText = function(txt){ - $(this).text(txt); - $(this).html($(this).html().replace(/\n/g,'
')); - return this; - }; -})(jQuery); diff --git a/src/leiningen/fetch_js.clj b/src/leiningen/fetch_js.clj new file mode 100644 index 0000000..bbc9122 --- /dev/null +++ b/src/leiningen/fetch_js.clj @@ -0,0 +1,18 @@ +(ns leiningen.fetch-js + (:use [leiningen.core :only [prepend-tasks]] + [leiningen.deps :only [deps]]) + (:require [clojure.java.io :as io])) + +(defn fetch-js [project] + (let [js (io/file "resources" "public" "javascript") + jquery (io/file js "jquery-1.4.2.min.js") + console (io/file js "jquery.console.js")] + (when-not (.exists jquery) + (println "Downloading jquery.") + (io/copy (io/reader "http://code.jquery.com/jquery-1.4.2.min.js") jquery)) + (when-not (.exists console) + (println "Downloading jquery-console.") + (io/copy (io/reader "https://raw.github.com/chrisdone/jquery-console/055c2c212944349ddbb045e8536ebfb2192acb80/jquery.console.js") + console)))) + +(prepend-tasks #'deps fetch-js) \ No newline at end of file From 4952dc326727b005c4aac125bdd2bd9814d4488f Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Sat, 19 Nov 2011 17:12:39 -0600 Subject: [PATCH 082/127] Ignore jquery. --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9a5d0bb..4f14c94 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ pom.xml lib classes *~ -.cake +resources/public/javascript/jquery* \ No newline at end of file From e99706fd92764a1467a6e6446459b16d0a79d094 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Mon, 28 Nov 2011 04:21:25 -0600 Subject: [PATCH 083/127] Added analytics. --- src/tryclojure/core.clj | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/tryclojure/core.clj b/src/tryclojure/core.clj index 46a6694..4932813 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/core.clj @@ -81,7 +81,17 @@ [:p.bottom "Built with " (link-to "http://webnoir.org" "Noir") - "."]]]]])) + "."]] + (javascript-tag + "var _gaq = _gaq || []; + _gaq.push(['_setAccount', 'UA-27340918-1']); + _gaq.push(['_trackPageview']); + + (function() { + var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; + ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; + var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); + })();")]]])) (defpage "/" [] (root-html)) From 2974c81672cbc1179f0eea085379ab14bc206216 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Tue, 29 Nov 2011 08:08:00 -0600 Subject: [PATCH 084/127] Use jQuery's 'trim' function so that things work on IE 8. Fixes #20. --- resources/public/javascript/tryclojure.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/public/javascript/tryclojure.js b/resources/public/javascript/tryclojure.js index dec3762..4e04303 100644 --- a/resources/public/javascript/tryclojure.js +++ b/resources/public/javascript/tryclojure.js @@ -125,7 +125,7 @@ function onValidate(input) { } function onHandle(line, report) { - var input = line.trim(); + var input = $.trim(line); // handle commands if (doCommand(input, report)) return; From bc0a0b870aa6cc8eecb92adf640335625abca8aa Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Tue, 29 Nov 2011 08:37:09 -0600 Subject: [PATCH 085/127] Move to a more traditional web app layout. --- project.clj | 2 +- src/tryclojure/models/eval.clj | 41 +++++++++++ src/tryclojure/server.clj | 18 +++++ src/tryclojure/views/eval.clj | 16 +++++ src/tryclojure/{core.clj => views/home.clj} | 78 +-------------------- src/tryclojure/views/tutorial.clj | 5 ++ 6 files changed, 84 insertions(+), 76 deletions(-) create mode 100644 src/tryclojure/models/eval.clj create mode 100644 src/tryclojure/server.clj create mode 100644 src/tryclojure/views/eval.clj rename src/tryclojure/{core.clj => views/home.clj} (63%) create mode 100644 src/tryclojure/views/tutorial.clj diff --git a/project.clj b/project.clj index 2120500..c7974b4 100644 --- a/project.clj +++ b/project.clj @@ -8,6 +8,6 @@ ;; this line to .cake/config: jvm.opts = -Djava.security.policy=example.policy :jvm-opts ["-Djava.security.policy=example.policy""-Xmx80M"] :hooks [leiningen.fetch-js] - :main tryclojure.core) + :main tryclojure.server) diff --git a/src/tryclojure/models/eval.clj b/src/tryclojure/models/eval.clj new file mode 100644 index 0000000..da9b1f1 --- /dev/null +++ b/src/tryclojure/models/eval.clj @@ -0,0 +1,41 @@ +(ns tryclojure.models.eval + (:use [clojail.testers :only [secure-tester-without-def]] + [clojail.core :only [sandbox]] + [clojure.stacktrace :only [root-cause]]) + (:require [noir.session :as session]) + (:import java.io.StringWriter + java.util.concurrent.TimeoutException)) + +(defn eval-form [form sbox] + (with-open [out (StringWriter.)] + (let [result (sbox form {#'*out* out})] + {:expr form + :result [out result]}))) + +(defn eval-string [expr sbox] + (let [form (binding [*read-eval* false] (read-string expr))] + (eval-form form sbox))) + +(def try-clojure-tester + (into secure-tester-without-def + #{'tryclojure.core})) + +(defn make-sandbox [] + (sandbox try-clojure-tester + :timeout 2000 + :init '(do (use '[clojure.repl :only [doc]]) + (future (Thread/sleep 600000) + (-> *ns* .getName remove-ns))))) + +(defn find-sb [old] + (if-let [sb (get old "sb")] + old + (assoc old "sb" (make-sandbox)))) + +(defn eval-request [expr] + (try + (eval-string expr (get (session/swap! find-sb) "sb")) + (catch TimeoutException _ + {:error true :message "Execution Timed Out!"}) + (catch Exception e + {:error true :message (str (root-cause e))}))) \ No newline at end of file diff --git a/src/tryclojure/server.clj b/src/tryclojure/server.clj new file mode 100644 index 0000000..a606a15 --- /dev/null +++ b/src/tryclojure/server.clj @@ -0,0 +1,18 @@ +(ns tryclojure.server + (:use [ring.middleware.file :only [wrap-file]]) + (:require [noir.server :as server])) + +(server/add-middleware wrap-file (System/getProperty "user.dir")) +(server/load-views "src/tryclojure/views") + +(defn to-port [s] + (when-let [port s] (Long. port))) + +(defn tryclj [& [port]] + (server/start + (or (to-port port) + (to-port (System/getenv "PORT")) ;; For deploying to Heroku + 8801) + {:session-cookie-attrs {:max-age 600}})) + +(defn -main [& args] (tryclj (first args))) \ No newline at end of file diff --git a/src/tryclojure/views/eval.clj b/src/tryclojure/views/eval.clj new file mode 100644 index 0000000..782e40e --- /dev/null +++ b/src/tryclojure/views/eval.clj @@ -0,0 +1,16 @@ +(ns tryclojure.views.eval + (:use [noir.core :only [defpage]] + [noir.response :only [json]] + [tryclojure.models.eval :only [eval-request]])) + +(defpage "/eval.json" {:keys [expr jsonp]} + (update-in + (json + (let [{:keys [expr result error message] :as res} (eval-request expr)] + (if error + res + (let [[out res] result] + {:expr (pr-str expr) + :result (str out (pr-str res))})))) + [:body] + #(if jsonp (str jsonp "(" % ")") %))) \ No newline at end of file diff --git a/src/tryclojure/core.clj b/src/tryclojure/views/home.clj similarity index 63% rename from src/tryclojure/core.clj rename to src/tryclojure/views/home.clj index 4932813..db6f1a4 100644 --- a/src/tryclojure/core.clj +++ b/src/tryclojure/views/home.clj @@ -1,15 +1,6 @@ -(ns tryclojure.core - (:use [hiccup form-helpers page-helpers] - ring.middleware.file - noir.core - [noir.response :only [json]] - [clojure.stacktrace :only [root-cause]] - [clojail.core :only [sandbox eagerly-consume]] - [clojail.testers :only [secure-tester-without-def]]) - (:require [noir.server :as server] - [noir.session :as session]) - (:import java.io.StringWriter - java.util.concurrent.TimeoutException)) +(ns tryclojure.views.home + (:use [noir.core :only [defpartial defpage]] + [hiccup form-helpers page-helpers])) (defpartial links [] (unordered-list @@ -104,66 +95,3 @@ (defpage "/links" [] (links)) - -(defpage [:post "/tutorial"] {n :n} - (slurp (str "resources/public/tutorial/page" n ".html"))) - -(defn eval-form [form sbox] - (with-open [out (java.io.StringWriter.)] - (let [result (sbox form {#'*out* out})] - {:expr form - :result [out result]}))) - -(defn eval-string [expr sbox] - (let [form (binding [*read-eval* false] (read-string expr))] - (eval-form form sbox))) - -(def try-clojure-tester - (into secure-tester-without-def - #{'tryclojure.core})) - -(defn make-sandbox [] - (sandbox try-clojure-tester - :timeout 2000 - :init '(do (use '[clojure.repl :only [doc]]) - (future (Thread/sleep 600000) - (-> *ns* .getName remove-ns))))) - -(defn find-sb [old] - (if-let [sb (get old "sb")] - old - (assoc old "sb" (make-sandbox)))) - -(defn eval-request [expr] - (try - (eval-string expr (get (session/swap! find-sb) "sb")) - (catch TimeoutException _ - {:error true :message "Execution Timed Out!"}) - (catch Exception e - {:error true :message (str (root-cause e))}))) - -(defpage "/eval.json" {:keys [expr jsonp]} - (update-in - (json - (let [{:keys [expr result error message] :as res} (eval-request expr)] - (if error - res - (let [[out res] result] - {:expr (pr-str expr) - :result (str out (pr-str res))})))) - [:body] - #(if jsonp (str jsonp "(" % ")") %))) - -(server/add-middleware wrap-file (System/getProperty "user.dir")) - -(defn to-port [s] - (when-let [port s] (Long. port))) - -(defn tryclj [& [port]] - (server/start - (or (to-port port) - (to-port (System/getenv "PORT")) ;; For deploying to Heroku - 8801) - {:session-cookie-attrs {:max-age 600}})) - -(defn -main [& args] (tryclj (first args))) \ No newline at end of file diff --git a/src/tryclojure/views/tutorial.clj b/src/tryclojure/views/tutorial.clj new file mode 100644 index 0000000..69538ad --- /dev/null +++ b/src/tryclojure/views/tutorial.clj @@ -0,0 +1,5 @@ +(ns tryclojure.views.tutorial + (:use [noir.core :only [defpage]])) + +(defpage [:post "/tutorial"] {n :n} + (slurp (str "resources/public/tutorial/page" n ".html"))) \ No newline at end of file From a44c152f50f6834d4737e8d395e4a45c13a4bcab Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Tue, 29 Nov 2011 08:37:41 -0600 Subject: [PATCH 086/127] This used to be used more than once, but not anymore. --- src/tryclojure/views/home.clj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/tryclojure/views/home.clj b/src/tryclojure/views/home.clj index db6f1a4..d1da94b 100644 --- a/src/tryclojure/views/home.clj +++ b/src/tryclojure/views/home.clj @@ -12,8 +12,6 @@ (link-to "http://disclojure.org" "Disclojure") (link-to "http://planet.clojure.in" "Planet Clojure")])) -(def github-link (link-to "http://github.com/Raynes/tryclojure" "github")) - (defpartial about-html [] [:p.bottom "Please note that this REPL is sandboxed, so you wont be able to do everything in it " @@ -26,7 +24,7 @@ "It'll be in an entirely new namespace/sandbox."] [:p.bottom "You can find the site's source and such on its " - github-link + (link-to "http://github.com/Raynes/tryclojure" "github") " page."] [:p.bottom "TryClojure is written in Clojure and JavaScript (JQuery), powered by " From 006cb693e8ce3a35d2eb598990870cb64617c871 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Tue, 29 Nov 2011 09:57:58 -0600 Subject: [PATCH 087/127] Add some eval tests. --- test/tryclojure/core_test.clj | 6 ------ test/tryclojure/eval_test.clj | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) delete mode 100644 test/tryclojure/core_test.clj create mode 100644 test/tryclojure/eval_test.clj diff --git a/test/tryclojure/core_test.clj b/test/tryclojure/core_test.clj deleted file mode 100644 index d91e716..0000000 --- a/test/tryclojure/core_test.clj +++ /dev/null @@ -1,6 +0,0 @@ -(ns tryclojure.core-test - (:use [tryclojure.core] :reload-all) - (:use [clojure.test])) - -(deftest replace-me ;; FIXME: write - (is false)) diff --git a/test/tryclojure/eval_test.clj b/test/tryclojure/eval_test.clj new file mode 100644 index 0000000..041ae6c --- /dev/null +++ b/test/tryclojure/eval_test.clj @@ -0,0 +1,18 @@ +(ns tryclojure.eval-test + (:use tryclojure.models.eval + clojure.test) + (:require noir.session)) + +(def sb (make-sandbox)) + +(deftest eval-form-test + (let [form "(do (println 10) (+ 3 3))" + result (eval-string form sb)] + (is (= "10\n" (-> result :result first str))) + (is (= "6" (-> result :result second str))) + (is (= (read-string form) (-> result :expr))))) + +(alter-var-root #'noir.session/*noir-session* (constantly (atom {}))) + +(deftest eval-request-test + (is (= "Execution Timed Out!" (:message (eval-request "(while true)"))))) \ No newline at end of file From 8e132f046391b1c5fb2a383154f02ccf2f4a35ad Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Tue, 29 Nov 2011 10:06:38 -0600 Subject: [PATCH 088/127] Use jsonp from noir.response. --- src/tryclojure/views/eval.clj | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/tryclojure/views/eval.clj b/src/tryclojure/views/eval.clj index 782e40e..8010a34 100644 --- a/src/tryclojure/views/eval.clj +++ b/src/tryclojure/views/eval.clj @@ -1,16 +1,16 @@ (ns tryclojure.views.eval (:use [noir.core :only [defpage]] - [noir.response :only [json]] - [tryclojure.models.eval :only [eval-request]])) + [tryclojure.models.eval :only [eval-request]]) + (:require [noir.response :as resp])) (defpage "/eval.json" {:keys [expr jsonp]} - (update-in - (json - (let [{:keys [expr result error message] :as res} (eval-request expr)] - (if error - res - (let [[out res] result] - {:expr (pr-str expr) - :result (str out (pr-str res))})))) - [:body] - #(if jsonp (str jsonp "(" % ")") %))) \ No newline at end of file + (let [{:keys [expr result error message] :as res} (eval-request expr) + data (if error + res + (let [[out res] result] + {:expr (pr-str expr) + :result (str out (pr-str res))}))] + + (if jsonp + (resp/jsonp jsonp data) + (resp/json data)))) \ No newline at end of file From e271f59f20dd80c07357a6d44dcab8c5d0096b4d Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Tue, 29 Nov 2011 10:06:53 -0600 Subject: [PATCH 089/127] Add a .travis.yml file. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..4f42080 --- /dev/null +++ b/.travis.yml @@ -0,0 +1 @@ +language: clojure \ No newline at end of file From b9d2d30f953620e45938d611c3e4c2838e089e71 Mon Sep 17 00:00:00 2001 From: Michael Kopinsky Date: Sun, 18 Dec 2011 11:28:43 -0500 Subject: [PATCH 090/127] Grammar fix --- resources/public/tutorial/page10.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/public/tutorial/page10.html b/resources/public/tutorial/page10.html index 9968bcb..f19c97b 100644 --- a/resources/public/tutorial/page10.html +++ b/resources/public/tutorial/page10.html @@ -11,7 +11,7 @@ Lists: '(1 2 3 4)
Sets: #{1 2 3 4}
Vectors and lists are sequential and ordered collections. You'll see vectors used much more than lists. - Maps are typical hash-maps. Unordered collections indexed by keys. The keys can be any object. Here, + Maps are typical hash-maps - unordered collections indexed by keys. The keys can be any object. Here, we've used a keyword, :foo as a key. Keywords make excellent keys. We also used a number. Sets are mathematical sets.

From 91cb8c4c71de121d8ab3b58c5190cf5ffe554bcd Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Sat, 5 May 2012 16:08:07 -0500 Subject: [PATCH 091/127] Rename README to README.md. --- README => README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename README => README.md (100%) diff --git a/README b/README.md similarity index 100% rename from README rename to README.md From b2ebc38fc96d4a51c86398db9a1b3c25b44a454d Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Sat, 5 May 2012 16:10:52 -0500 Subject: [PATCH 092/127] Update to 1.4, get rid of leiningen tasks for fetching stuff. Updated all the dependencies and got rid of the Leiningen task for fetching javascript files. Instead, we're going to use a git submodule to fetch jquery-console and use a CDN for jquery itself. --- .gitignore | 1 - .gitmodules | 3 + project.clj | 7 +- .../javascript/jquery-console/.gitignore | 20 + .../public/javascript/jquery-console/README | 72 ++ .../javascript/jquery-console/demo.html | 194 ++++++ .../jquery-console/jquery-1.4.2.min.js | 154 +++++ .../jquery-console/jquery.console.js | 649 ++++++++++++++++++ src/leiningen/fetch_js.clj | 18 - src/tryclojure/models/eval.clj | 10 +- src/tryclojure/server.clj | 4 +- src/tryclojure/views/eval.clj | 6 +- src/tryclojure/views/home.clj | 13 +- src/tryclojure/views/tutorial.clj | 2 +- 14 files changed, 1112 insertions(+), 41 deletions(-) create mode 100644 .gitmodules create mode 100644 resources/public/javascript/jquery-console/.gitignore create mode 100644 resources/public/javascript/jquery-console/README create mode 100644 resources/public/javascript/jquery-console/demo.html create mode 100644 resources/public/javascript/jquery-console/jquery-1.4.2.min.js create mode 100644 resources/public/javascript/jquery-console/jquery.console.js delete mode 100644 src/leiningen/fetch_js.clj diff --git a/.gitignore b/.gitignore index 4f14c94..5fcc507 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,3 @@ pom.xml lib classes *~ -resources/public/javascript/jquery* \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..9cf7391 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "resources/public/javascript/jquery-console"] + path = resources/public/javascript/jquery-console + url = https://github.com/chrisdone/jquery-console.git diff --git a/project.clj b/project.clj index c7974b4..cce6b10 100644 --- a/project.clj +++ b/project.clj @@ -1,13 +1,10 @@ (defproject tryclojure "0.1.0-SNAPSHOT" :description "A simple web-based Clojure REPL for trying out Clojure without having to install it." - :dependencies [[org.clojure/clojure "1.3.0"] - [noir "1.2.1"] + :dependencies [[org.clojure/clojure "1.4.0"] + [noir "1.3.0-beta3"] [commons-lang/commons-lang "2.5"] [clojail "0.5.1"]] - ;; For lein and Heroku compatibility. If you're using cake, add - ;; this line to .cake/config: jvm.opts = -Djava.security.policy=example.policy :jvm-opts ["-Djava.security.policy=example.policy""-Xmx80M"] - :hooks [leiningen.fetch-js] :main tryclojure.server) diff --git a/resources/public/javascript/jquery-console/.gitignore b/resources/public/javascript/jquery-console/.gitignore new file mode 100644 index 0000000..ac8f968 --- /dev/null +++ b/resources/public/javascript/jquery-console/.gitignore @@ -0,0 +1,20 @@ +# Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so +*.pyc + +# Logs and databases # +###################### +*.log + +# OS generated files # +###################### +.DS_Store* +ehthumbs.db +Icon? +Thumbs.db diff --git a/resources/public/javascript/jquery-console/README b/resources/public/javascript/jquery-console/README new file mode 100644 index 0000000..245420f --- /dev/null +++ b/resources/public/javascript/jquery-console/README @@ -0,0 +1,72 @@ +INTRODUCTION + +See demo.html. Or here: http://chrisdone.com/jquery-console/demo.html + +Options available: + + autofocus bool Autofocus the terminal, rather than + having to click on it. + + promptHistory bool Provide history support (kind of crappy, + needs doing properly.) + + historyPreserveColumn bool Preserve the column you were one when + switching between history. + + welcomeMessage string Just a first message to display on the + terminal. + + promptLabel string Prompt string like 'JavaScript> '. + + commandValidate function When user hits return, validate + whether to trigger commandHandle and + re-prompt. + + commandHandle function Handle the command line, return a + string, boolean, or list + of {msg:"foo",className:"my-css-class"}. + commandHandle(line,report) is + called. Report function is for you + to report a result of the command + asynchronously. + + animateScroll bool Whether to animate the scroll to + top. Currently disabled. + + charInsertTrigger function Predicate for whether to allow + character insertion. + charInsertTrigger(char,line) is called. + + cancelHandle function Handle a user-signaled interrupt. + +LICENSE + +Copyright 2010 Chris Done, Simon David Pratt. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + + 2. Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + diff --git a/resources/public/javascript/jquery-console/demo.html b/resources/public/javascript/jquery-console/demo.html new file mode 100644 index 0000000..370ca73 --- /dev/null +++ b/resources/public/javascript/jquery-console/demo.html @@ -0,0 +1,194 @@ + + + + JQuery Console Demo + + + + + + + + + +

Simple console demo

+

Tested on:

+
    +
  • Internet Explorer 6
  • +
  • Opera 10.01
  • +
  • Chromium 4.0.237.0 (Ubuntu build 31094)
  • +
  • Firefox 3.5.8
  • +
+ + diff --git a/resources/public/javascript/jquery-console/jquery-1.4.2.min.js b/resources/public/javascript/jquery-console/jquery-1.4.2.min.js new file mode 100644 index 0000000..7c24308 --- /dev/null +++ b/resources/public/javascript/jquery-console/jquery-1.4.2.min.js @@ -0,0 +1,154 @@ +/*! + * jQuery JavaScript Library v1.4.2 + * http://jquery.com/ + * + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2010, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Sat Feb 13 22:33:48 2010 -0500 + */ +(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/, +Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&& +(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this, +a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b=== +"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this, +function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b
a"; +var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected, +parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent= +false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n= +s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true, +applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando]; +else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this, +a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b=== +w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i, +cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected= +c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); +a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g, +function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split("."); +k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a), +C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B=0){a.type= +e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&& +f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive; +if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data", +e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a, +"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a, +d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, +e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift(); +t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D|| +g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()}, +CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m, +g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)}, +text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}}, +setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return hl[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h= +h[3];l=0;for(m=h.length;l=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m=== +"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g, +h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&& +q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML=""; +if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="

";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}(); +(function(){var g=s.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}: +function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var j=d;j0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j= +{},i;if(f&&a.length){e=0;for(var o=a.length;e-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a=== +"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode", +d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")? +a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType=== +1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/"},F={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= +c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, +wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, +prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, +this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); +return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja, +""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]); +return this}else{e=0;for(var j=d.length;e0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["", +""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]===""&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e= +c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]? +c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja= +function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter= +Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a, +"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f= +a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b= +a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=//gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!== +"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("
").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this}, +serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), +function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href, +global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&& +e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)? +"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache=== +false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B= +false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since", +c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E|| +d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x); +g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status=== +1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b=== +"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional; +if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration=== +"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]|| +c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start; +this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now= +this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem, +e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b
"; +a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b); +c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a, +d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top- +f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset": +"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in +e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window); diff --git a/resources/public/javascript/jquery-console/jquery.console.js b/resources/public/javascript/jquery-console/jquery.console.js new file mode 100644 index 0000000..1766fb5 --- /dev/null +++ b/resources/public/javascript/jquery-console/jquery.console.js @@ -0,0 +1,649 @@ +// JQuery Console 1.0 +// Sun Feb 21 20:28:47 GMT 2010 +// +// Copyright 2010 Chris Done, Simon David Pratt. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above +// copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// TESTED ON +// Internet Explorer 6 +// Opera 10.01 +// Chromium 4.0.237.0 (Ubuntu build 31094) +// Firefox 3.5.8, 3.6.2 (Mac) +// Safari 4.0.5 (6531.22.7) (Mac) +// Google Chrome 5.0.375.55 (Mac) + +(function($){ + $.fn.console = function(config){ + //////////////////////////////////////////////////////////////////////// + // Constants + // Some are enums, data types, others just for optimisation + var keyCodes = { + // left + 37: moveBackward, + // right + 39: moveForward, + // up + 38: previousHistory, + // down + 40: nextHistory, + // backspace + 8: backDelete, + // delete + 46: forwardDelete, + // end + 35: moveToEnd, + // start + 36: moveToStart, + // return + 13: commandTrigger, + // tab + 18: doNothing + }; + var ctrlCodes = { + // C-a + 65: moveToStart, + // C-e + 69: moveToEnd, + // C-d + 68: forwardDelete, + // C-n + 78: nextHistory, + // C-p + 80: previousHistory, + // C-b + 66: moveBackward, + // C-f + 70: moveForward, + // C-k + 75: deleteUntilEnd + }; + var altCodes = { + // M-f + 70: moveToNextWord, + // M-b + 66: moveToPreviousWord, + // M-d + 68: deleteNextWord + }; + var cursor = ' '; + + //////////////////////////////////////////////////////////////////////// + // Globals + var container = $(this); + var inner = $('
'); + // erjiang: changed this from a text input to a textarea so we + // can get pasted newlines + var typer = $(''); + // Prompt + var promptBox; + var prompt; + var promptLabel = config && config.promptLabel? config.promptLabel : "> "; + var continuedPromptLabel = config && config.continuedPromptLabel? + config.continuedPromptLabel : "> "; + var column = 0; + var promptText = ''; + var restoreText = ''; + var continuedText = ''; + // Prompt history stack + var history = []; + var ringn = 0; + // For reasons unknown to The Sword of Michael himself, Opera + // triggers and sends a key character when you hit various + // keys like PgUp, End, etc. So there is no way of knowing + // when a user has typed '#' or End. My solution is in the + // typer.keydown and typer.keypress functions; I use the + // variable below to ignore the keypress event if the keydown + // event succeeds. + var cancelKeyPress = 0; + // When this value is false, the prompt will not respond to input + var acceptInput = true; + // When this value is true, the command has been canceled + var cancelCommand = false; + + // External exports object + var extern = {}; + + //////////////////////////////////////////////////////////////////////// + // Main entry point + (function(){ + container.append(inner); + inner.append(typer); + typer.css({position:'absolute',top:0,left:'-9999px'}); + if (config.welcomeMessage) + message(config.welcomeMessage,'jquery-console-welcome'); + newPromptBox(); + if (config.autofocus) { + inner.addClass('jquery-console-focus'); + typer.focus(); + setTimeout(function(){ + inner.addClass('jquery-console-focus'); + typer.focus(); + },100); + } + extern.inner = inner; + extern.typer = typer; + extern.scrollToBottom = scrollToBottom; + })(); + + //////////////////////////////////////////////////////////////////////// + // Reset terminal + extern.reset = function(){ + var welcome = (typeof config.welcomeMessage != 'undefined'); + inner.parent().fadeOut(function(){ + inner.find('div').each(function(){ + if (!welcome) { + $(this).remove(); + } else { + welcome = false; + } + }); + newPromptBox(); + inner.parent().fadeIn(function(){ + inner.addClass('jquery-console-focus'); + typer.focus(); + }); + }); + }; + + //////////////////////////////////////////////////////////////////////// + // Reset terminal + extern.notice = function(msg,style){ + var n = $('
').append($('
').text(msg)) + .css({visibility:'hidden'}); + container.append(n); + var focused = true; + if (style=='fadeout') + setTimeout(function(){ + n.fadeOut(function(){ + n.remove(); + }); + },4000); + else if (style=='prompt') { + var a = $('
'); + n.append(a); + focused = false; + a.click(function(){ n.fadeOut(function(){ n.remove();inner.css({opacity:1}) }); }); + } + var h = n.height(); + n.css({height:'0px',visibility:'visible'}) + .animate({height:h+'px'},function(){ + if (!focused) inner.css({opacity:0.5}); + }); + n.css('cursor','default'); + return n; + }; + + //////////////////////////////////////////////////////////////////////// + // Make a new prompt box + function newPromptBox() { + column = 0; + promptText = ''; + ringn = 0; // Reset the position of the history ring + enableInput(); + promptBox = $('
'); + var label = $(''); + var labelText = extern.continuedPrompt? continuedPromptLabel : promptLabel; + promptBox.append(label.text(labelText).show()); + label.html(label.html().replace(' ',' ')); + prompt = $(''); + promptBox.append(prompt); + inner.append(promptBox); + updatePromptDisplay(); + }; + + //////////////////////////////////////////////////////////////////////// + // Handle setting focus + container.click(function(){ + inner.addClass('jquery-console-focus'); + inner.removeClass('jquery-console-nofocus'); + typer.focus(); + scrollToBottom(); + return false; + }); + + //////////////////////////////////////////////////////////////////////// + // Handle losing focus + typer.blur(function(){ + inner.removeClass('jquery-console-focus'); + inner.addClass('jquery-console-nofocus'); + }); + + //////////////////////////////////////////////////////////////////////// + // Bind to the paste event of the input box so we know when we + // get pasted data + typer.bind('paste', function(e) { + // wipe typer input clean just in case + typer.val(""); + // this timeout is required because the onpaste event is + // fired *before* the text is actually pasted + setTimeout(function() { + typer.consoleInsert(typer.val()); + typer.val(""); + }, 0); + }); + + //////////////////////////////////////////////////////////////////////// + // Handle key hit before translation + // For picking up control characters like up/left/down/right + + typer.keydown(function(e){ + cancelKeyPress = 0; + var keyCode = e.keyCode; + // C-c: cancel the execution + if(e.ctrlKey && keyCode == 67) { + cancelKeyPress = keyCode; + cancelExecution(); + return false; + } + if (acceptInput) { + if (keyCode in keyCodes) { + cancelKeyPress = keyCode; + (keyCodes[keyCode])(); + return false; + } else if (e.ctrlKey && keyCode in ctrlCodes) { + cancelKeyPress = keyCode; + (ctrlCodes[keyCode])(); + return false; + } else if (e.altKey && keyCode in altCodes) { + cancelKeyPress = keyCode; + (altCodes[keyCode])(); + return false; + } + } + }); + + //////////////////////////////////////////////////////////////////////// + // Handle key press + typer.keypress(function(e){ + var keyCode = e.keyCode || e.which; + if (isIgnorableKey(e)) { + return false; + } + // // C-v: don't insert on paste event + if ((e.ctrlKey || e.metaKey) && String.fromCharCode(keyCode).toLowerCase() == 'v') { + return true; + } + if (acceptInput && cancelKeyPress != keyCode && keyCode >= 32){ + if (cancelKeyPress) return false; + if (typeof config.charInsertTrigger == 'undefined' || + (typeof config.charInsertTrigger == 'function' && + config.charInsertTrigger(keyCode,promptText))) + typer.consoleInsert(keyCode); + } + if ($.browser.webkit) return false; + }); + + function isIgnorableKey(e) { + // for now just filter alt+tab that we receive on some platforms when + // user switches windows (goes away from the browser) + return ((e.keyCode == keyCodes.tab || e.keyCode == 192) && e.altKey); + }; + + //////////////////////////////////////////////////////////////////////// + // Rotate through the command history + function rotateHistory(n){ + if (history.length == 0) return; + ringn += n; + if (ringn < 0) ringn = history.length; + else if (ringn > history.length) ringn = 0; + var prevText = promptText; + if (ringn == 0) { + promptText = restoreText; + } else { + promptText = history[ringn - 1]; + } + if (config.historyPreserveColumn) { + if (promptText.length < column + 1) { + column = promptText.length; + } else if (column == 0) { + column = promptText.length; + } + } else { + column = promptText.length; + } + updatePromptDisplay(); + }; + + function previousHistory() { + rotateHistory(-1); + }; + + function nextHistory() { + rotateHistory(1); + }; + + // Add something to the history ring + function addToHistory(line){ + history.push(line); + restoreText = ''; + }; + + // Delete the character at the current position + function deleteCharAtPos(){ + if (column < promptText.length){ + promptText = + promptText.substring(0,column) + + promptText.substring(column+1); + restoreText = promptText; + return true; + } else return false; + }; + + function backDelete() { + if (moveColumn(-1)){ + deleteCharAtPos(); + updatePromptDisplay(); + } + }; + + function forwardDelete() { + if (deleteCharAtPos()) + updatePromptDisplay(); + }; + + function deleteUntilEnd() { + while(deleteCharAtPos()) { + updatePromptDisplay(); + } + }; + + function deleteNextWord() { + // A word is defined within this context as a series of alphanumeric + // characters. + // Delete up to the next alphanumeric character + while(column < promptText.length && + !isCharAlphanumeric(promptText[column])) { + deleteCharAtPos(); + updatePromptDisplay(); + } + // Then, delete until the next non-alphanumeric character + while(column < promptText.length && + isCharAlphanumeric(promptText[column])) { + deleteCharAtPos(); + updatePromptDisplay(); + } + }; + + //////////////////////////////////////////////////////////////////////// + // Validate command and trigger it if valid, or show a validation error + function commandTrigger() { + var line = promptText; + if (typeof config.commandValidate == 'function') { + var ret = config.commandValidate(line); + if (ret == true || ret == false) { + if (ret) { + handleCommand(); + } + } else { + commandResult(ret,"jquery-console-message-error"); + } + } else { + handleCommand(); + } + }; + + // Scroll to the bottom of the view + function scrollToBottom() { + if (jQuery.fn.jquery > "1.6") { + inner.prop({ scrollTop: inner.prop("scrollHeight") }); + } + else { + inner.attr({ scrollTop: inner.attr("scrollHeight") }); + } + }; + + function cancelExecution() { + if(typeof config.cancelHandle == 'function') { + config.cancelHandle(); + } + } + + //////////////////////////////////////////////////////////////////////// + // Handle a command + function handleCommand() { + if (typeof config.commandHandle == 'function') { + disableInput(); + addToHistory(promptText); + var text = promptText; + if (extern.continuedPrompt) { + if (continuedText) + continuedText += '\n' + promptText; + else continuedText = promptText; + } else continuedText = undefined; + if (continuedText) text = continuedText; + var ret = config.commandHandle(text,function(msgs){ + commandResult(msgs); + }); + if (extern.continuedPrompt && !continuedText) + continuedText = promptText; + if (typeof ret == 'boolean') { + if (ret) { + // Command succeeded without a result. + commandResult(); + } else { + commandResult('Command failed.', + "jquery-console-message-error"); + } + } else if (typeof ret == "string") { + commandResult(ret,"jquery-console-message-success"); + } else if (typeof ret == 'object' && ret.length) { + commandResult(ret); + } else if (extern.continuedPrompt) { + commandResult(); + } + } + }; + + //////////////////////////////////////////////////////////////////////// + // Disable input + function disableInput() { + acceptInput = false; + }; + + // Enable input + function enableInput() { + acceptInput = true; + } + + //////////////////////////////////////////////////////////////////////// + // Reset the prompt in invalid command + function commandResult(msg,className) { + column = -1; + updatePromptDisplay(); + if (typeof msg == 'string') { + message(msg,className); + } else if ($.isArray(msg)) { + for (var x in msg) { + var ret = msg[x]; + message(ret.msg,ret.className); + } + } else { // Assume it's a DOM node or jQuery object. + inner.append(msg); + } + newPromptBox(); + }; + + //////////////////////////////////////////////////////////////////////// + // Display a message + function message(msg,className) { + var mesg = $('
'); + if (className) mesg.addClass(className); + mesg.filledText(msg).hide(); + inner.append(mesg); + mesg.show(); + }; + + //////////////////////////////////////////////////////////////////////// + // Handle normal character insertion + // data can either be a number, which will be interpreted as the + // numeric value of a single character, or a string + typer.consoleInsert = function(data){ + // TODO: remove redundant indirection + var text = isNaN(data) ? data : String.fromCharCode(data); + var before = promptText.substring(0,column); + var after = promptText.substring(column); + promptText = before + text + after; + moveColumn(text.length); + restoreText = promptText; + updatePromptDisplay(); + }; + + //////////////////////////////////////////////////////////////////////// + // Move to another column relative to this one + // Negative means go back, positive means go forward. + function moveColumn(n){ + if (column + n >= 0 && column + n <= promptText.length){ + column += n; + return true; + } else return false; + }; + + function moveForward() { + if(moveColumn(1)) { + updatePromptDisplay(); + return true; + } + return false; + }; + + function moveBackward() { + if(moveColumn(-1)) { + updatePromptDisplay(); + return true; + } + return false; + }; + + function moveToStart() { + if (moveColumn(-column)) + updatePromptDisplay(); + }; + + function moveToEnd() { + if (moveColumn(promptText.length-column)) + updatePromptDisplay(); + }; + + function moveToNextWord() { + while(column < promptText.length && + !isCharAlphanumeric(promptText[column]) && + moveForward()) { + } + while(column < promptText.length && + isCharAlphanumeric(promptText[column]) && + moveForward()) { + } + }; + + function moveToPreviousWord() { + // Move backward until we find the first alphanumeric + while(column -1 >= 0 && + !isCharAlphanumeric(promptText[column-1]) && + moveBackward()) { + } + // Move until we find the first non-alphanumeric + while(column -1 >= 0 && + isCharAlphanumeric(promptText[column-1]) && + moveBackward()) { + } + }; + + function isCharAlphanumeric(charToTest) { + if(typeof charToTest == 'string') { + var code = charToTest.charCodeAt(); + return (code >= 'A'.charCodeAt() && code <= 'Z'.charCodeAt()) || + (code >= 'a'.charCodeAt() && code <= 'z'.charCodeAt()) || + (code >= '0'.charCodeAt() && code <= '9'.charCodeAt()); + } + return false; + }; + + function doNothing() {}; + + extern.promptText = function(text){ + if (text) { + promptText = text; + column = promptText.length; + updatePromptDisplay(); + } + return promptText; + }; + + //////////////////////////////////////////////////////////////////////// + // Update the prompt display + function updatePromptDisplay(){ + var line = promptText; + var html = ''; + if (column > 0 && line == ''){ + // When we have an empty line just display a cursor. + html = cursor; + } else if (column == promptText.length){ + // We're at the end of the line, so we need to display + // the text *and* cursor. + html = htmlEncode(line) + cursor; + } else { + // Grab the current character, if there is one, and + // make it the current cursor. + var before = line.substring(0, column); + var current = line.substring(column,column+1); + if (current){ + current = + '' + + htmlEncode(current) + + ''; + } + var after = line.substring(column+1); + html = htmlEncode(before) + current + htmlEncode(after); + } + prompt.html(html); + scrollToBottom(); + }; + + // Simple HTML encoding + // Simply replace '<', '>' and '&' + // TODO: Use jQuery's .html() trick, or grab a proper, fast + // HTML encoder. + function htmlEncode(text){ + return ( + text.replace(/&/g,'&') + .replace(/') + ); + }; + + return extern; + }; + // Simple utility for printing messages + $.fn.filledText = function(txt){ + $(this).text(txt); + $(this).html($(this).html().replace(/\n/g,'
')); + return this; + }; +})(jQuery); diff --git a/src/leiningen/fetch_js.clj b/src/leiningen/fetch_js.clj deleted file mode 100644 index bbc9122..0000000 --- a/src/leiningen/fetch_js.clj +++ /dev/null @@ -1,18 +0,0 @@ -(ns leiningen.fetch-js - (:use [leiningen.core :only [prepend-tasks]] - [leiningen.deps :only [deps]]) - (:require [clojure.java.io :as io])) - -(defn fetch-js [project] - (let [js (io/file "resources" "public" "javascript") - jquery (io/file js "jquery-1.4.2.min.js") - console (io/file js "jquery.console.js")] - (when-not (.exists jquery) - (println "Downloading jquery.") - (io/copy (io/reader "http://code.jquery.com/jquery-1.4.2.min.js") jquery)) - (when-not (.exists console) - (println "Downloading jquery-console.") - (io/copy (io/reader "https://raw.github.com/chrisdone/jquery-console/055c2c212944349ddbb045e8536ebfb2192acb80/jquery.console.js") - console)))) - -(prepend-tasks #'deps fetch-js) \ No newline at end of file diff --git a/src/tryclojure/models/eval.clj b/src/tryclojure/models/eval.clj index da9b1f1..09f0d24 100644 --- a/src/tryclojure/models/eval.clj +++ b/src/tryclojure/models/eval.clj @@ -1,8 +1,8 @@ (ns tryclojure.models.eval - (:use [clojail.testers :only [secure-tester-without-def]] - [clojail.core :only [sandbox]] - [clojure.stacktrace :only [root-cause]]) - (:require [noir.session :as session]) + (:require [clojail.testers :refer [secure-tester-without-def]] + [clojail.core :refer [sandbox]] + [clojure.stacktrace :refer [root-cause]] + [noir.session :as session]) (:import java.io.StringWriter java.util.concurrent.TimeoutException)) @@ -23,7 +23,7 @@ (defn make-sandbox [] (sandbox try-clojure-tester :timeout 2000 - :init '(do (use '[clojure.repl :only [doc]]) + :init '(do (require '[clojure.repl :refer [doc]]) (future (Thread/sleep 600000) (-> *ns* .getName remove-ns))))) diff --git a/src/tryclojure/server.clj b/src/tryclojure/server.clj index a606a15..08eeedc 100644 --- a/src/tryclojure/server.clj +++ b/src/tryclojure/server.clj @@ -1,6 +1,6 @@ (ns tryclojure.server - (:use [ring.middleware.file :only [wrap-file]]) - (:require [noir.server :as server])) + (:require [noir.server :as server] + [ring.middleware.file :refer [wrap-file]])) (server/add-middleware wrap-file (System/getProperty "user.dir")) (server/load-views "src/tryclojure/views") diff --git a/src/tryclojure/views/eval.clj b/src/tryclojure/views/eval.clj index 8010a34..f9d0ff4 100644 --- a/src/tryclojure/views/eval.clj +++ b/src/tryclojure/views/eval.clj @@ -1,7 +1,7 @@ (ns tryclojure.views.eval - (:use [noir.core :only [defpage]] - [tryclojure.models.eval :only [eval-request]]) - (:require [noir.response :as resp])) + (:require [noir.core :refer [defpage]] + [tryclojure.models.eval :refer [eval-request]] + [noir.response :as resp])) (defpage "/eval.json" {:keys [expr jsonp]} (let [{:keys [expr result error message] :as res} (eval-request expr) diff --git a/src/tryclojure/views/home.clj b/src/tryclojure/views/home.clj index d1da94b..0efeebb 100644 --- a/src/tryclojure/views/home.clj +++ b/src/tryclojure/views/home.clj @@ -1,6 +1,7 @@ (ns tryclojure.views.home - (:use [noir.core :only [defpartial defpage]] - [hiccup form-helpers page-helpers])) + (:require [noir.core :refer [defpartial defpage]] + [hiccup.element :refer [javascript-tag link-to unordered-list]] + [hiccup.page :refer [include-css include-js html5]])) (defpartial links [] (unordered-list @@ -43,12 +44,12 @@ (link-to "http://github.com/Raynes/tryclojure" "github") "!"]) -(defpartial root-html [] - (html4 +(defn root-html [] + (html5 [:head (include-css "/resources/public/css/tryclojure.css") - (include-js "/resources/public/javascript/jquery-1.4.2.min.js" - "/resources/public/javascript/jquery.console.js" + (include-js "http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" + "/resources/public/javascript/jquery-console/jquery.console.js" "/resources/public/javascript/tryclojure.js") [:title "Try Clojure"]] [:body diff --git a/src/tryclojure/views/tutorial.clj b/src/tryclojure/views/tutorial.clj index 69538ad..f7ee2b2 100644 --- a/src/tryclojure/views/tutorial.clj +++ b/src/tryclojure/views/tutorial.clj @@ -1,5 +1,5 @@ (ns tryclojure.views.tutorial - (:use [noir.core :only [defpage]])) + (:require [noir.core :refer [defpage]])) (defpage [:post "/tutorial"] {n :n} (slurp (str "resources/public/tutorial/page" n ".html"))) \ No newline at end of file From daafa51c63bfc1cd74942ea8b340068b53f46798 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Thu, 28 Jun 2012 16:03:28 -0500 Subject: [PATCH 093/127] Upgrade clojail. --- project.clj | 4 ++-- src/tryclojure/models/eval.clj | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/project.clj b/project.clj index cce6b10..c4bfcb1 100644 --- a/project.clj +++ b/project.clj @@ -1,9 +1,9 @@ (defproject tryclojure "0.1.0-SNAPSHOT" :description "A simple web-based Clojure REPL for trying out Clojure without having to install it." :dependencies [[org.clojure/clojure "1.4.0"] - [noir "1.3.0-beta3"] + [noir "1.3.0-beta10"] [commons-lang/commons-lang "2.5"] - [clojail "0.5.1"]] + [clojail "0.6.0"]] :jvm-opts ["-Djava.security.policy=example.policy""-Xmx80M"] :main tryclojure.server) diff --git a/src/tryclojure/models/eval.clj b/src/tryclojure/models/eval.clj index 09f0d24..50aa8b0 100644 --- a/src/tryclojure/models/eval.clj +++ b/src/tryclojure/models/eval.clj @@ -1,5 +1,5 @@ (ns tryclojure.models.eval - (:require [clojail.testers :refer [secure-tester-without-def]] + (:require [clojail.testers :refer [secure-tester-without-def blanket]] [clojail.core :refer [sandbox]] [clojure.stacktrace :refer [root-cause]] [noir.session :as session]) @@ -17,8 +17,7 @@ (eval-form form sbox))) (def try-clojure-tester - (into secure-tester-without-def - #{'tryclojure.core})) + (blanket secure-tester-without-def "tryclojure")) (defn make-sandbox [] (sandbox try-clojure-tester From 67f8beeb1a826f382efec54a54d2425ac6c317a2 Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Mon, 2 Jul 2012 23:52:30 -0500 Subject: [PATCH 094/127] Inc clojail version. --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index c4bfcb1..8aed13e 100644 --- a/project.clj +++ b/project.clj @@ -3,7 +3,7 @@ :dependencies [[org.clojure/clojure "1.4.0"] [noir "1.3.0-beta10"] [commons-lang/commons-lang "2.5"] - [clojail "0.6.0"]] + [clojail "0.6.1"]] :jvm-opts ["-Djava.security.policy=example.policy""-Xmx80M"] :main tryclojure.server) From 68834e71d9ea955c05f430bafe650bfd5347b34d Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Wed, 5 Sep 2012 16:55:06 -0500 Subject: [PATCH 095/127] Update clojail. --- project.clj | 2 +- src/tryclojure/models/eval.clj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/project.clj b/project.clj index 8aed13e..7ae763e 100644 --- a/project.clj +++ b/project.clj @@ -3,7 +3,7 @@ :dependencies [[org.clojure/clojure "1.4.0"] [noir "1.3.0-beta10"] [commons-lang/commons-lang "2.5"] - [clojail "0.6.1"]] + [clojail "1.0.1"]] :jvm-opts ["-Djava.security.policy=example.policy""-Xmx80M"] :main tryclojure.server) diff --git a/src/tryclojure/models/eval.clj b/src/tryclojure/models/eval.clj index 50aa8b0..9a05b0c 100644 --- a/src/tryclojure/models/eval.clj +++ b/src/tryclojure/models/eval.clj @@ -17,7 +17,7 @@ (eval-form form sbox))) (def try-clojure-tester - (blanket secure-tester-without-def "tryclojure")) + (conj secure-tester-without-def (blanket "tryclojure" "noir"))) (defn make-sandbox [] (sandbox try-clojure-tester From 10644117c4b51f18e12530869bc6ff8abcb0939d Mon Sep 17 00:00:00 2001 From: Trevor Hartman Date: Mon, 1 Oct 2012 11:36:36 -0700 Subject: [PATCH 096/127] Allow source from clojure.repl in addition to doc --- src/tryclojure/models/eval.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tryclojure/models/eval.clj b/src/tryclojure/models/eval.clj index 9a05b0c..810f862 100644 --- a/src/tryclojure/models/eval.clj +++ b/src/tryclojure/models/eval.clj @@ -22,7 +22,7 @@ (defn make-sandbox [] (sandbox try-clojure-tester :timeout 2000 - :init '(do (require '[clojure.repl :refer [doc]]) + :init '(do (require '[clojure.repl :refer [doc source]]) (future (Thread/sleep 600000) (-> *ns* .getName remove-ns))))) @@ -37,4 +37,4 @@ (catch TimeoutException _ {:error true :message "Execution Timed Out!"}) (catch Exception e - {:error true :message (str (root-cause e))}))) \ No newline at end of file + {:error true :message (str (root-cause e))}))) From 6c1ca1f100e24945682308cce2dbb5623b5ccda2 Mon Sep 17 00:00:00 2001 From: "Paolo \"Nusco\" Perrotta" Date: Sat, 24 Nov 2012 01:56:37 +0100 Subject: [PATCH 097/127] add Travis build status to README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 67d773e..c7be349 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ TryClojure is a online Clojure REPL written using Noir and Chris Done's jquery console (you're awesome, Chris). +[](http://travis-ci.org/Raynes/tryclojure) + ## Usage http://tryclj.com From e780a6a831f5d5c933c48c143bbb241012b98166 Mon Sep 17 00:00:00 2001 From: "Paolo \"Nusco\" Perrotta" Date: Sat, 24 Nov 2012 13:56:48 +0100 Subject: [PATCH 098/127] fix indentation in project file --- project.clj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/project.clj b/project.clj index 7ae763e..81f6fb5 100644 --- a/project.clj +++ b/project.clj @@ -2,9 +2,7 @@ :description "A simple web-based Clojure REPL for trying out Clojure without having to install it." :dependencies [[org.clojure/clojure "1.4.0"] [noir "1.3.0-beta10"] - [commons-lang/commons-lang "2.5"] + [commons-lang/commons-lang "2.5"] [clojail "1.0.1"]] :jvm-opts ["-Djava.security.policy=example.policy""-Xmx80M"] :main tryclojure.server) - - From 73ce6f5ef7fd14a9c87bf34545ba5b9a77bb7a64 Mon Sep 17 00:00:00 2001 From: "Paolo \"Nusco\" Perrotta" Date: Sat, 24 Nov 2012 15:15:20 +0100 Subject: [PATCH 099/127] add "Fork me on GitHub" banner --- resources/public/css/gh-fork-ribbon.css | 74 +++++++++++++++++++++++++ src/tryclojure/views/home.clj | 18 +++--- 2 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 resources/public/css/gh-fork-ribbon.css diff --git a/resources/public/css/gh-fork-ribbon.css b/resources/public/css/gh-fork-ribbon.css new file mode 100644 index 0000000..d5c21cf --- /dev/null +++ b/resources/public/css/gh-fork-ribbon.css @@ -0,0 +1,74 @@ +/* From https://github.com/simonwhitaker/github-fork-ribbon-css */ + +/* Left will inherit from right (so we don't need to duplicate code */ +.github-fork-ribbon { + /* The right and left lasses determine the side we attach our banner to */ + position: absolute; + + /* Add a bit of padding to give some substance outside the "stitching" */ + padding: 2px 0; + + /* Set the base colour */ + background-color: #5881d8; + + /* Set a gradient: transparent black at the top to almost-transparent black at the bottom */ + background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0.00)), to(rgba(0, 0, 0, 0.15))); + background-image: -webkit-linear-gradient(top, rgba(0, 0, 0, 0.00), rgba(0, 0, 0, 0.15)); + background-image: -moz-linear-gradient(top, rgba(0, 0, 0, 0.00), rgba(0, 0, 0, 0.15)); + background-image: -o-linear-gradient(top, rgba(0, 0, 0, 0.00), rgba(0, 0, 0, 0.15)); + background-image: -ms-linear-gradient(top, rgba(0, 0, 0, 0.00), rgba(0, 0, 0, 0.15)); + background-image: linear-gradient(top, rgba(0, 0, 0, 0.00), rgba(0, 0, 0, 0.15)); + filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#000000', EndColorStr='#000000'); + + /* Add a drop shadow */ + -webkit-box-shadow: 0px 2px 3px 0px rgba(0, 0, 0, 0.5); + box-shadow: 0px 2px 3px 0px rgba(0, 0, 0, 0.5); +} +.github-fork-ribbon a { + /* Set the font */ + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 13px; + font-weight: 700; + color: white; + + /* Set the text properties */ + text-decoration: none; + text-shadow: 0 -1px rgba(0,0,0,0.5); + text-align: center; + + /* Set the geometry. If you fiddle with these you'll also need to tweak the top and right values in #github-fork-ribbon. */ + width: 200px; + line-height: 20px; + + /* Set the layout properties */ + display: inline-block; + padding: 2px 0; + + /* Add "stitching" effect */ + border-width: 1px 0; + border-style: dotted; + border-color: rgba(255,255,255,0.7); +} + +.github-fork-ribbon-wrapper { + width: 150px; + height: 150px; + position: absolute; + overflow: hidden; + top: 0; +} + +.github-fork-ribbon-wrapper.right { + right: 0; +} + +.github-fork-ribbon-wrapper.right .github-fork-ribbon { + top: 42px; + right: -43px; + + /* Rotate the banner 45 degrees */ + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -o-transform: rotate(45deg); + transform: rotate(45deg); +} diff --git a/src/tryclojure/views/home.clj b/src/tryclojure/views/home.clj index 0efeebb..b63e4dd 100644 --- a/src/tryclojure/views/home.clj +++ b/src/tryclojure/views/home.clj @@ -23,10 +23,6 @@ "The sandbox wipes defs if you def too many things, so don't be surprised. Furthermore, " "The sandbox will automatically be wiped after 15 minutes and if you evaluate more after that," "It'll be in an entirely new namespace/sandbox."] - [:p.bottom - "You can find the site's source and such on its " - (link-to "http://github.com/Raynes/tryclojure" "github") - " page."] [:p.bottom "TryClojure is written in Clojure and JavaScript (JQuery), powered by " (link-to "https://github.com/flatland/clojail" "clojail") @@ -38,22 +34,24 @@ [:p.bottom "Welcome to Try Clojure. See that little box up there? That's a Clojure repl. You can type " "expressions and see their results right here in your browser. We also have a brief tutorial to " - "give you a taste of Clojure. Try it out by typing " [:code.expr "tutorial"] " in the console!"] - [:p.bottom - "Check out the site's source on " - (link-to "http://github.com/Raynes/tryclojure" "github") - "!"]) + "give you a taste of Clojure. Try it out by typing " [:code.expr "tutorial"] " in the console!"]) (defn root-html [] (html5 [:head - (include-css "/resources/public/css/tryclojure.css") + (include-css "/resources/public/css/tryclojure.css" + "/resources/public/css/gh-fork-ribbon.css") (include-js "http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" "/resources/public/javascript/jquery-console/jquery.console.js" "/resources/public/javascript/tryclojure.js") [:title "Try Clojure"]] [:body [:div#wrapper + [:div.github-fork-ribbon-wrapper.right + [:div.github-fork-ribbon + (link-to "https://github.com/Raynes/tryclojure" "Fork me on GitHub") + ] + ] [:div#content [:div#header [:h1 From d4c1e2acfcd8b982069c26109514f943828066aa Mon Sep 17 00:00:00 2001 From: "Paolo \"Nusco\" Perrotta" Date: Sat, 24 Nov 2012 15:20:37 +0100 Subject: [PATCH 100/127] .gitignore lein deps file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5fcc507..102c661 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ pom.xml lib classes *~ +.lein-deps-sum From c4003a295849e6e51a88343bbc505ac233a25ddd Mon Sep 17 00:00:00 2001 From: "Paolo \"Nusco\" Perrotta" Date: Sat, 24 Nov 2012 15:20:58 +0100 Subject: [PATCH 101/127] clean up copyright line --- src/tryclojure/views/home.clj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/tryclojure/views/home.clj b/src/tryclojure/views/home.clj index b63e4dd..0f2b03b 100644 --- a/src/tryclojure/views/home.clj +++ b/src/tryclojure/views/home.clj @@ -65,9 +65,7 @@ [:a#about.buttons.last "about"]] [:div#changer (home-html)]] [:div.footer - [:p.bottom "©2011 Anthony Grimes and numerous contributors"] - [:p.bottom - "Built with " + [:p.bottom "©2011-2012 Anthony Grimes and numerous contributors. Built with " (link-to "http://webnoir.org" "Noir") "."]] (javascript-tag From 62c2706eb67320c274313fcf2e09a7b49ebe4dd8 Mon Sep 17 00:00:00 2001 From: "Paolo \"Nusco\" Perrotta" Date: Sat, 24 Nov 2012 18:27:37 +0100 Subject: [PATCH 102/127] change text --- resources/public/javascript/tryclojure.js | 4 +-- resources/public/tutorial/page1.html | 12 ++----- resources/public/tutorial/page10.html | 35 +++++++++++-------- resources/public/tutorial/page11.html | 13 ++++--- resources/public/tutorial/page2.html | 7 ++-- resources/public/tutorial/page3.html | 13 +++---- resources/public/tutorial/page4.html | 8 ++--- resources/public/tutorial/page5.html | 9 ++--- resources/public/tutorial/page6.html | 9 ++--- resources/public/tutorial/page7.html | 13 +++---- resources/public/tutorial/page8.html | 17 +++++---- resources/public/tutorial/page9.html | 16 +++++---- src/tryclojure/views/home.clj | 42 ++++++++++------------- 13 files changed, 97 insertions(+), 101 deletions(-) diff --git a/resources/public/javascript/tryclojure.js b/resources/public/javascript/tryclojure.js index 4e04303..1e0256c 100644 --- a/resources/public/javascript/tryclojure.js +++ b/resources/public/javascript/tryclojure.js @@ -166,8 +166,8 @@ var controller; $(document).ready(function() { controller = $("#console").console({ - welcomeMessage:'Enter some Clojure code to be evaluated.', - promptLabel: 'Clojure> ', + welcomeMessage:'Give me some Clojure:', + promptLabel: '> ', commandValidate: onValidate, commandHandle: onHandle, autofocus:true, diff --git a/resources/public/tutorial/page1.html b/resources/public/tutorial/page1.html index 7ffd325..4654fcb 100644 --- a/resources/public/tutorial/page1.html +++ b/resources/public/tutorial/page1.html @@ -1,15 +1,7 @@

- This tutorial isn't entirely complete yet. More fun content is to be written. Stay tuned. + I'll take you on a 5-minutes tour of Clojure, but feel free to experiment on your own along the road!

- Above, you have your REPL. Please try the examples as we go along, and by all means experiment - with concepts independently. Should you refuse, you will promptly be IP banned from this - website, and a teddy bear will eat your candies. ctrl+v to paste and ctrl+c to copy like you - would normally expect, but you can't paste with your browser's right-click menu. -

- -

- You can type 'next' to move forward, 'back' to go back to the previous step, and 'restart' to - restart the tutorial. Go ahead and type 'next' to begin + You can type next to skip forward, back to return to the previous step, and restart to get back to the beginning. Let's get started: type next.

diff --git a/resources/public/tutorial/page10.html b/resources/public/tutorial/page10.html index f19c97b..5b43a30 100644 --- a/resources/public/tutorial/page10.html +++ b/resources/public/tutorial/page10.html @@ -1,30 +1,35 @@ -

Awesome. Now you can call this function just like we called the old square function.

+

+ Success! Now you can call this new square function just like you called the old square function. +

- Clojure has a great set of immutable and persistent data structures. Vectors, lists, maps, sets, - I get chills. + By now, you know that lists are quite important in Clojure. + But Clojure also has other data structures:

Vectors: [1 2 3 4]
Maps: {:foo "bar" 3 4}
- Lists: '(1 2 3 4)
Sets: #{1 2 3 4}
- Vectors and lists are sequential and ordered collections. You'll see vectors used much more than lists. - Maps are typical hash-maps - unordered collections indexed by keys. The keys can be any object. Here, - we've used a keyword, :foo as a key. Keywords make excellent keys. We also used a number. - Sets are mathematical sets.

- Clojure's collections are one of the most imporant parts of Clojure. Being a functional language, Clojure - encourages immutability and as little state as possible. Therefore, instead of for loops mutating variables - and such, most of the time you'll see higher order functions doing transformations on immutable data and - returning new collections rather than ever modifying the old one. + Vectors and lists are sequential and ordered collections. + Sets are not ordered, and they cannot contain duplicate elements. + Maps are key-value collections, where the keys can be any object. + Here, we've used what Clojure calls a keyword (:foo) for one of the keys, and a number for the other key. +

+ +

+ Now I'll tell you another thing that may surprise you: Clojure collections are immutable - they can never change. + When you do anything on a list, including adding and removing elements, you actually get a brand new list. + (Fortunately, Clojure is amazingly efficient at creating new lists). + In general, Clojure encourages you to have as little mutable state as possible. + For example, instead of "for" loops and other state-changing constructs, most of the time you'll see functions doing transformations on immutable data and returning new collections, without changing the old one.

- A prime example of this is map. We can use map, a higher order function (which is a function - that takes functions as arguments or returns functions), to 'map' a function to every element of a sequence. - Let's use this to increment each number in a vector. Type (map inc [1 2 3 4]) to continue. + A prime example of this is map. map is a higher order function, which means that it takes another function as an argument. + For example, you can ask map to increment each number in a vector by passing it the inc function, followed by the vector. + Try it for yourself: type (map inc [1 2 3 4]) to continue.

diff --git a/resources/public/tutorial/page11.html b/resources/public/tutorial/page11.html index da67d0a..726fe3c 100644 --- a/resources/public/tutorial/page11.html +++ b/resources/public/tutorial/page11.html @@ -1,8 +1,11 @@ -

Excellent work.

+

Great job!

- Well, that's all there is right now. This tutorial is still a work in progress, and I'm working on more - steps. If anybody wants to contribute, you can find a link to the Github repository on the 'about' page. - Furthermore, if you're just a brand new Clojure developer looking for some learning experiences, check - out the 'links' page. You'll find links to some tutorials and such there. + We've only scratched the surface of Clojure and its mind-bending power. + This tutorial is still a work in progress, and I'm working on more steps. + Meanwhile, you can learn more about Clojure by visiting the 'links' page. +

+ +

+ Welcome to your adventures in Clojure, and be prepared to be surprised and delighted every step of the way!

diff --git a/resources/public/tutorial/page2.html b/resources/public/tutorial/page2.html index acd317f..1d11c62 100644 --- a/resources/public/tutorial/page2.html +++ b/resources/public/tutorial/page2.html @@ -1,6 +1,7 @@

- I bet you're curious to find out what Clojure code looks like, aren't you? Sure you are. - Well, I've give you a hint: it's made up of lists. Let's do some arithmetic. + The first thing you may notice about Clojure is that common operations look... strange.

-(+ 3 3) +

+ For example, try typing (+ 3 3) in the REPL. +

diff --git a/resources/public/tutorial/page3.html b/resources/public/tutorial/page3.html index 925f30e..4edd21e 100644 --- a/resources/public/tutorial/page3.html +++ b/resources/public/tutorial/page3.html @@ -1,12 +1,13 @@

- Excellent work! There are other arithmetic functions. + That was a strange way to say "three plus three", wasn't it?

-* -- -/

- Try them out. Save / for last (don't worry, I have a reason!). + A Clojure program is made of lists. + (+ 3 3) is a list that contains an operator, and then the operands. + Try out the same concept with the * and - operators.

-

Once you're finished playing around, try (/ 10 3).

+

+ Division might surprise you. When you're ready to move forward, try (/ 10 3). +

diff --git a/resources/public/tutorial/page4.html b/resources/public/tutorial/page4.html index 0d06d50..991dc20 100644 --- a/resources/public/tutorial/page4.html +++ b/resources/public/tutorial/page4.html @@ -1,8 +1,4 @@

- I bet that caught you by surprise, didn't it? Don't fear! Clojure simply has a built in Rational type. - We can prove that we've got a rational by running (type (/ 10 3)). Rationals are more - concise and precise than floating point numbers. However, we can force Clojure to do floating point - division by just making one of our numbers floating point. + Now, that was a bit surprising: Clojure has a built in Rational type. + You can still force Clojure to do floating point division by making one of the operands floating point: type (/ 10 3.0) to continue.

- -

Type (/ 10 3.0) to continue.

diff --git a/resources/public/tutorial/page5.html b/resources/public/tutorial/page5.html index 5f40ebb..1788c09 100644 --- a/resources/public/tutorial/page5.html +++ b/resources/public/tutorial/page5.html @@ -1,11 +1,6 @@

Awesome!

- Another neat thing about Clojure is that functions can take an arbitrary number of arguments. - Functions are allowed to specify a 'catch-all' to put an optional and arbitrary number of arguments - into. Because of this, a lot of Clojure's core functions have interesting capabilities. For example - the arithmetic functions we've already played with are not limited to two arguments! No sir, they can - take any number of arguments they wish. + Many Clojure functions can take an arbitrary number of arguments. + Try it out: type (+ 1 2 3 4 5 6) to continue.

- -

Try it out. Type (+ 1 2 3 4 5 6) to continue.

diff --git a/resources/public/tutorial/page6.html b/resources/public/tutorial/page6.html index 0b55ee3..daa7698 100644 --- a/resources/public/tutorial/page6.html +++ b/resources/public/tutorial/page6.html @@ -1,7 +1,8 @@ -

Alright, that's enough math. Let's do some fun stuff, like write functions.

-

- You can define functions in Clojure with defn + That's enough math. Let's do some fun stuff, like defining functions. + You can do that in Clojure with defn.

-

Type (defn square [x] (* x x)) to continue!

+

+ Type (defn square [x] (* x x)) to define a "square" function that takes a single number and squares it. +

diff --git a/resources/public/tutorial/page7.html b/resources/public/tutorial/page7.html index cf0f574..e6c58c0 100644 --- a/resources/public/tutorial/page7.html +++ b/resources/public/tutorial/page7.html @@ -1,12 +1,13 @@ -

Oh boy! We wrote our very own function! It's a lovely one, isn't it?

+

Congratulations - you just defined your first Clojure function. Many more will follow!

- Our square function takes a single argument -- a number -- and squares it. + defn takes the name of the function, then the list of arguments, and then the body of the function. + I told you that a Clojure program is made of lists, right? + The entire defn is a list, and the function body is also a list. + (Even the arguments are collected in a vector, which is similar to a list - we'll talk about vectors soon).

- Take a look at how our defn form looks. First comes the name of the function, then the argument list - (which is a vector, and not an actual list), then the body of the function. + Oh, sorry for talking so long - you probably want to try out your brand new function! + Type (square 10).

- -

But wait! We don't even know if our function works or not! Let's try it out. Type (square 10).

diff --git a/resources/public/tutorial/page8.html b/resources/public/tutorial/page8.html index 075857f..878d1a4 100644 --- a/resources/public/tutorial/page8.html +++ b/resources/public/tutorial/page8.html @@ -1,17 +1,20 @@

Yay! It works!

- You know, Clojure is a functional programming language. As such, it has first-class and - anonymous functions. Let's write our square function as an anonymous function. + By now, you probably think that Clojure is very different from the programming languages you already know. + Indeed, it belongs to a different family than most popular languages' - the family of "functional" programming languages. + Like most functional languages, Clojure can define a function without even giving it a name:

+ (fn [x] (* x x)) +

- If you run this in the REPL above (as you should), you'll note that some very weird and - cryptic thing is printed. Functions are just normal values like a number, a string, or - anything else. The cryptic thing is simply how they look when printed. + If you run this code, you'll see some cryptic output. + In Clojures, functions are just normal values like numbers or strings. + fn defines a function and then returns it. + What you're seeing is simply what a function looks like when you print it on the screen.

- Our anonymous function isn't very useful if we don't call it. Let's do it. Type - ((fn [x] (* x x)) 10) + But wait - an anonymous function isn't very useful if you can't call it. Try to define a new anonymous function and call it straight away: ((fn [x] (* x x)) 10).

diff --git a/resources/public/tutorial/page9.html b/resources/public/tutorial/page9.html index d6e81da..8679f37 100644 --- a/resources/public/tutorial/page9.html +++ b/resources/public/tutorial/page9.html @@ -1,12 +1,16 @@

- Yay! Notice how we called our anonymous function? We just wrapped the function in parentheses, - placing it as the first element in this new list and passing it arguments just like we did earlier - with the arithmetic functions. Awesome, huh? + Let's see what you just did: you evaluated a list where the first element is the function itself, defined on the spot - and the other elements are the arguments that you pass to the function. + That's exactly the same syntax that you used earlier on to call functions like square or even +. + The only difference is that now you defined the function in the same place where you called it.

- You may not know this, but defn is actually just a bit of sugar around def - and fn to create named functions. We can create named functions without defn + Remember defn? + Now I can tell you a secret: defn is actually just a bit of syntactic sugar around def and fn. + You've just seen fn at work: it defines a new function. + def binds the newly defined function to a name.

-

Type (def square (fn [x] (* x x))) to continue.

+

+ If you want, you can create a named functions without using defn: type (def square (fn [x] (* x x))) to continue. +

diff --git a/src/tryclojure/views/home.clj b/src/tryclojure/views/home.clj index 0efeebb..1c67091 100644 --- a/src/tryclojure/views/home.clj +++ b/src/tryclojure/views/home.clj @@ -15,34 +15,32 @@ (defpartial about-html [] [:p.bottom - "Please note that this REPL is sandboxed, so you wont be able to do everything in it " - "that you would in a local unsandboxed REPL. Keep in mind that this site is designed for " - "beginners to try out Clojure and not necessarily as a general-purpose server-side REPL."] + "Welcome to Try Clojure - a quick tour of Clojure for absolute beginners." + ] [:p.bottom - "One quirk you might run into is that things you bind with def can sometimes disappear. " - "The sandbox wipes defs if you def too many things, so don't be surprised. Furthermore, " - "The sandbox will automatically be wiped after 15 minutes and if you evaluate more after that," - "It'll be in an entirely new namespace/sandbox."] + "Here is our only disclaimer: this site is an introduction to Clojure, not a generic Clojure REPL. " + "You won't be able to do everything in it that you could do in your local interpreter. " + "Also, the interpreter deletes the data that you enter if you define too many things, or after 15 minutes."] [:p.bottom "You can find the site's source and such on its " (link-to "http://github.com/Raynes/tryclojure" "github") " page."] [:p.bottom - "TryClojure is written in Clojure and JavaScript (JQuery), powered by " - (link-to "https://github.com/flatland/clojail" "clojail") - " and Chris Done's " - (link-to "https://github.com/chrisdone/jquery-console" "jquery-console")] - [:p.bottom "Design by " (link-to "http://apgwoz.com" "Andrew Gwozdziewycz")]) + "TryClojure is written in Clojure and JavaScript with " + (link-to "http://webnoir.org" "Noir") ", " + (link-to "https://github.com/flatland/clojail" "clojail") ", and Chris Done's " + (link-to "https://github.com/chrisdone/jquery-console" "jquery-console") ". " + " The design is by " (link-to "http://apgwoz.com" "Andrew Gwozdziewycz") "." + ]) (defpartial home-html [] [:p.bottom - "Welcome to Try Clojure. See that little box up there? That's a Clojure repl. You can type " - "expressions and see their results right here in your browser. We also have a brief tutorial to " - "give you a taste of Clojure. Try it out by typing " [:code.expr "tutorial"] " in the console!"] - [:p.bottom - "Check out the site's source on " - (link-to "http://github.com/Raynes/tryclojure" "github") - "!"]) + "Welcome to Clojure! " + "You can see a Clojure interpreter above - we call it a REPL." + ] + [:p.bottom + "Type \"tutorial\" in the REPL to begin." + ]) (defn root-html [] (html5 @@ -67,11 +65,7 @@ [:a#about.buttons.last "about"]] [:div#changer (home-html)]] [:div.footer - [:p.bottom "©2011 Anthony Grimes and numerous contributors"] - [:p.bottom - "Built with " - (link-to "http://webnoir.org" "Noir") - "."]] + [:p.bottom "©2011-2012 Anthony Grimes and numerous contributors."]] (javascript-tag "var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-27340918-1']); From 7327047e7b8960b1ecce115eee05a9e260d00be3 Mon Sep 17 00:00:00 2001 From: "Paolo \"Nusco\" Perrotta" Date: Sat, 24 Nov 2012 18:57:42 +0100 Subject: [PATCH 103/127] fix typo in html tags (they're styled out anyway, but that may change) --- resources/public/tutorial/page10.html | 6 +++--- resources/public/tutorial/page3.html | 2 +- src/tryclojure/views/home.clj | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/public/tutorial/page10.html b/resources/public/tutorial/page10.html index 5b43a30..f1e2228 100644 --- a/resources/public/tutorial/page10.html +++ b/resources/public/tutorial/page10.html @@ -17,11 +17,11 @@ Vectors and lists are sequential and ordered collections. Sets are not ordered, and they cannot contain duplicate elements. Maps are key-value collections, where the keys can be any object. - Here, we've used what Clojure calls a keyword (:foo) for one of the keys, and a number for the other key. + Here, we've used what Clojure calls a keyword (:foo) for one of the keys, and a number for the other key.

- Now I'll tell you another thing that may surprise you: Clojure collections are immutable - they can never change. + Now I'll tell you another thing that may surprise you: Clojure collections are immutable - they can never change. When you do anything on a list, including adding and removing elements, you actually get a brand new list. (Fortunately, Clojure is amazingly efficient at creating new lists). In general, Clojure encourages you to have as little mutable state as possible. @@ -29,7 +29,7 @@

- A prime example of this is map. map is a higher order function, which means that it takes another function as an argument. + A prime example of this is map. map is a higher order function, which means that it takes another function as an argument. For example, you can ask map to increment each number in a vector by passing it the inc function, followed by the vector. Try it for yourself: type (map inc [1 2 3 4]) to continue.

diff --git a/resources/public/tutorial/page3.html b/resources/public/tutorial/page3.html index 4edd21e..ae8cc9b 100644 --- a/resources/public/tutorial/page3.html +++ b/resources/public/tutorial/page3.html @@ -3,7 +3,7 @@

- A Clojure program is made of lists. + A Clojure program is made of lists. (+ 3 3) is a list that contains an operator, and then the operands. Try out the same concept with the * and - operators.

diff --git a/src/tryclojure/views/home.clj b/src/tryclojure/views/home.clj index 1c67091..ad758dd 100644 --- a/src/tryclojure/views/home.clj +++ b/src/tryclojure/views/home.clj @@ -36,7 +36,7 @@ (defpartial home-html [] [:p.bottom "Welcome to Clojure! " - "You can see a Clojure interpreter above - we call it a REPL." + "You can see a Clojure interpreter above - we call it a REPL." ] [:p.bottom "Type \"tutorial\" in the REPL to begin." From 3815723323b56c3321dbaf9e2dd789b10c64f408 Mon Sep 17 00:00:00 2001 From: "Paolo \"Nusco\" Perrotta" Date: Sat, 24 Nov 2012 19:01:30 +0100 Subject: [PATCH 104/127] fix typo --- resources/public/tutorial/page8.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/public/tutorial/page8.html b/resources/public/tutorial/page8.html index 878d1a4..d3a26cf 100644 --- a/resources/public/tutorial/page8.html +++ b/resources/public/tutorial/page8.html @@ -10,7 +10,7 @@

If you run this code, you'll see some cryptic output. - In Clojures, functions are just normal values like numbers or strings. + In Clojure, functions are just normal values like numbers or strings. fn defines a function and then returns it. What you're seeing is simply what a function looks like when you print it on the screen.

From ded1e42964d8fd54d8f0ea89fee190d2b56ddb84 Mon Sep 17 00:00:00 2001 From: "Paolo \\\"Nusco\\\" Perrotta" Date: Sat, 24 Nov 2012 18:27:37 +0100 Subject: [PATCH 105/127] change text --- resources/public/javascript/tryclojure.js | 4 +-- resources/public/tutorial/page1.html | 12 ++----- resources/public/tutorial/page10.html | 35 +++++++++++--------- resources/public/tutorial/page11.html | 13 +++++--- resources/public/tutorial/page2.html | 7 ++-- resources/public/tutorial/page3.html | 13 ++++---- resources/public/tutorial/page4.html | 8 ++--- resources/public/tutorial/page5.html | 9 ++--- resources/public/tutorial/page6.html | 9 ++--- resources/public/tutorial/page7.html | 13 ++++---- resources/public/tutorial/page8.html | 17 ++++++---- resources/public/tutorial/page9.html | 16 +++++---- src/tryclojure/views/home.clj | 40 +++++++++++++---------- 13 files changed, 101 insertions(+), 95 deletions(-) diff --git a/resources/public/javascript/tryclojure.js b/resources/public/javascript/tryclojure.js index 4e04303..1e0256c 100644 --- a/resources/public/javascript/tryclojure.js +++ b/resources/public/javascript/tryclojure.js @@ -166,8 +166,8 @@ var controller; $(document).ready(function() { controller = $("#console").console({ - welcomeMessage:'Enter some Clojure code to be evaluated.', - promptLabel: 'Clojure> ', + welcomeMessage:'Give me some Clojure:', + promptLabel: '> ', commandValidate: onValidate, commandHandle: onHandle, autofocus:true, diff --git a/resources/public/tutorial/page1.html b/resources/public/tutorial/page1.html index 7ffd325..4654fcb 100644 --- a/resources/public/tutorial/page1.html +++ b/resources/public/tutorial/page1.html @@ -1,15 +1,7 @@

- This tutorial isn't entirely complete yet. More fun content is to be written. Stay tuned. + I'll take you on a 5-minutes tour of Clojure, but feel free to experiment on your own along the road!

- Above, you have your REPL. Please try the examples as we go along, and by all means experiment - with concepts independently. Should you refuse, you will promptly be IP banned from this - website, and a teddy bear will eat your candies. ctrl+v to paste and ctrl+c to copy like you - would normally expect, but you can't paste with your browser's right-click menu. -

- -

- You can type 'next' to move forward, 'back' to go back to the previous step, and 'restart' to - restart the tutorial. Go ahead and type 'next' to begin + You can type next to skip forward, back to return to the previous step, and restart to get back to the beginning. Let's get started: type next.

diff --git a/resources/public/tutorial/page10.html b/resources/public/tutorial/page10.html index f19c97b..5b43a30 100644 --- a/resources/public/tutorial/page10.html +++ b/resources/public/tutorial/page10.html @@ -1,30 +1,35 @@ -

Awesome. Now you can call this function just like we called the old square function.

+

+ Success! Now you can call this new square function just like you called the old square function. +

- Clojure has a great set of immutable and persistent data structures. Vectors, lists, maps, sets, - I get chills. + By now, you know that lists are quite important in Clojure. + But Clojure also has other data structures:

Vectors: [1 2 3 4]
Maps: {:foo "bar" 3 4}
- Lists: '(1 2 3 4)
Sets: #{1 2 3 4}
- Vectors and lists are sequential and ordered collections. You'll see vectors used much more than lists. - Maps are typical hash-maps - unordered collections indexed by keys. The keys can be any object. Here, - we've used a keyword, :foo as a key. Keywords make excellent keys. We also used a number. - Sets are mathematical sets.

- Clojure's collections are one of the most imporant parts of Clojure. Being a functional language, Clojure - encourages immutability and as little state as possible. Therefore, instead of for loops mutating variables - and such, most of the time you'll see higher order functions doing transformations on immutable data and - returning new collections rather than ever modifying the old one. + Vectors and lists are sequential and ordered collections. + Sets are not ordered, and they cannot contain duplicate elements. + Maps are key-value collections, where the keys can be any object. + Here, we've used what Clojure calls a keyword (:foo) for one of the keys, and a number for the other key. +

+ +

+ Now I'll tell you another thing that may surprise you: Clojure collections are immutable - they can never change. + When you do anything on a list, including adding and removing elements, you actually get a brand new list. + (Fortunately, Clojure is amazingly efficient at creating new lists). + In general, Clojure encourages you to have as little mutable state as possible. + For example, instead of "for" loops and other state-changing constructs, most of the time you'll see functions doing transformations on immutable data and returning new collections, without changing the old one.

- A prime example of this is map. We can use map, a higher order function (which is a function - that takes functions as arguments or returns functions), to 'map' a function to every element of a sequence. - Let's use this to increment each number in a vector. Type (map inc [1 2 3 4]) to continue. + A prime example of this is map. map is a higher order function, which means that it takes another function as an argument. + For example, you can ask map to increment each number in a vector by passing it the inc function, followed by the vector. + Try it for yourself: type (map inc [1 2 3 4]) to continue.

diff --git a/resources/public/tutorial/page11.html b/resources/public/tutorial/page11.html index da67d0a..726fe3c 100644 --- a/resources/public/tutorial/page11.html +++ b/resources/public/tutorial/page11.html @@ -1,8 +1,11 @@ -

Excellent work.

+

Great job!

- Well, that's all there is right now. This tutorial is still a work in progress, and I'm working on more - steps. If anybody wants to contribute, you can find a link to the Github repository on the 'about' page. - Furthermore, if you're just a brand new Clojure developer looking for some learning experiences, check - out the 'links' page. You'll find links to some tutorials and such there. + We've only scratched the surface of Clojure and its mind-bending power. + This tutorial is still a work in progress, and I'm working on more steps. + Meanwhile, you can learn more about Clojure by visiting the 'links' page. +

+ +

+ Welcome to your adventures in Clojure, and be prepared to be surprised and delighted every step of the way!

diff --git a/resources/public/tutorial/page2.html b/resources/public/tutorial/page2.html index acd317f..1d11c62 100644 --- a/resources/public/tutorial/page2.html +++ b/resources/public/tutorial/page2.html @@ -1,6 +1,7 @@

- I bet you're curious to find out what Clojure code looks like, aren't you? Sure you are. - Well, I've give you a hint: it's made up of lists. Let's do some arithmetic. + The first thing you may notice about Clojure is that common operations look... strange.

-(+ 3 3) +

+ For example, try typing (+ 3 3) in the REPL. +

diff --git a/resources/public/tutorial/page3.html b/resources/public/tutorial/page3.html index 925f30e..4edd21e 100644 --- a/resources/public/tutorial/page3.html +++ b/resources/public/tutorial/page3.html @@ -1,12 +1,13 @@

- Excellent work! There are other arithmetic functions. + That was a strange way to say "three plus three", wasn't it?

-* -- -/

- Try them out. Save / for last (don't worry, I have a reason!). + A Clojure program is made of lists. + (+ 3 3) is a list that contains an operator, and then the operands. + Try out the same concept with the * and - operators.

-

Once you're finished playing around, try (/ 10 3).

+

+ Division might surprise you. When you're ready to move forward, try (/ 10 3). +

diff --git a/resources/public/tutorial/page4.html b/resources/public/tutorial/page4.html index 0d06d50..991dc20 100644 --- a/resources/public/tutorial/page4.html +++ b/resources/public/tutorial/page4.html @@ -1,8 +1,4 @@

- I bet that caught you by surprise, didn't it? Don't fear! Clojure simply has a built in Rational type. - We can prove that we've got a rational by running (type (/ 10 3)). Rationals are more - concise and precise than floating point numbers. However, we can force Clojure to do floating point - division by just making one of our numbers floating point. + Now, that was a bit surprising: Clojure has a built in Rational type. + You can still force Clojure to do floating point division by making one of the operands floating point: type (/ 10 3.0) to continue.

- -

Type (/ 10 3.0) to continue.

diff --git a/resources/public/tutorial/page5.html b/resources/public/tutorial/page5.html index 5f40ebb..1788c09 100644 --- a/resources/public/tutorial/page5.html +++ b/resources/public/tutorial/page5.html @@ -1,11 +1,6 @@

Awesome!

- Another neat thing about Clojure is that functions can take an arbitrary number of arguments. - Functions are allowed to specify a 'catch-all' to put an optional and arbitrary number of arguments - into. Because of this, a lot of Clojure's core functions have interesting capabilities. For example - the arithmetic functions we've already played with are not limited to two arguments! No sir, they can - take any number of arguments they wish. + Many Clojure functions can take an arbitrary number of arguments. + Try it out: type (+ 1 2 3 4 5 6) to continue.

- -

Try it out. Type (+ 1 2 3 4 5 6) to continue.

diff --git a/resources/public/tutorial/page6.html b/resources/public/tutorial/page6.html index 0b55ee3..daa7698 100644 --- a/resources/public/tutorial/page6.html +++ b/resources/public/tutorial/page6.html @@ -1,7 +1,8 @@ -

Alright, that's enough math. Let's do some fun stuff, like write functions.

-

- You can define functions in Clojure with defn + That's enough math. Let's do some fun stuff, like defining functions. + You can do that in Clojure with defn.

-

Type (defn square [x] (* x x)) to continue!

+

+ Type (defn square [x] (* x x)) to define a "square" function that takes a single number and squares it. +

diff --git a/resources/public/tutorial/page7.html b/resources/public/tutorial/page7.html index cf0f574..e6c58c0 100644 --- a/resources/public/tutorial/page7.html +++ b/resources/public/tutorial/page7.html @@ -1,12 +1,13 @@ -

Oh boy! We wrote our very own function! It's a lovely one, isn't it?

+

Congratulations - you just defined your first Clojure function. Many more will follow!

- Our square function takes a single argument -- a number -- and squares it. + defn takes the name of the function, then the list of arguments, and then the body of the function. + I told you that a Clojure program is made of lists, right? + The entire defn is a list, and the function body is also a list. + (Even the arguments are collected in a vector, which is similar to a list - we'll talk about vectors soon).

- Take a look at how our defn form looks. First comes the name of the function, then the argument list - (which is a vector, and not an actual list), then the body of the function. + Oh, sorry for talking so long - you probably want to try out your brand new function! + Type (square 10).

- -

But wait! We don't even know if our function works or not! Let's try it out. Type (square 10).

diff --git a/resources/public/tutorial/page8.html b/resources/public/tutorial/page8.html index 075857f..878d1a4 100644 --- a/resources/public/tutorial/page8.html +++ b/resources/public/tutorial/page8.html @@ -1,17 +1,20 @@

Yay! It works!

- You know, Clojure is a functional programming language. As such, it has first-class and - anonymous functions. Let's write our square function as an anonymous function. + By now, you probably think that Clojure is very different from the programming languages you already know. + Indeed, it belongs to a different family than most popular languages' - the family of "functional" programming languages. + Like most functional languages, Clojure can define a function without even giving it a name:

+ (fn [x] (* x x)) +

- If you run this in the REPL above (as you should), you'll note that some very weird and - cryptic thing is printed. Functions are just normal values like a number, a string, or - anything else. The cryptic thing is simply how they look when printed. + If you run this code, you'll see some cryptic output. + In Clojures, functions are just normal values like numbers or strings. + fn defines a function and then returns it. + What you're seeing is simply what a function looks like when you print it on the screen.

- Our anonymous function isn't very useful if we don't call it. Let's do it. Type - ((fn [x] (* x x)) 10) + But wait - an anonymous function isn't very useful if you can't call it. Try to define a new anonymous function and call it straight away: ((fn [x] (* x x)) 10).

diff --git a/resources/public/tutorial/page9.html b/resources/public/tutorial/page9.html index d6e81da..8679f37 100644 --- a/resources/public/tutorial/page9.html +++ b/resources/public/tutorial/page9.html @@ -1,12 +1,16 @@

- Yay! Notice how we called our anonymous function? We just wrapped the function in parentheses, - placing it as the first element in this new list and passing it arguments just like we did earlier - with the arithmetic functions. Awesome, huh? + Let's see what you just did: you evaluated a list where the first element is the function itself, defined on the spot - and the other elements are the arguments that you pass to the function. + That's exactly the same syntax that you used earlier on to call functions like square or even +. + The only difference is that now you defined the function in the same place where you called it.

- You may not know this, but defn is actually just a bit of sugar around def - and fn to create named functions. We can create named functions without defn + Remember defn? + Now I can tell you a secret: defn is actually just a bit of syntactic sugar around def and fn. + You've just seen fn at work: it defines a new function. + def binds the newly defined function to a name.

-

Type (def square (fn [x] (* x x))) to continue.

+

+ If you want, you can create a named functions without using defn: type (def square (fn [x] (* x x))) to continue. +

diff --git a/src/tryclojure/views/home.clj b/src/tryclojure/views/home.clj index 0f2b03b..36701c2 100644 --- a/src/tryclojure/views/home.clj +++ b/src/tryclojure/views/home.clj @@ -15,26 +15,32 @@ (defpartial about-html [] [:p.bottom - "Please note that this REPL is sandboxed, so you wont be able to do everything in it " - "that you would in a local unsandboxed REPL. Keep in mind that this site is designed for " - "beginners to try out Clojure and not necessarily as a general-purpose server-side REPL."] + "Welcome to Try Clojure - a quick tour of Clojure for absolute beginners." + ] [:p.bottom - "One quirk you might run into is that things you bind with def can sometimes disappear. " - "The sandbox wipes defs if you def too many things, so don't be surprised. Furthermore, " - "The sandbox will automatically be wiped after 15 minutes and if you evaluate more after that," - "It'll be in an entirely new namespace/sandbox."] + "Here is our only disclaimer: this site is an introduction to Clojure, not a generic Clojure REPL. " + "You won't be able to do everything in it that you could do in your local interpreter. " + "Also, the interpreter deletes the data that you enter if you define too many things, or after 15 minutes."] [:p.bottom - "TryClojure is written in Clojure and JavaScript (JQuery), powered by " - (link-to "https://github.com/flatland/clojail" "clojail") - " and Chris Done's " - (link-to "https://github.com/chrisdone/jquery-console" "jquery-console")] - [:p.bottom "Design by " (link-to "http://apgwoz.com" "Andrew Gwozdziewycz")]) + "You can find the site's source and such on its " + (link-to "http://github.com/Raynes/tryclojure" "github") + " page."] + [:p.bottom + "TryClojure is written in Clojure and JavaScript with " + (link-to "http://webnoir.org" "Noir") ", " + (link-to "https://github.com/flatland/clojail" "clojail") ", and Chris Done's " + (link-to "https://github.com/chrisdone/jquery-console" "jquery-console") ". " + " The design is by " (link-to "http://apgwoz.com" "Andrew Gwozdziewycz") "." + ]) (defpartial home-html [] [:p.bottom - "Welcome to Try Clojure. See that little box up there? That's a Clojure repl. You can type " - "expressions and see their results right here in your browser. We also have a brief tutorial to " - "give you a taste of Clojure. Try it out by typing " [:code.expr "tutorial"] " in the console!"]) + "Welcome to Clojure! " + "You can see a Clojure interpreter above - we call it a REPL." + ] + [:p.bottom + "Type \"tutorial\" in the REPL to begin." + ]) (defn root-html [] (html5 @@ -65,9 +71,7 @@ [:a#about.buttons.last "about"]] [:div#changer (home-html)]] [:div.footer - [:p.bottom "©2011-2012 Anthony Grimes and numerous contributors. Built with " - (link-to "http://webnoir.org" "Noir") - "."]] + [:p.bottom "©2011-2012 Anthony Grimes and numerous contributors."]] (javascript-tag "var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-27340918-1']); From 9a4a1dc28c44f46504707504e71dcdc1e1c60e8d Mon Sep 17 00:00:00 2001 From: "Paolo \\\"Nusco\\\" Perrotta" Date: Sat, 24 Nov 2012 18:57:42 +0100 Subject: [PATCH 106/127] fix typo in html tags (they're styled out anyway, but that may change) --- resources/public/tutorial/page10.html | 6 +++--- resources/public/tutorial/page3.html | 2 +- src/tryclojure/views/home.clj | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/public/tutorial/page10.html b/resources/public/tutorial/page10.html index 5b43a30..f1e2228 100644 --- a/resources/public/tutorial/page10.html +++ b/resources/public/tutorial/page10.html @@ -17,11 +17,11 @@ Vectors and lists are sequential and ordered collections. Sets are not ordered, and they cannot contain duplicate elements. Maps are key-value collections, where the keys can be any object. - Here, we've used what Clojure calls a keyword (:foo) for one of the keys, and a number for the other key. + Here, we've used what Clojure calls a keyword (:foo) for one of the keys, and a number for the other key.

- Now I'll tell you another thing that may surprise you: Clojure collections are immutable - they can never change. + Now I'll tell you another thing that may surprise you: Clojure collections are immutable - they can never change. When you do anything on a list, including adding and removing elements, you actually get a brand new list. (Fortunately, Clojure is amazingly efficient at creating new lists). In general, Clojure encourages you to have as little mutable state as possible. @@ -29,7 +29,7 @@

- A prime example of this is map. map is a higher order function, which means that it takes another function as an argument. + A prime example of this is map. map is a higher order function, which means that it takes another function as an argument. For example, you can ask map to increment each number in a vector by passing it the inc function, followed by the vector. Try it for yourself: type (map inc [1 2 3 4]) to continue.

diff --git a/resources/public/tutorial/page3.html b/resources/public/tutorial/page3.html index 4edd21e..ae8cc9b 100644 --- a/resources/public/tutorial/page3.html +++ b/resources/public/tutorial/page3.html @@ -3,7 +3,7 @@

- A Clojure program is made of lists. + A Clojure program is made of lists. (+ 3 3) is a list that contains an operator, and then the operands. Try out the same concept with the * and - operators.

diff --git a/src/tryclojure/views/home.clj b/src/tryclojure/views/home.clj index 36701c2..2a1c4af 100644 --- a/src/tryclojure/views/home.clj +++ b/src/tryclojure/views/home.clj @@ -36,7 +36,7 @@ (defpartial home-html [] [:p.bottom "Welcome to Clojure! " - "You can see a Clojure interpreter above - we call it a REPL." + "You can see a Clojure interpreter above - we call it a REPL." ] [:p.bottom "Type \"tutorial\" in the REPL to begin." From 37a5dbd2a2bd0cc011605ce0bd8ea5b5feff60ab Mon Sep 17 00:00:00 2001 From: "Paolo \\\"Nusco\\\" Perrotta" Date: Sat, 24 Nov 2012 19:01:30 +0100 Subject: [PATCH 107/127] fix typo --- resources/public/tutorial/page8.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/public/tutorial/page8.html b/resources/public/tutorial/page8.html index 878d1a4..d3a26cf 100644 --- a/resources/public/tutorial/page8.html +++ b/resources/public/tutorial/page8.html @@ -10,7 +10,7 @@

If you run this code, you'll see some cryptic output. - In Clojures, functions are just normal values like numbers or strings. + In Clojure, functions are just normal values like numbers or strings. fn defines a function and then returns it. What you're seeing is simply what a function looks like when you print it on the screen.

From 8bf5ac3fab676b3f23a144e7e70c9190528a281e Mon Sep 17 00:00:00 2001 From: "Paolo \"Nusco\" Perrotta" Date: Sat, 24 Nov 2012 19:47:29 +0100 Subject: [PATCH 108/127] refactor page resolution to allow arbitrary page numbers --- resources/public/javascript/tryclojure.js | 38 ++++++++++++++--------- src/tryclojure/views/tutorial.clj | 4 +-- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/resources/public/javascript/tryclojure.js b/resources/public/javascript/tryclojure.js index 1e0256c..32b64ec 100644 --- a/resources/public/javascript/tryclojure.js +++ b/resources/public/javascript/tryclojure.js @@ -1,5 +1,17 @@ -var pageNum = -1; -var page = null; +var currentPage = -1; +var pageNames = [ + "page1", + "page2", + "page3", + "page4", + "page5", + "page6", + "page7", + "page8", + "page9", + "page10", + "page11" + ]; var pages = [ { verify: function(data) { return false; } @@ -37,14 +49,12 @@ var pages = [ ]; function showPage(n) { - var res = pages[n]; - if (res) { - pageNum = n; - page = res; + if (pages[n]) { + currentPage = n; var block = $("#changer"); block.fadeOut(function(e) { - block.load("/tutorial", { 'n' : n+1 }, function() { + block.load("/tutorial", { 'page' : pageNames[n+1] }, function() { block.fadeIn(); changerUpdated(); }); @@ -92,23 +102,23 @@ function doCommand(input, report) { report(); return true; case 'back': - if (pageNum > 0) { - showPage(pageNum - 1); + if (currentPage > 0) { + showPage(currentPage - 1); report(); return true; } else { return false; } case 'next': - if (pageNum >= 0 && pageNum < pages.length - 1) { - showPage(pageNum + 1); + if (currentPage >= 0 && currentPage < pages.length - 1) { + showPage(currentPage + 1); report(); return true; } else { return false; } case 'restart': - if (pageNum > 0) { + if (currentPage > 0) { showPage(0); report(); return true; @@ -139,8 +149,8 @@ function onHandle(line, report) { } // handle page - if (page && page.verify(data)) { - showPage(pageNum + 1); + if (pages[n] && pages[n].verify(data)) { + showPage(currentPage + 1); } // display expr results diff --git a/src/tryclojure/views/tutorial.clj b/src/tryclojure/views/tutorial.clj index f7ee2b2..550ab54 100644 --- a/src/tryclojure/views/tutorial.clj +++ b/src/tryclojure/views/tutorial.clj @@ -1,5 +1,5 @@ (ns tryclojure.views.tutorial (:require [noir.core :refer [defpage]])) -(defpage [:post "/tutorial"] {n :n} - (slurp (str "resources/public/tutorial/page" n ".html"))) \ No newline at end of file +(defpage [:post "/tutorial"] {page :page} + (slurp (str "resources/public/tutorial/" page ".html"))) \ No newline at end of file From a7e3cc81c5a34de5a5b199f4f0304792ed1db22a Mon Sep 17 00:00:00 2001 From: "Paolo \"Nusco\" Perrotta" Date: Sun, 25 Nov 2012 01:11:21 +0100 Subject: [PATCH 109/127] fix problems in screen flow --- resources/public/javascript/tryclojure.js | 88 +++++++++++------------ src/tryclojure/views/home.clj | 2 +- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/resources/public/javascript/tryclojure.js b/resources/public/javascript/tryclojure.js index 32b64ec..36162cd 100644 --- a/resources/public/javascript/tryclojure.js +++ b/resources/public/javascript/tryclojure.js @@ -1,5 +1,5 @@ var currentPage = -1; -var pageNames = [ +var pages = [ "page1", "page2", "page3", @@ -12,7 +12,7 @@ var pageNames = [ "page10", "page11" ]; -var pages = [ +var pageExitConditions = [ { verify: function(data) { return false; } }, @@ -48,18 +48,34 @@ var pages = [ } ]; -function showPage(n) { - if (pages[n]) { - currentPage = n; +function nextPage() { + if (currentPage < pages.length - 1) { + goToPage(currentPage + 1); + } +} - var block = $("#changer"); - block.fadeOut(function(e) { - block.load("/tutorial", { 'page' : pageNames[n+1] }, function() { - block.fadeIn(); - changerUpdated(); - }); - }); - } +function previousPage() { + if (currentPage > 0) { + goToPage(currentPage - 1); + } +} + +function goToFirstPage() { + if (currentPage > 0) { + goToPage(0); + } +} + +function goToPage(pageNumber) { + currentPage = pageNumber; + + var block = $("#changer"); + block.fadeOut(function(e) { + block.load("/tutorial", { 'page' : pages[pageNumber] }, function() { + block.fadeIn(); + changerUpdated(); + }); + }); } function setupLink(url) { @@ -95,36 +111,17 @@ function html_escape(val) { return result; } -function doCommand(input, report) { - switch (input) { - case 'tutorial': - showPage(0); - report(); - return true; +function doCommand(input) { + switch (input) { + case 'next': + nextPage(); + return true; case 'back': - if (currentPage > 0) { - showPage(currentPage - 1); - report(); - return true; - } else { - return false; - } - case 'next': - if (currentPage >= 0 && currentPage < pages.length - 1) { - showPage(currentPage + 1); - report(); - return true; - } else { - return false; - } + previousPage(); + return true; case 'restart': - if (currentPage > 0) { - showPage(0); - report(); - return true; - } else { - return false; - } + goToFirstPage(); + return true; default: return false; } @@ -138,7 +135,10 @@ function onHandle(line, report) { var input = $.trim(line); // handle commands - if (doCommand(input, report)) return; + if (doCommand(input)) { + report(); + return; + } // perform evaluation var data = eval_clojure(input); @@ -149,8 +149,8 @@ function onHandle(line, report) { } // handle page - if (pages[n] && pages[n].verify(data)) { - showPage(currentPage + 1); + if (currentPage >= 0 && pageExitConditions[currentPage].verify(data)) { + nextPage(); } // display expr results diff --git a/src/tryclojure/views/home.clj b/src/tryclojure/views/home.clj index 54909be..a3fbbda 100644 --- a/src/tryclojure/views/home.clj +++ b/src/tryclojure/views/home.clj @@ -35,7 +35,7 @@ "You can see a Clojure interpreter above - we call it a REPL." ] [:p.bottom - "Type \"tutorial\" in the REPL to begin." + "Type \"next\" in the REPL to begin." ]) (defn root-html [] From 9ebd2ed74b1c4e5ccf88eadbd98533286fc9535f Mon Sep 17 00:00:00 2001 From: "Paolo \"Nusco\" Perrotta" Date: Sun, 25 Nov 2012 02:19:07 +0100 Subject: [PATCH 110/127] add a few command alias (Like 'previous' for back and 'reset' for 'restart'.) I tended to forget the commands the first time I was using TryClojure. These aliases should help users as absent-minded as I am. --- resources/public/javascript/tryclojure.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/resources/public/javascript/tryclojure.js b/resources/public/javascript/tryclojure.js index 36162cd..4e26625 100644 --- a/resources/public/javascript/tryclojure.js +++ b/resources/public/javascript/tryclojure.js @@ -113,13 +113,19 @@ function html_escape(val) { function doCommand(input) { switch (input) { - case 'next': + case 'next': + case 'forward': nextPage(); return true; - case 'back': + case 'previous': + case 'prev': + case 'back': previousPage(); return true; case 'restart': + case 'reset': + case 'home': + case 'quit': goToFirstPage(); return true; default: From e1f13feec4247c7360f6aca8863c5f9419c4b94e Mon Sep 17 00:00:00 2001 From: "Paolo \"Nusco\" Perrotta" Date: Sun, 25 Nov 2012 02:20:43 +0100 Subject: [PATCH 111/127] refactorings --- resources/public/javascript/tryclojure.js | 28 ++++++----------------- src/tryclojure/views/home.clj | 6 ++--- 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/resources/public/javascript/tryclojure.js b/resources/public/javascript/tryclojure.js index 4e26625..19e60bd 100644 --- a/resources/public/javascript/tryclojure.js +++ b/resources/public/javascript/tryclojure.js @@ -48,25 +48,11 @@ var pageExitConditions = [ } ]; -function nextPage() { - if (currentPage < pages.length - 1) { - goToPage(currentPage + 1); - } -} - -function previousPage() { - if (currentPage > 0) { - goToPage(currentPage - 1); - } -} - -function goToFirstPage() { - if (currentPage > 0) { - goToPage(0); - } -} - function goToPage(pageNumber) { + if (pageNumber == currentPage || pageNumber < 0 || pageNumber >= pages.length) { + return; + } + currentPage = pageNumber; var block = $("#changer"); @@ -115,18 +101,18 @@ function doCommand(input) { switch (input) { case 'next': case 'forward': - nextPage(); + goToPage(currentPage + 1); return true; case 'previous': case 'prev': case 'back': - previousPage(); + goToPage(currentPage - 1); return true; case 'restart': case 'reset': case 'home': case 'quit': - goToFirstPage(); + goToPage(0); return true; default: return false; diff --git a/src/tryclojure/views/home.clj b/src/tryclojure/views/home.clj index a3fbbda..593c49b 100644 --- a/src/tryclojure/views/home.clj +++ b/src/tryclojure/views/home.clj @@ -3,7 +3,7 @@ [hiccup.element :refer [javascript-tag link-to unordered-list]] [hiccup.page :refer [include-css include-js html5]])) -(defpartial links [] +(defpartial links-html [] (unordered-list [(link-to "http://clojure.org" "The official Clojure website") (link-to "http://dev.clojure.org/display/doc/Getting+Started" "Getting started with Clojure") @@ -35,7 +35,7 @@ "You can see a Clojure interpreter above - we call it a REPL." ] [:p.bottom - "Type \"next\" in the REPL to begin." + "Type next in the REPL to begin." ]) (defn root-html [] @@ -89,4 +89,4 @@ (about-html)) (defpage "/links" [] - (links)) + (links-html)) From b398f7b5031bc35ca734b5ce125bd4004ee5c2c4 Mon Sep 17 00:00:00 2001 From: "Paolo \"Nusco\" Perrotta" Date: Sun, 25 Nov 2012 02:37:42 +0100 Subject: [PATCH 112/127] add a final page In case people type 'next' on the last tutorial page --- resources/public/javascript/tryclojure.js | 6 +++++- resources/public/tutorial/end.html | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 resources/public/tutorial/end.html diff --git a/resources/public/javascript/tryclojure.js b/resources/public/javascript/tryclojure.js index 19e60bd..fc2977b 100644 --- a/resources/public/javascript/tryclojure.js +++ b/resources/public/javascript/tryclojure.js @@ -10,7 +10,8 @@ var pages = [ "page8", "page9", "page10", - "page11" + "page11", + "end" ]; var pageExitConditions = [ { @@ -43,6 +44,9 @@ var pageExitConditions = [ { verify: function (data) { return data.expr == "(map inc [1 2 3 4])"; } }, + { + verify: function (data) { return false; } + }, { verify: function (data) { return false; } } diff --git a/resources/public/tutorial/end.html b/resources/public/tutorial/end.html new file mode 100644 index 0000000..fe64621 --- /dev/null +++ b/resources/public/tutorial/end.html @@ -0,0 +1,7 @@ +

+ Glad to know you're eager for more, but unfortunately this tutorial is over. +

+ +

+ Maybe you can click the 'links' button above for more resources?. +

From d2b277c4ac1fa0e563162a3bd6d3f42dbedbc9dc Mon Sep 17 00:00:00 2001 From: "Paolo \"Nusco\" Perrotta" Date: Sun, 25 Nov 2012 02:51:51 +0100 Subject: [PATCH 113/127] remove 'home' button' It broke 'next' and 'back' If someone wants to go back to the beginning, she can either use 'restart' or just refresh the URL. --- src/tryclojure/views/home.clj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tryclojure/views/home.clj b/src/tryclojure/views/home.clj index 593c49b..856451f 100644 --- a/src/tryclojure/views/home.clj +++ b/src/tryclojure/views/home.clj @@ -62,7 +62,6 @@ [:div#container [:div#console.console] [:div#buttons - [:a#home.buttons "home"] [:a#links.buttons "links"] [:a#about.buttons.last "about"]] [:div#changer (home-html)]] From 473e8e1296bf3d35253f0e61561734e8b9c64ff7 Mon Sep 17 00:00:00 2001 From: "Paolo \"Nusco\" Perrotta" Date: Sun, 25 Nov 2012 03:08:33 +0100 Subject: [PATCH 114/127] add undocumented 'gopage x' command (useful for debugging) --- resources/public/javascript/tryclojure.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/resources/public/javascript/tryclojure.js b/resources/public/javascript/tryclojure.js index fc2977b..c6069fc 100644 --- a/resources/public/javascript/tryclojure.js +++ b/resources/public/javascript/tryclojure.js @@ -54,7 +54,7 @@ var pageExitConditions = [ function goToPage(pageNumber) { if (pageNumber == currentPage || pageNumber < 0 || pageNumber >= pages.length) { - return; + return; } currentPage = pageNumber; @@ -102,6 +102,11 @@ function html_escape(val) { } function doCommand(input) { + if (input.match(/^gopage /)) { + goToPage(parseInt(input.substring("gopage ".length))); + return true; + } + switch (input) { case 'next': case 'forward': @@ -146,7 +151,7 @@ function onHandle(line, report) { // handle page if (currentPage >= 0 && pageExitConditions[currentPage].verify(data)) { - nextPage(); + goToPage(currentPage + 1); } // display expr results From 4608432aecd0fd52106e8cfc3b581145729b5aa5 Mon Sep 17 00:00:00 2001 From: "Paolo \"Nusco\" Perrotta" Date: Sun, 25 Nov 2012 12:26:08 +0100 Subject: [PATCH 115/127] fix typo --- resources/public/tutorial/end.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/public/tutorial/end.html b/resources/public/tutorial/end.html index fe64621..f85f040 100644 --- a/resources/public/tutorial/end.html +++ b/resources/public/tutorial/end.html @@ -3,5 +3,5 @@

- Maybe you can click the 'links' button above for more resources?. + Maybe you can click the 'links' button above for more resources?

From 098dbb9381a3db79d1e2d32895bccef4e6f11525 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Fri, 15 Mar 2013 22:18:54 +0400 Subject: [PATCH 116/127] Link to clojure-doc.org --- src/tryclojure/views/home.clj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tryclojure/views/home.clj b/src/tryclojure/views/home.clj index 856451f..0bbc4d4 100644 --- a/src/tryclojure/views/home.clj +++ b/src/tryclojure/views/home.clj @@ -6,9 +6,8 @@ (defpartial links-html [] (unordered-list [(link-to "http://clojure.org" "The official Clojure website") - (link-to "http://dev.clojure.org/display/doc/Getting+Started" "Getting started with Clojure") + (link-to "http://clojure-doc.org/" "Clojure tutorials and documentation") (link-to "http://groups.google.com/group/clojure" "Clojure mailing list") - (link-to "http://java.ociweb.com/mark/clojure/article.html" "A comprehensive Clojure tutorial") (link-to "http://joyofclojure.com/" "The Joy of Clojure: a book by Michael Fogus and Chris Houser") (link-to "http://disclojure.org" "Disclojure") (link-to "http://planet.clojure.in" "Planet Clojure")])) From bb93101ca4f5f3249d7f351b39938c55f818bccf Mon Sep 17 00:00:00 2001 From: Anthony Grimes Date: Tue, 12 Mar 2013 22:07:06 -0700 Subject: [PATCH 117/127] Update clojail. --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index 81f6fb5..6a91931 100644 --- a/project.clj +++ b/project.clj @@ -3,6 +3,6 @@ :dependencies [[org.clojure/clojure "1.4.0"] [noir "1.3.0-beta10"] [commons-lang/commons-lang "2.5"] - [clojail "1.0.1"]] + [clojail "1.0.6"]] :jvm-opts ["-Djava.security.policy=example.policy""-Xmx80M"] :main tryclojure.server) From 4ecf3727bdb5aa790de00dc298ba9b14d5cf2257 Mon Sep 17 00:00:00 2001 From: Phil Hagelberg Date: Mon, 1 Jul 2013 16:24:53 -0700 Subject: [PATCH 118/127] Generate an uberjar during deployment. --- Procfile | 2 +- README.md | 2 +- project.clj | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Procfile b/Procfile index 09a6c19..c4d5a3c 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: lein run +web: java $JVM_OPTS -cp target/tryclojure-standalone.jar clojure.main -m tryclojure.server diff --git a/README.md b/README.md index c7be349..3ec9350 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ TryClojure is a online Clojure REPL written using Noir and Chris Done's jquery c http://tryclj.com -If you want to run it, refer to https://github.com/Raynes/tryclojure/wiki/Running-Tryclojure +To run it locally, use `lein run`. ## Credits diff --git a/project.clj b/project.clj index 6a91931..a49a54c 100644 --- a/project.clj +++ b/project.clj @@ -4,5 +4,7 @@ [noir "1.3.0-beta10"] [commons-lang/commons-lang "2.5"] [clojail "1.0.6"]] - :jvm-opts ["-Djava.security.policy=example.policy""-Xmx80M"] + :jvm-opts ["-Djava.security.policy=example.policy" "-Xmx80M"] + :min-lein-version "2.0.0" + :uberjar-name "tryclojure-standalone.jar" :main tryclojure.server) From afa0845bb99ff027b7369654f3106453c691e33e Mon Sep 17 00:00:00 2001 From: Phil Hagelberg Date: Mon, 1 Jul 2013 16:43:43 -0700 Subject: [PATCH 119/127] Move security policy setting into Procfile --- Procfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Procfile b/Procfile index c4d5a3c..c39b2ec 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: java $JVM_OPTS -cp target/tryclojure-standalone.jar clojure.main -m tryclojure.server +web: java -Djava.security.policy=example.policy $JVM_OPTS -cp target/tryclojure-standalone.jar clojure.main -m tryclojure.server From 7a6bf2b9a576e723843588cac366c08abd35134c Mon Sep 17 00:00:00 2001 From: Dan Date: Wed, 19 Mar 2014 11:53:42 -0400 Subject: [PATCH 120/127] Updated to latest version of jquery-console. --- .../public/javascript/jquery-console/README | 15 +- .../javascript/jquery-console/demo.html | 27 + .../jquery-console/jquery.console.js | 1308 +++++++++-------- 3 files changed, 746 insertions(+), 604 deletions(-) diff --git a/resources/public/javascript/jquery-console/README b/resources/public/javascript/jquery-console/README index 245420f..c5d2de3 100644 --- a/resources/public/javascript/jquery-console/README +++ b/resources/public/javascript/jquery-console/README @@ -1,6 +1,6 @@ INTRODUCTION -See demo.html. Or here: http://chrisdone.com/jquery-console/demo.html +See demo.html. Or here: http://chrisdone.com/jquery-console/ Options available: @@ -18,6 +18,10 @@ Options available: promptLabel string Prompt string like 'JavaScript> '. + cols integer the number of cols, this value is only + used by the command completion to format + the list of results. + commandValidate function When user hits return, validate whether to trigger commandHandle and re-prompt. @@ -30,6 +34,10 @@ Options available: to report a result of the command asynchronously. + commandComplete function Handle the command completion when the + tab key is pressed. It returns a list + of string completion suffixes. + animateScroll bool Whether to animate the scroll to top. Currently disabled. @@ -39,6 +47,9 @@ Options available: cancelHandle function Handle a user-signaled interrupt. + fadeOnReset bool Whether to trigger a fade in/out when + the console is reset. Defaults to true. + LICENSE Copyright 2010 Chris Done, Simon David Pratt. All rights reserved. @@ -68,5 +79,3 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - diff --git a/resources/public/javascript/jquery-console/demo.html b/resources/public/javascript/jquery-console/demo.html index 370ca73..7f85186 100644 --- a/resources/public/javascript/jquery-console/demo.html +++ b/resources/public/javascript/jquery-console/demo.html @@ -102,6 +102,33 @@ }, promptHistory:true }); + /* Fifth console */ + var console5 = $('
'); + $('body').append(console5); + var controller5 = console5.console({ + promptLabel: 'Complete> ', + commandHandle:function(line){ + if (line) { + return [{msg:"you typed " + line,className:"jquery-console-message-value"}]; + } else { + var m = "type a color among (" + this.colors.join(", ") + ")"; + return [{msg:m,className:"jquery-console-message-value"}]; + } + }, + colors: ["red","blue","green","black","yellow","white","grey"], + cols: 40, + completeHandle:function(prefix){ + var colors = this.colors; + var ret = []; + for (var i=0;i