diff --git a/.gitignore b/.gitignore
index d9148e9..102c661 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
pom.xml
*jar
lib
-classes
\ No newline at end of file
+classes
+*~
+.lein-deps-sum
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..9cf7391
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "resources/public/javascript/jquery-console"]
+ path = resources/public/javascript/jquery-console
+ url = https://github.com/chrisdone/jquery-console.git
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..4f42080
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1 @@
+language: clojure
\ No newline at end of file
diff --git a/Procfile b/Procfile
new file mode 100644
index 0000000..92abe1c
--- /dev/null
+++ b/Procfile
@@ -0,0 +1 @@
+web: java -Djava.security.policy=example.policy $JVM_OPTS -cp target/tryclojure-standalone.jar clojure.main -m tryclojure.server $PORT
diff --git a/README b/README
deleted file mode 100644
index 57f30c5..0000000
--- a/README
+++ /dev/null
@@ -1,13 +0,0 @@
-# tryclojure
-
-TryClojure is a online Clojure REPL written using Moustache, Ring, and Chris Done's jquery console (you're awesome, Chris).
-
-## Usage
-
-http://www.try-clojure.org
-
-Many thanks to Licenser for giving me a place to put it.
-
-## License
-
-Licensed under the same thing Clojure is licensed under: the EPL, of which you can find a copy at the root of this directory.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..65cfeb7
--- /dev/null
+++ b/README.md
@@ -0,0 +1,19 @@
+# tryclojure
+
+TryClojure is a online Clojure REPL written using Noir and Chris Done's jquery console (you're awesome, Chris).
+
+[](http://travis-ci.org/Raynes/tryclojure)
+
+## Usage
+
+http://tryclj.com
+
+To run it locally, use `lein ring server`.
+
+## Credits
+
+apgwoz: Design
+
+## License
+
+Licensed under the same thing Clojure is licensed under: the EPL, of which you can find a copy at the root of this directory.
diff --git a/example.policy b/example.policy
new file mode 100644
index 0000000..dc85222
--- /dev/null
+++ b/example.policy
@@ -0,0 +1,3 @@
+grant {
+ permission java.security.AllPermission;
+};
diff --git a/project.clj b/project.clj
index 5b90c5d..44808a1 100644
--- a/project.clj
+++ b/project.clj
@@ -1,12 +1,13 @@
(defproject tryclojure "0.1.0-SNAPSHOT"
:description "A simple web-based Clojure REPL for trying out Clojure without having to install it."
- :dependencies [[org.clojure/clojure "1.1.0"]
- [org.clojure/clojure-contrib "1.1.0"]
- [net.cgrand/moustache "1.0.0-SNAPSHOT"]
- [ring "0.2.5"]
- [commons-lang/commons-lang "2.5"]
- [clj-sandbox "0.3.8"]
- [clj-highlight "0.1.1-SNAPSHOT"]
- [hiccup "0.2.6"]]
- :dev-dependencies [[swank-clojure "1.2.1"]
- [ring/ring-devel "0.2.5"]])
+ :dependencies [[org.clojure/clojure "1.4.0"]
+ [lib-noir "0.8.1"]
+ [compojure "1.1.6"]
+ [ring-server "0.3.1"]
+ [commons-lang/commons-lang "2.5"]
+ [clojail "1.0.6"]]
+ :jvm-opts ["-Djava.security.policy=example.policy" "-Xmx80M"]
+ :min-lein-version "2.0.0"
+ :uberjar-name "tryclojure-standalone.jar"
+ :plugins [[lein-ring "0.8.10"]]
+ :ring {:handler tryclojure.server/app :port 8801})
diff --git a/resources/public/clojure-logo.png b/resources/public/clojure-logo.png
new file mode 100644
index 0000000..583ebce
Binary files /dev/null and b/resources/public/clojure-logo.png differ
diff --git a/resources/public/css/gh-fork-ribbon.css b/resources/public/css/gh-fork-ribbon.css
new file mode 100644
index 0000000..6fe4efb
--- /dev/null
+++ b/resources/public/css/gh-fork-ribbon.css
@@ -0,0 +1,82 @@
+/* From https://github.com/simonwhitaker/github-fork-ribbon-css */
+
+/* Left will inherit from right (so we don't need to duplicate code */
+.github-fork-ribbon {
+ /* The right and left lasses determine the side we attach our banner to */
+ position: absolute;
+
+ /* Add a bit of padding to give some substance outside the "stitching" */
+ padding: 2px 0;
+
+ /* Set the base colour */
+ background-color: #5881d8;
+
+ /* Set a gradient: transparent black at the top to almost-transparent black at the bottom */
+ background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0.00)), to(rgba(0, 0, 0, 0.15)));
+ background-image: -webkit-linear-gradient(top, rgba(0, 0, 0, 0.00), rgba(0, 0, 0, 0.15));
+ background-image: -moz-linear-gradient(top, rgba(0, 0, 0, 0.00), rgba(0, 0, 0, 0.15));
+ background-image: -o-linear-gradient(top, rgba(0, 0, 0, 0.00), rgba(0, 0, 0, 0.15));
+ background-image: -ms-linear-gradient(top, rgba(0, 0, 0, 0.00), rgba(0, 0, 0, 0.15));
+ background-image: linear-gradient(top, rgba(0, 0, 0, 0.00), rgba(0, 0, 0, 0.15));
+ filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#000000', EndColorStr='#000000');
+
+ /* Add a drop shadow */
+ -webkit-box-shadow: 0px 2px 3px 0px rgba(0, 0, 0, 0.5);
+ box-shadow: 0px 2px 3px 0px rgba(0, 0, 0, 0.5);
+}
+.github-fork-ribbon a {
+ /* Set the font */
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 13px;
+ font-weight: 700;
+ color: white;
+
+ /* Set the text properties */
+ text-decoration: none;
+ text-shadow: 0 -1px rgba(0,0,0,0.5);
+ text-align: center;
+
+ /* Set the geometry. If you fiddle with these you'll also need to tweak the top and right values in #github-fork-ribbon. */
+ width: 200px;
+ line-height: 20px;
+
+ /* Set the layout properties */
+ display: inline-block;
+ padding: 2px 0;
+
+ /* Add "stitching" effect */
+ border-width: 1px 0;
+ border-style: dotted;
+ border-color: rgba(255,255,255,0.7);
+}
+
+.github-fork-ribbon-wrapper {
+ width: 150px;
+ height: 150px;
+ position: absolute;
+ overflow: hidden;
+ top: 0;
+}
+
+.github-fork-ribbon-wrapper.right {
+ right: 0;
+}
+
+.github-fork-ribbon-wrapper.right .github-fork-ribbon {
+ top: 42px;
+ right: -43px;
+
+ /* Rotate the banner 45 degrees */
+ -webkit-transform: rotate(45deg);
+ -moz-transform: rotate(45deg);
+ -o-transform: rotate(45deg);
+ transform: rotate(45deg);
+}
+
+@media (max-width: 619px) {
+
+ .github-fork-ribbon {
+ zoom: 0.6;
+ }
+
+}
diff --git a/resources/public/css/tryclojure.css b/resources/public/css/tryclojure.css
index 9d03de9..e5145d8 100644
--- a/resources/public/css/tryclojure.css
+++ b/resources/public/css/tryclojure.css
@@ -1,63 +1,172 @@
-body { background-color: white; }
-div#container { width: 600px; height: 100%;
- margin: 50px auto 50px auto; background-color: white;}
-h1 { font-size: 32pt; color: black; border-bottom: 1px solid #333; font-family: arial;}
-.bottom { background-color: white; color: #333; }
-.bottom a,a:visited { color: #111; }
-table.bottom { width: 100%; border: 1px solid black;}
-div#changer { width: 100%; height: 100%; }
-div.buttons { background: transparent url('/resources/public/buttontdbg.png') no-repeat top left;
- padding-left: 0.2em; padding-right: 0.2em; text-align: center; }
-.buttons a.buttons { border-right: 1px solid #5F665F; color: black; padding-right: 0.3em;
- font-family: verdana; font-size: 10pt; font-weight: bold; padding-left: 0.3em;}
-a.lbutton { color: black; font-family: verdana; font-size: 10pt; font-weight: bold;
- padding-left: 0.3em; padding-right: 0.3em; }
-td.bholder { border: 1px solid black; }
-div#tuttext { overflow-y: auto; max-height: 300px; }
-a.buttons:hover,a.lbutton:hover { color: white; cursor: pointer; }
-div.continue { width: 100%; text-align: center; padding-top: 0.5em; }
-
-p.bottom
-{
- padding: 0;
- font-family: verdana;
- font-size: 10pt;
-}
-p
-{
- color:gray;
- font-family:\"Times New Roman\";
- font-size:14px;
-}
-div.bottom { width: 600px; }
-div.footer { width: 100%; border-top: 1px solid black; text-align: center; }
-div.console div.jquery-console-inner
-{ width:595px; height:300px; background:#efefef; padding-left: 3px; padding-top: 3px;
- overflow:auto; text-align:left; border: 1px solid black;
- background: transparent url('/resources/public/wtf.png') no-repeat top left; }
-div.console div.jquery-console-message-value
-{ color:#0066FF; font-family:monospace;
- padding:0.1em; }
-div.console div.jquery-console-prompt-box
-{ color:#444; font-family:monospace; }
-div.console div.jquery-console-focus span.jquery-console-cursor
-{ background:#333; color:#eee; font-weight:bold }
-div.console div.jquery-console-message-error
-{ color:#ef0505; font-family:sans-serif; font-weight:bold;
- padding:0.1em; }
-div.console div.jquery-console-message-success
-{ color:#187718; font-family:monospace;
- padding:0.1em; }
-div.console span.jquery-console-prompt-label { font-weight:bold }
+body {
+ background: #F7F7F7;
+ text-align: center;
+ font-family: Helvetica, sans-serif;
+ color: #6C7B89;
+}
+
+#wrapper {
+ text-align: left;
+ width: 620px;
+ margin: 0 auto;
+}
+
+#header {
+ margin: 20px 0px 20px 190px;
+}
+
+#header h1 {
+ height: 72px;
+ line-height: 72px;
+ background: url(../clojure-logo.png) no-repeat;
+ padding: 0px 0px 0px 80px;
+ font-weight: normal;
+ font-size: 36px;
+}
+
+#header h1 span.logo-try {
+ color: #63b132;
+ display:inline;
+}
+
+#header h1 span.logo-clojure {
+ color: #2B4A8B;
+ display:inline
+}
+
+#container {
+ margin-bottom: 40px;
+}
+
+#console {
+ height: 220px;
+ padding: 10px;
+ background: #FFFFFF;
+ font-family: monospace;
+ font-size: 14px;
+ color: #6C7B89;
+ margin-bottom: 10px;
+}
+
+#console textarea {
+ /* Use a largish font to ensure iOS doesn't zoom in on focus */
+ font-size: 2em;
+}
+
+#console div.jquery-console-inner {
+ width:580px;
+ height:200px;
+ margin: 10px 10px;
+ overflow:auto;
+ text-align:left;
+}
+
+#console div.jquery-console-message-value {
+ color:#63b132;
+}
+
+#console div.jquery-console-prompt-box {
+ color: #6C7B89;
+}
+
+#console div.jquery-console-focus span.jquery-console-cursor {
+ background:#6C7B89;
+ color: #FFFFFF;
+}
+
+#console div.jquery-console-message-error {
+ color:#FD713B;
+}
+
+#console div.jquery-console-message-success {
+ color:#63b132;
+}
+
+#console span.jquery-console-prompt-label {
+ font-weight:bold;
+}
+
+#buttons {
+ background: transparent;
+ text-align: center;
+ margin-bottom: 10px;
+}
+
+#buttons a {
+ display: inline-block;
+ color: #2B4A8B;
+ padding: 2px 2px;
+ border-bottom: 2px solid #2B4A8B;
+ text-transform: uppercase;
+ margin: 10px;
+ font-family: Helvetica, sans-serif;
+ font-size: 16px;
+ transition: all 100ms;
+}
+
+#buttons a.last {
+ margin-right: 0;
+}
+
+#buttons a:hover {
+ cursor: pointer;
+}
+
+#changer {
+ line-height: 1.3em;
+ padding: 20px;
+ background: #D6DADD;
+ margin-bottom: 10px;
+}
+
+#changer p {
+ margin: 0.6em;
+}
+
+#changer a {
+ color: #2B4A8B;
+}
+
+#changer a:visited {
+ color: #60749E;
+}
+
+#changer code {
+ background-color: #eee;
+ border: 1px solid #aaa;
+ color: #555;
+ font-family: courier, monospace;
+ padding: 0.1em 0.25em 0.1em 0.25em;
+}
+
+#changer ul {
+ margin: 0px;
+ padding: 0px;
+ list-style: none;
+}
+
+#tuttext {
+}
+
+.continue {
+ width: 100%;
+ text-align: center;
+ padding-top: 0.5em;
+}
+
+.footer {
+ text-align: center;
+}
/* Coderay alpha style */
+code {
+ display: inline-block;
+}
.code {
- background-color: #f8f8f8;
- border: 1px solid silver;
-/* font-family: 'Courier New', 'Terminal', monospace; */
color: #000;
}
+
.code pre { margin: 0px; }
span.code { white-space: pre; border: 0px; padding: 2px; }
@@ -71,105 +180,54 @@ table.code td { padding: 2px 4px; vertical-align: top; }
color: gray;
text-align: right;
}
-.code .line_numbers a:target, .code .no a:target { color: blue; }
-.code .line_numbers .highlighted, .code .no .highlighted { color: red; }
-.code .no { padding: 0px 4px; }
-.code .code { width: 100%; }
-.code .code pre { overflow: auto; }
-
-.debug { color:white ! important; background:blue ! important; }
-
-.an { color:#007 }
-.at { color:#f08 }
-.av { color:#700 }
-.bi { color:#509; font-weight:bold }
-.c { color:#888; }
-.c .dl { color:#444; }
-.c .ch { color:#444; }
-
-.ch { color:#04D }
-.ch .k { color:#04D }
-.ch .dl { color:#039 }
-
-.cl { color:#B06; font-weight:bold }
-.cm { color:#A08; font-weight:bold }
-.co { color:#036; font-weight:bold }
-.cr { color:#0A0 }
-.cv { color:#369 }
-.de { color:#B0B; }
-.df { color:#099; font-weight:bold }
-.di { color:#088; font-weight:bold }
-.dl { color:black }
-.do { color:#970 }
-.dt { color:#34b }
-.ds { color:#D42; font-weight:bold }
-.e { color:#666; font-weight:bold }
-.en { color:#800; font-weight:bold }
-.er { color:#F00; background-color:#FAA }
-.ex { color:#C00; font-weight:bold }
-.fl { color:#60E; font-weight:bold }
-.fu { color:#06B; font-weight:bold }
-.gv { color:#d70; font-weight:bold }
-.hx { color:#058; font-weight:bold }
-.i { color:#00D; font-weight:bold }
-.ic { color:#B44; font-weight:bold }
-
-.il { background-color: hsla(0,0%,0%,0.1); color: black }
-.il .idl { font-weight: bold; color: #666 }
-.idl { font-weight: bold; color: #666; }
-
-.im { color:#f00; }
-.in { color:#B2B; font-weight:bold }
-.iv { color:#33B }
-.la { color:#970; font-weight:bold }
-.lv { color:#963 }
-.ns { color:#707; font-weight:bold }
-.oc { color:#40E; font-weight:bold }
-.op { }
-.pc { color:#058; font-weight:bold }
-.pd { color:#369; font-weight:bold }
-.pp { color:#579; }
-.ps { color:#00C; font-weight:bold }
-.pt { color:#074; font-weight:bold }
-.r, .kw { color:#080; font-weight:bold }
-
-.ke { color: #808; }
-.ke .dl { color: #606; }
-.ke .ch { color: #80f; }
-.vl { color: #088; }
-
-.rx { background-color:hsla(300,100%,50%,0.1); color:#808 }
-.rx .k { }
-.rx .dl { color:#404 }
-.rx .mod { color:#C2C }
-.rx .fu { color:#404; font-weight: bold }
-
-.s { color: #D20; }
-.s .k { }
-.s .ch { color: #b0b; }
-.s .dl { color: #710; }
-
-.sh { background-color:hsla(120,100%,50%,0.1); color:#2B2 }
-.sh .k { }
-.sh .dl { color:#161 }
-
-.sy { color:#A60 }
-.sy .k { color:#A60 }
-.sy .dl { color:#630 }
-
-.ta { color:#070 }
-.ts { color:#D70; font-weight:bold }
-.ty { color:#339; font-weight:bold }
-.v { color:#036 }
-.xt { color:#444 }
-
-.ins { background: #afa; }
-.del { background: #faa; }
-.chg { color: #aaf; background: #007; }
-.head { color: #f8f; background: #505 }
-.head .filename { color: white; }
-
-.ins .ins { color: #080; font-weight:bold }
-.del .del { color: #800; font-weight:bold }
-.chg .chg { color: #66f; }
-.head .head { color: #f4f; }
\ No newline at end of file
+
+/* keywords */
+#changer .code span.r {
+ color: #0000ff;
+ font-weight: bold;
+}
+
+/* symbols */
+#changer .code span.sy {
+ color: #318495;
+}
+
+/* strings */
+#changer .code span.s {
+ color: #008800;
+}
+
+/* paren */
+#changer .code span.of {
+ color: #222;
+ font-weight: bold;
+}
+
+/* comment */
+#changer .code span.c {
+ color: #ccc;
+}
+
+/* operator */
+#changer .code span.cl {
+
+}
+
+/* number */
+#changer .code span.i {
+ color: #ff0000;
+}
+
+@media (max-width: 619px) {
+
+ #wrapper {
+ width: 100%;
+ }
+ #header {
+ margin: 20px auto;
+ }
+ #console div.jquery-console-inner {
+ width: auto;
+ }
+
+}
diff --git a/resources/public/javascript/jquery-console/.gitignore b/resources/public/javascript/jquery-console/.gitignore
new file mode 100644
index 0000000..ac8f968
--- /dev/null
+++ b/resources/public/javascript/jquery-console/.gitignore
@@ -0,0 +1,20 @@
+# Compiled source #
+###################
+*.com
+*.class
+*.dll
+*.exe
+*.o
+*.so
+*.pyc
+
+# Logs and databases #
+######################
+*.log
+
+# OS generated files #
+######################
+.DS_Store*
+ehthumbs.db
+Icon?
+Thumbs.db
diff --git a/resources/public/javascript/jquery-console/README b/resources/public/javascript/jquery-console/README
new file mode 100644
index 0000000..c5d2de3
--- /dev/null
+++ b/resources/public/javascript/jquery-console/README
@@ -0,0 +1,81 @@
+INTRODUCTION
+
+See demo.html. Or here: http://chrisdone.com/jquery-console/
+
+Options available:
+
+ autofocus bool Autofocus the terminal, rather than
+ having to click on it.
+
+ promptHistory bool Provide history support (kind of crappy,
+ needs doing properly.)
+
+ historyPreserveColumn bool Preserve the column you were one when
+ switching between history.
+
+ welcomeMessage string Just a first message to display on the
+ terminal.
+
+ promptLabel string Prompt string like 'JavaScript> '.
+
+ cols integer the number of cols, this value is only
+ used by the command completion to format
+ the list of results.
+
+ commandValidate function When user hits return, validate
+ whether to trigger commandHandle and
+ re-prompt.
+
+ commandHandle function Handle the command line, return a
+ string, boolean, or list
+ of {msg:"foo",className:"my-css-class"}.
+ commandHandle(line,report) is
+ called. Report function is for you
+ to report a result of the command
+ asynchronously.
+
+ commandComplete function Handle the command completion when the
+ tab key is pressed. It returns a list
+ of string completion suffixes.
+
+ animateScroll bool Whether to animate the scroll to
+ top. Currently disabled.
+
+ charInsertTrigger function Predicate for whether to allow
+ character insertion.
+ charInsertTrigger(char,line) is called.
+
+ cancelHandle function Handle a user-signaled interrupt.
+
+ fadeOnReset bool Whether to trigger a fade in/out when
+ the console is reset. Defaults to true.
+
+LICENSE
+
+Copyright 2010 Chris Done, Simon David Pratt. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ 1. Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the following
+ disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/resources/public/javascript/jquery-console/demo.html b/resources/public/javascript/jquery-console/demo.html
new file mode 100644
index 0000000..7f85186
--- /dev/null
+++ b/resources/public/javascript/jquery-console/demo.html
@@ -0,0 +1,221 @@
+
+
+
+ JQuery Console Demo
+
+
+
+
+
+
+
+
+
+
Simple console demo
+
Tested on:
+
+
Internet Explorer 6
+
Opera 10.01
+
Chromium 4.0.237.0 (Ubuntu build 31094)
+
Firefox 3.5.8
+
+
+
diff --git a/resources/public/javascript/jquery-1.4.2.min.js b/resources/public/javascript/jquery-console/jquery-1.4.2.min.js
similarity index 100%
rename from resources/public/javascript/jquery-1.4.2.min.js
rename to resources/public/javascript/jquery-console/jquery-1.4.2.min.js
diff --git a/resources/public/javascript/jquery-console/jquery.console.js b/resources/public/javascript/jquery-console/jquery.console.js
new file mode 100644
index 0000000..5c1ac3f
--- /dev/null
+++ b/resources/public/javascript/jquery-console/jquery.console.js
@@ -0,0 +1,755 @@
+// JQuery Console 1.0
+// Sun Feb 21 20:28:47 GMT 2010
+//
+// Copyright 2010 Chris Done, Simon David Pratt. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above
+// copyright notice, this list of conditions and the following
+// disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials
+// provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+// TESTED ON
+// Internet Explorer 6
+// Opera 10.01
+// Chromium 4.0.237.0 (Ubuntu build 31094)
+// Firefox 3.5.8, 3.6.2 (Mac)
+// Safari 4.0.5 (6531.22.7) (Mac)
+// Google Chrome 5.0.375.55 (Mac)
+
+(function($){
+ var isWebkit = !!~navigator.userAgent.indexOf(' AppleWebKit/');
+
+ $.fn.console = function(config){
+ ////////////////////////////////////////////////////////////////////////
+ // Constants
+ // Some are enums, data types, others just for optimisation
+ var keyCodes = {
+ // left
+ 37: moveBackward,
+ // right
+ 39: moveForward,
+ // up
+ 38: previousHistory,
+ // down
+ 40: nextHistory,
+ // backspace
+ 8: backDelete,
+ // delete
+ 46: forwardDelete,
+ // end
+ 35: moveToEnd,
+ // start
+ 36: moveToStart,
+ // return
+ 13: commandTrigger,
+ // tab
+ 18: doNothing,
+ // tab
+ 9: doComplete
+ };
+ 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
+ };
+ if(config.ctrlCodes) {
+ $.extend(ctrlCodes, config.ctrlCodes);
+ }
+ var altCodes = {
+ // M-f
+ 70: moveToNextWord,
+ // M-b
+ 66: moveToPreviousWord,
+ // M-d
+ 68: deleteNextWord
+ };
+ var shiftCodes = {
+ // return
+ 13: newLine,
+ };
+ var cursor = '';
+
+ ////////////////////////////////////////////////////////////////////////
+ // Globals
+ var container = $(this);
+ var inner = $('');
+ // erjiang: changed this from a text input to a textarea so we
+ // can get pasted newlines
+ var typer = $('');
+ // Prompt
+ var promptBox;
+ var prompt;
+ var promptLabel = config && config.promptLabel? config.promptLabel : "> ";
+ var continuedPromptLabel = config && config.continuedPromptLabel?
+ config.continuedPromptLabel : "> ";
+ var column = 0;
+ var promptText = '';
+ var restoreText = '';
+ var continuedText = '';
+ var fadeOnReset = config.fadeOnReset !== undefined ? config.fadeOnReset : true;
+ // Prompt history stack
+ var history = [];
+ var ringn = 0;
+ // For reasons unknown to The Sword of Michael himself, Opera
+ // triggers and sends a key character when you hit various
+ // keys like PgUp, End, etc. So there is no way of knowing
+ // when a user has typed '#' or End. My solution is in the
+ // typer.keydown and typer.keypress functions; I use the
+ // variable below to ignore the keypress event if the keydown
+ // event succeeds.
+ var cancelKeyPress = 0;
+ // When this value is false, the prompt will not respond to input
+ var acceptInput = true;
+ // When this value is true, the command has been canceled
+ var cancelCommand = false;
+
+ // External exports object
+ var extern = {};
+
+ ////////////////////////////////////////////////////////////////////////
+ // Main entry point
+ (function(){
+ container.append(inner);
+ inner.append(typer);
+ typer.css({position:'absolute',top:0,left:'-9999px'});
+ if (config.welcomeMessage)
+ message(config.welcomeMessage,'jquery-console-welcome');
+ newPromptBox();
+ if (config.autofocus) {
+ inner.addClass('jquery-console-focus');
+ typer.focus();
+ setTimeout(function(){
+ inner.addClass('jquery-console-focus');
+ typer.focus();
+ },100);
+ }
+ extern.inner = inner;
+ extern.typer = typer;
+ extern.scrollToBottom = scrollToBottom;
+ })();
+
+ ////////////////////////////////////////////////////////////////////////
+ // Reset terminal
+ extern.reset = function(){
+ var welcome = (typeof config.welcomeMessage != 'undefined');
+
+ var removeElements = function() {
+ inner.find('div').each(function(){
+ if (!welcome) {
+ $(this).remove();
+ } else {
+ welcome = false;
+ }
+ });
+ };
+
+ var focusConsole = function() {
+ inner.addClass('jquery-console-focus');
+ typer.focus();
+ };
+
+ if (fadeOnReset) {
+ inner.parent().fadeOut(function() {
+ removeElements();
+ newPromptBox();
+ inner.parent().fadeIn(focusConsole);
+ });
+ }
+ else {
+ removeElements();
+ newPromptBox();
+ focusConsole();
+ }
+ };
+
+ ////////////////////////////////////////////////////////////////////////
+ // Reset terminal
+ extern.notice = function(msg,style){
+ var n = $('').append($('').text(msg))
+ .css({visibility:'hidden'});
+ container.append(n);
+ var focused = true;
+ if (style=='fadeout')
+ setTimeout(function(){
+ n.fadeOut(function(){
+ n.remove();
+ });
+ },4000);
+ else if (style=='prompt') {
+ var a = $('
');
+ n.append(a);
+ focused = false;
+ a.click(function(){ n.fadeOut(function(){ n.remove();inner.css({opacity:1}) }); });
+ }
+ var h = n.height();
+ n.css({height:'0px',visibility:'visible'})
+ .animate({height:h+'px'},function(){
+ if (!focused) inner.css({opacity:0.5});
+ });
+ n.css('cursor','default');
+ return n;
+ };
+
+ ////////////////////////////////////////////////////////////////////////
+ // Make a new prompt box
+ function newPromptBox() {
+ column = 0;
+ promptText = '';
+ ringn = 0; // Reset the position of the history ring
+ enableInput();
+ promptBox = $('');
+ var label = $('');
+ var labelText = extern.continuedPrompt? continuedPromptLabel : promptLabel;
+ promptBox.append(label.text(labelText).show());
+ label.html(label.html().replace(' ',' '));
+ prompt = $('');
+ promptBox.append(prompt);
+ inner.append(promptBox);
+ updatePromptDisplay();
+ };
+
+ ////////////////////////////////////////////////////////////////////////
+ // Handle setting focus
+ container.click(function(){
+ // Don't mess with the focus if there is an active selection
+ if (window.getSelection().toString()) {
+ return false;
+ }
+
+ inner.addClass('jquery-console-focus');
+ inner.removeClass('jquery-console-nofocus');
+ if (isWebkit) {
+ typer.focusWithoutScrolling();
+ } else {
+ typer.css('position', 'fixed').focus();
+ }
+ scrollToBottom();
+ return false;
+ });
+
+ ////////////////////////////////////////////////////////////////////////
+ // Handle losing focus
+ typer.blur(function(){
+ inner.removeClass('jquery-console-focus');
+ inner.addClass('jquery-console-nofocus');
+ });
+
+ ////////////////////////////////////////////////////////////////////////
+ // Bind to the paste event of the input box so we know when we
+ // get pasted data
+ typer.bind('paste', function(e) {
+ // wipe typer input clean just in case
+ typer.val("");
+ // this timeout is required because the onpaste event is
+ // fired *before* the text is actually pasted
+ setTimeout(function() {
+ typer.consoleInsert(typer.val());
+ typer.val("");
+ }, 0);
+ });
+
+ ////////////////////////////////////////////////////////////////////////
+ // Handle key hit before translation
+ // For picking up control characters like up/left/down/right
+
+ typer.keydown(function(e){
+ cancelKeyPress = 0;
+ var keyCode = e.keyCode;
+ // C-c: cancel the execution
+ if(e.ctrlKey && keyCode == 67) {
+ cancelKeyPress = keyCode;
+ cancelExecution();
+ return false;
+ }
+ if (acceptInput) {
+ if (e.shiftKey && keyCode in shiftCodes) {
+ cancelKeyPress = keyCode;
+ (shiftCodes[keyCode])();
+ return false;
+ } else if (e.altKey && keyCode in altCodes) {
+ cancelKeyPress = keyCode;
+ (altCodes[keyCode])();
+ return false;
+ } else if (e.ctrlKey && keyCode in ctrlCodes) {
+ cancelKeyPress = keyCode;
+ (ctrlCodes[keyCode])();
+ return false;
+ } else if (keyCode in keyCodes) {
+ cancelKeyPress = keyCode;
+ (keyCodes[keyCode])();
+ return false;
+ }
+ }
+ });
+
+ ////////////////////////////////////////////////////////////////////////
+ // Handle key press
+ typer.keypress(function(e){
+ var keyCode = e.keyCode || e.which;
+ if (isIgnorableKey(e)) {
+ return false;
+ }
+ // C-v: don't insert on paste event
+ if ((e.ctrlKey || e.metaKey) && String.fromCharCode(keyCode).toLowerCase() == 'v') {
+ return true;
+ }
+ if (acceptInput && cancelKeyPress != keyCode && keyCode >= 32){
+ if (cancelKeyPress) return false;
+ if (
+ typeof config.charInsertTrigger == 'undefined' || (
+ typeof config.charInsertTrigger == 'function' &&
+ config.charInsertTrigger(keyCode,promptText)
+ )
+ ){
+ typer.consoleInsert(keyCode);
+ }
+ }
+ if (isWebkit) return false;
+ });
+
+ function isIgnorableKey(e) {
+ // for now just filter alt+tab that we receive on some platforms when
+ // user switches windows (goes away from the browser)
+ return ((e.keyCode == keyCodes.tab || e.keyCode == 192) && e.altKey);
+ };
+
+ ////////////////////////////////////////////////////////////////////////
+ // Rotate through the command history
+ function rotateHistory(n){
+ if (history.length == 0) return;
+ ringn += n;
+ if (ringn < 0) ringn = history.length;
+ else if (ringn > history.length) ringn = 0;
+ var prevText = promptText;
+ if (ringn == 0) {
+ promptText = restoreText;
+ } else {
+ promptText = history[ringn - 1];
+ }
+ if (config.historyPreserveColumn) {
+ if (promptText.length < column + 1) {
+ column = promptText.length;
+ } else if (column == 0) {
+ column = promptText.length;
+ }
+ } else {
+ column = promptText.length;
+ }
+ updatePromptDisplay();
+ };
+
+ function previousHistory() {
+ rotateHistory(-1);
+ };
+
+ function nextHistory() {
+ rotateHistory(1);
+ };
+
+ // Add something to the history ring
+ function addToHistory(line){
+ history.push(line);
+ restoreText = '';
+ };
+
+ // Delete the character at the current position
+ function deleteCharAtPos(){
+ if (column < promptText.length){
+ promptText =
+ promptText.substring(0,column) +
+ promptText.substring(column+1);
+ restoreText = promptText;
+ return true;
+ } else return false;
+ };
+
+ function backDelete() {
+ if (moveColumn(-1)){
+ deleteCharAtPos();
+ updatePromptDisplay();
+ }
+ };
+
+ function forwardDelete() {
+ if (deleteCharAtPos()){
+ updatePromptDisplay();
+ }
+ };
+
+ function deleteUntilEnd() {
+ while(deleteCharAtPos()) {
+ updatePromptDisplay();
+ }
+ };
+
+ function deleteNextWord() {
+ // A word is defined within this context as a series of alphanumeric
+ // characters.
+ // Delete up to the next alphanumeric character
+ while(
+ column < promptText.length &&
+ !isCharAlphanumeric(promptText[column])
+ ) {
+ deleteCharAtPos();
+ updatePromptDisplay();
+ }
+ // Then, delete until the next non-alphanumeric character
+ while(
+ column < promptText.length &&
+ isCharAlphanumeric(promptText[column])
+ ) {
+ deleteCharAtPos();
+ updatePromptDisplay();
+ }
+ };
+
+ function newLine() {
+ promptText += "\n";
+ moveColumn(1);
+ updatePromptDisplay();
+ };
+
+ ////////////////////////////////////////////////////////////////////////
+ // Validate command and trigger it if valid, or show a validation error
+ function commandTrigger() {
+ var line = promptText;
+ if (typeof config.commandValidate == 'function') {
+ var ret = config.commandValidate(line);
+ if (ret == true || ret == false) {
+ if (ret) {
+ handleCommand();
+ }
+ } else {
+ commandResult(ret,"jquery-console-message-error");
+ }
+ } else {
+ handleCommand();
+ }
+ };
+
+ // Scroll to the bottom of the view
+ function scrollToBottom() {
+ var version = jQuery.fn.jquery.split('.');
+ var major = parseInt(version[0]);
+ var minor = parseInt(version[1]);
+
+ // check if we're using jquery > 1.6
+ if ((major == 1 && minor > 6) || major > 1) {
+ inner.prop({ scrollTop: inner.prop("scrollHeight") });
+ }
+ else {
+ inner.attr({ scrollTop: inner.attr("scrollHeight") });
+ }
+ };
+
+ function cancelExecution() {
+ if(typeof config.cancelHandle == 'function') {
+ config.cancelHandle();
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ // Handle a command
+ function handleCommand() {
+ if (typeof config.commandHandle == 'function') {
+ disableInput();
+ addToHistory(promptText);
+ var text = promptText;
+ if (extern.continuedPrompt) {
+ if (continuedText)
+ continuedText += '\n' + promptText;
+ else continuedText = promptText;
+ } else continuedText = undefined;
+ if (continuedText) text = continuedText;
+ var ret = config.commandHandle(text,function(msgs){
+ commandResult(msgs);
+ });
+ if (extern.continuedPrompt && !continuedText)
+ continuedText = promptText;
+ if (typeof ret == 'boolean') {
+ if (ret) {
+ // Command succeeded without a result.
+ commandResult();
+ } else {
+ commandResult(
+ 'Command failed.',
+ "jquery-console-message-error"
+ );
+ }
+ } else if (typeof ret == "string") {
+ commandResult(ret,"jquery-console-message-success");
+ } else if (typeof ret == 'object' && ret.length) {
+ commandResult(ret);
+ } else if (extern.continuedPrompt) {
+ commandResult();
+ }
+ }
+ };
+
+ ////////////////////////////////////////////////////////////////////////
+ // Disable input
+ function disableInput() {
+ acceptInput = false;
+ };
+
+ // Enable input
+ function enableInput() {
+ acceptInput = true;
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ // Reset the prompt in invalid command
+ function commandResult(msg,className) {
+ column = -1;
+ updatePromptDisplay();
+ if (typeof msg == 'string') {
+ message(msg,className);
+ } else if ($.isArray(msg)) {
+ for (var x in msg) {
+ var ret = msg[x];
+ message(ret.msg,ret.className);
+ }
+ } else { // Assume it's a DOM node or jQuery object.
+ inner.append(msg);
+ }
+ newPromptBox();
+ };
+
+ ////////////////////////////////////////////////////////////////////////
+ // Display a message
+ function message(msg,className) {
+ var mesg = $('');
+ if (className) mesg.addClass(className);
+ mesg.filledText(msg).hide();
+ inner.append(mesg);
+ mesg.show();
+ };
+
+ ////////////////////////////////////////////////////////////////////////
+ // Handle normal character insertion
+ // data can either be a number, which will be interpreted as the
+ // numeric value of a single character, or a string
+ typer.consoleInsert = function(data){
+ // TODO: remove redundant indirection
+ var text = isNaN(data) ? data : String.fromCharCode(data);
+ var before = promptText.substring(0,column);
+ var after = promptText.substring(column);
+ promptText = before + text + after;
+ moveColumn(text.length);
+ restoreText = promptText;
+ updatePromptDisplay();
+ };
+
+ ////////////////////////////////////////////////////////////////////////
+ // Move to another column relative to this one
+ // Negative means go back, positive means go forward.
+ function moveColumn(n){
+ if (column + n >= 0 && column + n <= promptText.length){
+ column += n;
+ return true;
+ } else return false;
+ };
+
+ function moveForward() {
+ if(moveColumn(1)) {
+ updatePromptDisplay();
+ return true;
+ }
+ return false;
+ };
+
+ function moveBackward() {
+ if(moveColumn(-1)) {
+ updatePromptDisplay();
+ return true;
+ }
+ return false;
+ };
+
+ function moveToStart() {
+ if (moveColumn(-column))
+ updatePromptDisplay();
+ };
+
+ function moveToEnd() {
+ if (moveColumn(promptText.length-column))
+ updatePromptDisplay();
+ };
+
+ function moveToNextWord() {
+ while(
+ column < promptText.length &&
+ !isCharAlphanumeric(promptText[column]) &&
+ moveForward()
+ ) {}
+ while(
+ column < promptText.length &&
+ isCharAlphanumeric(promptText[column]) &&
+ moveForward()
+ ) {}
+ };
+
+ function moveToPreviousWord() {
+ // Move backward until we find the first alphanumeric
+ while(
+ column -1 >= 0 &&
+ !isCharAlphanumeric(promptText[column-1]) &&
+ moveBackward()
+ ) {}
+ // Move until we find the first non-alphanumeric
+ while(
+ column -1 >= 0 &&
+ isCharAlphanumeric(promptText[column-1]) &&
+ moveBackward()
+ ) {}
+ };
+
+ function isCharAlphanumeric(charToTest) {
+ if(typeof charToTest == 'string') {
+ var code = charToTest.charCodeAt();
+ return (code >= 'A'.charCodeAt() && code <= 'Z'.charCodeAt()) ||
+ (code >= 'a'.charCodeAt() && code <= 'z'.charCodeAt()) ||
+ (code >= '0'.charCodeAt() && code <= '9'.charCodeAt());
+ }
+ return false;
+ };
+
+ function doComplete() {
+ if(typeof config.completeHandle == 'function') {
+ var completions = config.completeHandle(promptText);
+ var len = completions.length;
+ if (len === 1) {
+ extern.promptText(promptText + completions[0]);
+ } else if (len > 1 && config.cols) {
+ var prompt = promptText;
+ // Compute the number of rows that will fit in the width
+ var max = 0;
+ for (var i = 0;i < len;i++) {
+ max = Math.max(max, completions[i].length);
+ }
+ max += 2;
+ var n = Math.floor(config.cols / max);
+ var buffer = "";
+ var col = 0;
+ for (i = 0;i < len;i++) {
+ var completion = completions[i];
+ buffer += completions[i];
+ for (var j = completion.length;j < max;j++) {
+ buffer += " ";
+ }
+ if (++col >= n) {
+ buffer += "\n";
+ col = 0;
+ }
+ }
+ commandResult(buffer,"jquery-console-message-value");
+ extern.promptText(prompt);
+ }
+ }
+ };
+
+ function doNothing() {};
+
+ extern.promptText = function(text){
+ if (typeof text === 'string') {
+ promptText = text;
+ column = promptText.length;
+ updatePromptDisplay();
+ }
+ return promptText;
+ };
+
+ ////////////////////////////////////////////////////////////////////////
+ // Update the prompt display
+ function updatePromptDisplay(){
+ var line = promptText;
+ var html = '';
+ if (column > 0 && line == ''){
+ // When we have an empty line just display a cursor.
+ html = cursor;
+ } else if (column == promptText.length){
+ // We're at the end of the line, so we need to display
+ // the text *and* cursor.
+ html = htmlEncode(line) + cursor;
+ } else {
+ // Grab the current character, if there is one, and
+ // make it the current cursor.
+ var before = line.substring(0, column);
+ var current = line.substring(column,column+1);
+ if (current){
+ current =
+ '' +
+ htmlEncode(current) +
+ '';
+ }
+ var after = line.substring(column+1);
+ html = htmlEncode(before) + current + htmlEncode(after);
+ }
+ prompt.html(html);
+ scrollToBottom();
+ };
+
+ // Simple HTML encoding
+ // Simply replace '<', '>' and '&'
+ // TODO: Use jQuery's .html() trick, or grab a proper, fast
+ // HTML encoder.
+ function htmlEncode(text){
+ return (
+ text.replace(/&/g,'&')
+ .replace(/')
+ );
+ };
+
+ return extern;
+ };
+ // Simple utility for printing messages
+ $.fn.filledText = function(txt){
+ $(this).text(txt);
+ $(this).html($(this).html().replace(/\n/g,' '));
+ return this;
+ };
+
+ // Alternative method for focus without scrolling
+ $.fn.focusWithoutScrolling = function(){
+ var x = window.scrollX, y = window.scrollY;
+ $(this).focus();
+ window.scrollTo(x, y);
+ };
+})(jQuery);
diff --git a/resources/public/javascript/jquery.console.js b/resources/public/javascript/jquery.console.js
deleted file mode 100644
index aae45ce..0000000
--- a/resources/public/javascript/jquery.console.js
+++ /dev/null
@@ -1,486 +0,0 @@
-// JQuery Console 1.0
-// Sun Feb 21 20:28:47 GMT 2010
-//
-// Copyright 2010 Chris Done. All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions
-// are met:
-//
-// 1. Redistributions of source code must retain the above
-// copyright notice, this list of conditions and the following
-// disclaimer.
-
-// 2. Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials
-// provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY 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.
-//
-// TESTED ON
-// Internet Explorer 6
-// Opera 10.01
-// Chromium 4.0.237.0 (Ubuntu build 31094)
-// Firefox 3.5.8
-
-(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 cursor = '';
- // Opera only works with this character, not or ,
- // but IE6 displays this character, which is bad, so just use
- // it on Opera.
- var wbr = $.browser.opera? '' : '';
-
- ////////////////////////////////////////////////////////////////////////
- // Globals
- var container = $(this);
- var inner = $('');
- var typer = $('');
- // Prompt
- var promptBox;
- var prompt;
- var promptLabel = config && config.promptLabel? config.promptLabel : "> ";
- var column = 0;
- var promptText = '';
- var restoreText = '';
- // Prompt history stack
- var history = [];
- var ringn = 0;
- // For reasons unknown to The Sword of Michael himself, Opera
- // triggers and sends a key character when you hit various
- // keys like PgUp, End, etc. So there is no way of knowing
- // when a user has typed '#' or End. My solution is in the
- // typer.keydown and typer.keypress functions; I use the
- // variable below to ignore the keypress event if the keydown
- // event succeeds.
- var cancelKeyPress = 0;
-
- // External exports object
- var extern = {};
-
- ////////////////////////////////////////////////////////////////////////
- // Main entry point
- (function(){
- container.append(inner);
- inner.append(typer);
- typer.css({position:'absolute',top:0,left:'-9999px'});
- if (config.welcomeMessage)
- message(config.welcomeMessage,'jquery-console-welcome');
- newPromptBox();
- if (config.autofocus) {
- inner.addClass('jquery-console-focus');
- typer.focus();
- setTimeout(function(){
- inner.addClass('jquery-console-focus');
- typer.focus();
- },100);
- }
- extern.inner = inner;
- extern.scrollToBottom = scrollToBottom;
- })();
-
- ////////////////////////////////////////////////////////////////////////
- // Reset terminal
- extern.reset = function(){
- var welcome = true;
- inner.parent().fadeOut(function(){
- inner.find('div').each(function(){
- if (!welcome)
- $(this).remove();
- welcome = false;
- });
- newPromptBox();
- inner.parent().fadeIn(function(){
- inner.addClass('jquery-console-focus');
- typer.focus();
- });
- });
- };
-
- ////////////////////////////////////////////////////////////////////////
- // Reset terminal
- extern.notice = function(msg,style){
- var n = $('').append($('').text(msg))
- .css({visibility:'hidden'});
- container.append(n);
- var focused = true;
- if (style=='fadeout')
- setTimeout(function(){
- n.fadeOut(function(){
- n.remove();
- });
- },4000);
- else if (style=='prompt') {
- var a = $('
+ Vectors and lists are sequential and ordered collections.
+ Sets are not ordered, and they cannot contain duplicate elements.
+ Maps are key-value collections, where the keys can be any object.
+ Here, we've used what Clojure calls a keyword (:foo) for one of the keys, and a number for the other key.
+
+
+
+ Now I'll tell you another thing that may surprise you: Clojure collections are immutable - they can never change.
+ When you do anything on a list, including adding and removing elements, you actually get a brand new list.
+ (Fortunately, Clojure is amazingly efficient at creating new lists).
+ In general, Clojure encourages you to have as little mutable state as possible.
+ For example, instead of "for" loops and other state-changing constructs, most of the time you'll see functions doing transformations on immutable data and returning new collections, without changing the old one.
+
+
+
+ A prime example of this is map. map is a higher order function, which means that it takes another function as an argument.
+ For example, you can ask map to increment each number in a vector by passing it the inc function, followed by the vector.
+ Try it for yourself: type (map inc [1 2 3 4]) to continue.
+
diff --git a/resources/public/tutorial/page11.html b/resources/public/tutorial/page11.html
new file mode 100644
index 0000000..726fe3c
--- /dev/null
+++ b/resources/public/tutorial/page11.html
@@ -0,0 +1,11 @@
+
Great job!
+
+
+ We've only scratched the surface of Clojure and its mind-bending power.
+ This tutorial is still a work in progress, and I'm working on more steps.
+ Meanwhile, you can learn more about Clojure by visiting the 'links' page.
+
+
+
+ Welcome to your adventures in Clojure, and be prepared to be surprised and delighted every step of the way!
+
diff --git a/resources/public/tutorial/page2.html b/resources/public/tutorial/page2.html
new file mode 100644
index 0000000..1d11c62
--- /dev/null
+++ b/resources/public/tutorial/page2.html
@@ -0,0 +1,7 @@
+
+ The first thing you may notice about Clojure is that common operations look... strange.
+
+
+
+ For example, try typing (+ 3 3) in the REPL.
+
diff --git a/resources/public/tutorial/page3.html b/resources/public/tutorial/page3.html
new file mode 100644
index 0000000..ae8cc9b
--- /dev/null
+++ b/resources/public/tutorial/page3.html
@@ -0,0 +1,13 @@
+
+ That was a strange way to say "three plus three", wasn't it?
+
+
+
+ A Clojure program is made of lists.
+ (+ 3 3) is a list that contains an operator, and then the operands.
+ Try out the same concept with the * and - operators.
+
+
+
+ Division might surprise you. When you're ready to move forward, try (/ 10 3).
+
diff --git a/resources/public/tutorial/page4.html b/resources/public/tutorial/page4.html
new file mode 100644
index 0000000..991dc20
--- /dev/null
+++ b/resources/public/tutorial/page4.html
@@ -0,0 +1,4 @@
+
+ Now, that was a bit surprising: Clojure has a built in Rational type.
+ You can still force Clojure to do floating point division by making one of the operands floating point: type (/ 10 3.0) to continue.
+
diff --git a/resources/public/tutorial/page5.html b/resources/public/tutorial/page5.html
new file mode 100644
index 0000000..1788c09
--- /dev/null
+++ b/resources/public/tutorial/page5.html
@@ -0,0 +1,6 @@
+
Awesome!
+
+
+ Many Clojure functions can take an arbitrary number of arguments.
+ Try it out: type (+ 1 2 3 4 5 6) to continue.
+
diff --git a/resources/public/tutorial/page6.html b/resources/public/tutorial/page6.html
new file mode 100644
index 0000000..daa7698
--- /dev/null
+++ b/resources/public/tutorial/page6.html
@@ -0,0 +1,8 @@
+
+ That's enough math. Let's do some fun stuff, like defining functions.
+ You can do that in Clojure with defn.
+
+
+
+ Type (defn square [x] (* x x)) to define a "square" function that takes a single number and squares it.
+
diff --git a/resources/public/tutorial/page7.html b/resources/public/tutorial/page7.html
new file mode 100644
index 0000000..e6c58c0
--- /dev/null
+++ b/resources/public/tutorial/page7.html
@@ -0,0 +1,13 @@
+
Congratulations - you just defined your first Clojure function. Many more will follow!
+
+
+ defn takes the name of the function, then the list of arguments, and then the body of the function.
+ I told you that a Clojure program is made of lists, right?
+ The entire defn is a list, and the function body is also a list.
+ (Even the arguments are collected in a vector, which is similar to a list - we'll talk about vectors soon).
+
+
+
+ Oh, sorry for talking so long - you probably want to try out your brand new function!
+ Type (square 10).
+
diff --git a/resources/public/tutorial/page8.html b/resources/public/tutorial/page8.html
new file mode 100644
index 0000000..d3a26cf
--- /dev/null
+++ b/resources/public/tutorial/page8.html
@@ -0,0 +1,20 @@
+
Yay! It works!
+
+
+ By now, you probably think that Clojure is very different from the programming languages you already know.
+ Indeed, it belongs to a different family than most popular languages' - the family of "functional" programming languages.
+ Like most functional languages, Clojure can define a function without even giving it a name:
+
+
+(fn [x] (* x x))
+
+
+ If you run this code, you'll see some cryptic output.
+ In Clojure, functions are just normal values like numbers or strings.
+ fn defines a function and then returns it.
+ What you're seeing is simply what a function looks like when you print it on the screen.
+
+
+
+ But wait - an anonymous function isn't very useful if you can't call it. Try to define a new anonymous function and call it straight away: ((fn [x] (* x x)) 10).
+
diff --git a/resources/public/tutorial/page9.html b/resources/public/tutorial/page9.html
new file mode 100644
index 0000000..44e17cc
--- /dev/null
+++ b/resources/public/tutorial/page9.html
@@ -0,0 +1,16 @@
+
+ Let's see what you just did: you evaluated a list where the first element is the function itself, defined on the spot - and the other elements are the arguments that you pass to the function.
+ That's exactly the same syntax that you used earlier on to call functions like square or even +.
+ The only difference is that now you defined the function in the same place where you called it.
+
+
+
+ Remember defn?
+ Now I can tell you a secret: defn is actually just a bit of syntactic sugar around def and fn.
+ You've just seen fn at work: it defines a new function.
+ def binds the newly defined function to a name.
+
+
+
+ If you want, you can create a named function without using defn: type (def square (fn [x] (* x x))) to continue.
+