From 8eb873337ee443ebd987d9d6756eac787054ead2 Mon Sep 17 00:00:00 2001
From: Raynes L|74iWcC@*C
zt_kawCF%0}K-}f}zU+qPx>*}fW+@v0aBl1 In order to move on to the next page please type Wow you did it! Looks like you have a talent for this! I'm going to have to try something harder for you. In order to make it to " +
+ "the next page type: Your intelligence is too great for this lame tutorial. It's time for you to " +
+ "contribute to some open source Clojure projects. Have fun. In order to move on to the next page please type In order to move on to the next page please type Wow you did it! Looks like you have a talent for this! I'm going to have to try something harder for you. In order to make it to " +
- "the next page type: Wow you did it! Looks like you have a talent for this! I'm going to have to try something harder for you. In order to make it to " +
+ "the next page type: 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. In order to move on to the next page please type Wow you did it! Looks like you have a talent for this! I'm going to have to try something harder for you. In order to make it to " +
- "the next page type: Your intelligence is too great for this lame tutorial. It's time for you to " +
- "contribute to some open source Clojure projects. Have fun.
+ 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.
+
+ 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!
+
+ 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:
+
+ 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
+ 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
+ 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
+
+ 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:
+
+ Congratulations, you have passed step one. Press Next to continue.
+
+ 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:
+
+ 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:
+ 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:
+
+ All of the math functions take an arbitrary number of arguments. This means you can do stuff
+ like this:
+ That's the end of step two. Press Next to continue to step three, where we'll play with
+ sequences.
+
+ 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:
+ We have introduced something new here -- the anonymous function. Anonymous functions are used
+ quite heavily in Clojure code. They are created using
+ Another highly important collection is the hashmap. A literal hashmap looks like this:
+
+ To get a value at a key in a map, we can do this:
+ 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.
+
+ 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:
+ 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:
+
+ 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:
+
+ 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
+
+ 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
+ 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.
+ 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:
+ 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:
+
+ That's the end of step 4. Press Next to move on.
+
+ 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!
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:
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
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:
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:
- Congratulations, you have passed step one. Press Next to continue.
+ Congratulations, you have passed step one. Type next to continue.
") (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 Welcome to the first page of the interactive tutorial!
\n" +
+ "(+ 1 2).This is page two!
\n" +
+ "(map inc [1 2 3 4]).Third and final page!
\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
");
+ 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 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 Welcome to the first page of the interactive tutorial!
\n" +
- "(+ 1 2).(+ 1 2).This is page two!
\n" +
- "(map inc [1 2 3 4]).(map inc [1 2 3 4]).Third and final page!
\n" +
- "Welcome to the first page of the interactive tutorial!
\n" +
- "(+ 1 2).This is page two!
\n" +
- "(map inc [1 2 3 4]).Third and final page!
\n" +
- "
+ 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.
+(println "!dlrow ,olleH")
+
+reverse
+ should fix things -- so type the following into the REPL:
+(reverse "!dlrow ,olleH")
+
+str function! Try this:
+(str (reverse "!dlrow ,olleH"))
+
+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"))
+
+(println (apply str (reverse "!dlrow ,olleH")))
+
+(+ 2 2)(- 3 2)(* 5 5)(/ 4 3)(class (/ 4 3)).
+(/ 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.
+(+ 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.
+[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.
+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])
+{: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.
+({: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.
+(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?
+#{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)(#{\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.
+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.
+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)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).
+(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.
+(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.
+
+
(println "!dlrow ,olleH")
+(println "!dlrow ,olleH")
(reverse "!dlrow ,olleH")
+(reverse "!dlrow ,olleH")
str function! Try this:
(str (reverse "!dlrow ,olleH"))
+(str (reverse "!dlrow ,olleH"))
(apply str (reverse "!dlrow ,olleH"))
+(apply str (reverse "!dlrow ,olleH"))
(println (apply str (reverse "!dlrow ,olleH")))
+(println (apply str (reverse "!dlrow ,olleH")))
(+ 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.
#(* % %). 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 typingtutorial 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
')
+ .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.
+
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).
- 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.
- 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.
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!
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).
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)
+
+ 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.
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
- 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.
(+ 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).
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.
Try it out. Type (+ 1 2 3 4 5 6) to continue.
Try it out. Type (+ 1 2 3 4 5 6) to continue.
defn
-Type (defn square [x] (* x x)) to continue!
Type (defn square [x] (* x x)) to continue!
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).
(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)
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.
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 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 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).
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 = $('- 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=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;f
0)for(var j=d;j 0},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=/"+d+">"},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;e 0?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=/ + + + + + + + +
Simple console demo
+Tested on:
++
+ +- Internet Explorer 6
+- Opera 10.01
+- Chromium 4.0.237.0 (Ubuntu build 31094)
+- Firefox 3.5.8
+