From a87e6d26c5a4925ade9f24ae1741abd7c3d3f351 Mon Sep 17 00:00:00 2001 From: sostler Date: Sun, 8 Nov 2009 23:45:24 -0500 Subject: Initial Checkin --- bin/repl.bat | 3 + bin/run | 3 + bin/run.bat | 3 + bin/run.sh | 1 + lib/clojure-contrib.jar | Bin 3328562 -> 2945084 bytes lib/clojure.jar | Bin 1780580 -> 1534952 bytes lib/commons-codec-1.3.jar | Bin 0 -> 46725 bytes lib/commons-fileupload-1.2.1.jar | Bin 0 -> 57779 bytes lib/commons-io-1.4.jar | Bin 0 -> 109043 bytes lib/compojure.jar | Bin 0 -> 400825 bytes lib/compojureold.jar | Bin 0 -> 453316 bytes lib/jetty-6.1.14.jar | Bin 0 -> 516429 bytes lib/jetty-util-6.1.14.jar | Bin 0 -> 163122 bytes lib/jline-0.9.94.jar | Bin 0 -> 87325 bytes lib/servlet-api-2.5-6.1.14.jar | Bin 0 -> 132367 bytes src/site.clj | 71 ++++++++++++++++ static/index.html | 23 ++++++ static/pichat.css | 53 ++++++++++++ static/pichat.js | 170 +++++++++++++++++++++++++++++++++++++++ static/reset.css | 54 +++++++++++++ static/spinner.gif | Bin 0 -> 1849 bytes static/test.html | 75 +++++++++++++++++ 22 files changed, 456 insertions(+) create mode 100755 bin/repl.bat create mode 100755 bin/run create mode 100755 bin/run.bat create mode 100755 bin/run.sh mode change 100644 => 100755 lib/clojure-contrib.jar mode change 100644 => 100755 lib/clojure.jar create mode 100755 lib/commons-codec-1.3.jar create mode 100755 lib/commons-fileupload-1.2.1.jar create mode 100755 lib/commons-io-1.4.jar create mode 100755 lib/compojure.jar create mode 100755 lib/compojureold.jar create mode 100755 lib/jetty-6.1.14.jar create mode 100755 lib/jetty-util-6.1.14.jar create mode 100755 lib/jline-0.9.94.jar create mode 100755 lib/servlet-api-2.5-6.1.14.jar create mode 100755 src/site.clj create mode 100755 static/index.html create mode 100755 static/pichat.css create mode 100755 static/pichat.js create mode 100755 static/reset.css create mode 100755 static/spinner.gif create mode 100755 static/test.html diff --git a/bin/repl.bat b/bin/repl.bat new file mode 100755 index 0000000..25bb737 --- /dev/null +++ b/bin/repl.bat @@ -0,0 +1,3 @@ +REM Windows REPL script + +java -cp lib/commons-io-1.4.jar;lib/commons-fileupload-1.2.1.jar;lib/commons-codec-1.3.jar;lib/clojure.jar;lib/clojure-contrib.jar;lib/compojure.jar;lib/jetty-6.1.14.jar;lib/jetty-util-6.1.14.jar;lib/servlet-api-2.5-6.1.14.jar;lib/jline-0.9.94.jar jline.ConsoleRunner clojure.lang.Repl %1 \ No newline at end of file diff --git a/bin/run b/bin/run new file mode 100755 index 0000000..c2af849 --- /dev/null +++ b/bin/run @@ -0,0 +1,3 @@ +#!/bin/sh + +java -cp .:lib/commons-io-1.4.jar:lib/commons-fileupload-1.2.1.jar:lib/commons-codec-1.3.jar:lib/jline-0.9.94.jar:lib/clojure.jar:lib/clojure-contrib.jar:lib/compojure.jar:lib/jetty-6.1.14.jar:lib/jetty-util-6.1.14.jar:lib/servlet-api-2.5-6.1.14.jar:classes jline.ConsoleRunner clojure.lang.Script \ No newline at end of file diff --git a/bin/run.bat b/bin/run.bat new file mode 100755 index 0000000..bec30c8 --- /dev/null +++ b/bin/run.bat @@ -0,0 +1,3 @@ +REM Windows runner script + +java -cp lib/commons-io-1.4.jar;lib/commons-fileupload-1.2.1.jar;lib/commons-codec-1.3.jar;lib/clojure.jar;lib/clojure-contrib.jar;lib/compojure.jar;lib/jetty-6.1.14.jar;lib/jetty-util-6.1.14.jar;lib/servlet-api-2.5-6.1.14.jar clojure.lang.Script %1 \ No newline at end of file diff --git a/bin/run.sh b/bin/run.sh new file mode 100755 index 0000000..34998e0 --- /dev/null +++ b/bin/run.sh @@ -0,0 +1 @@ +java -cp lib/*; clojure.lang.Repl src/site.clj \ No newline at end of file diff --git a/lib/clojure-contrib.jar b/lib/clojure-contrib.jar old mode 100644 new mode 100755 index 7b58ad2..aca1fec Binary files a/lib/clojure-contrib.jar and b/lib/clojure-contrib.jar differ diff --git a/lib/clojure.jar b/lib/clojure.jar old mode 100644 new mode 100755 index 75ced67..fce575b Binary files a/lib/clojure.jar and b/lib/clojure.jar differ diff --git a/lib/commons-codec-1.3.jar b/lib/commons-codec-1.3.jar new file mode 100755 index 0000000..957b675 Binary files /dev/null and b/lib/commons-codec-1.3.jar differ diff --git a/lib/commons-fileupload-1.2.1.jar b/lib/commons-fileupload-1.2.1.jar new file mode 100755 index 0000000..aa209b3 Binary files /dev/null and b/lib/commons-fileupload-1.2.1.jar differ diff --git a/lib/commons-io-1.4.jar b/lib/commons-io-1.4.jar new file mode 100755 index 0000000..133dc6c Binary files /dev/null and b/lib/commons-io-1.4.jar differ diff --git a/lib/compojure.jar b/lib/compojure.jar new file mode 100755 index 0000000..29759c0 Binary files /dev/null and b/lib/compojure.jar differ diff --git a/lib/compojureold.jar b/lib/compojureold.jar new file mode 100755 index 0000000..8d3dd11 Binary files /dev/null and b/lib/compojureold.jar differ diff --git a/lib/jetty-6.1.14.jar b/lib/jetty-6.1.14.jar new file mode 100755 index 0000000..3e67d1e Binary files /dev/null and b/lib/jetty-6.1.14.jar differ diff --git a/lib/jetty-util-6.1.14.jar b/lib/jetty-util-6.1.14.jar new file mode 100755 index 0000000..7acc988 Binary files /dev/null and b/lib/jetty-util-6.1.14.jar differ diff --git a/lib/jline-0.9.94.jar b/lib/jline-0.9.94.jar new file mode 100755 index 0000000..dafca7c Binary files /dev/null and b/lib/jline-0.9.94.jar differ diff --git a/lib/servlet-api-2.5-6.1.14.jar b/lib/servlet-api-2.5-6.1.14.jar new file mode 100755 index 0000000..fbb5a04 Binary files /dev/null and b/lib/servlet-api-2.5-6.1.14.jar differ diff --git a/src/site.clj b/src/site.clj new file mode 100755 index 0000000..33b1bb3 --- /dev/null +++ b/src/site.clj @@ -0,0 +1,71 @@ +; site.clj + +(ns pichat + (:import java.lang.System) + (:use compojure + clojure.contrib.json.write)) + +(defstruct user-struct :nick :last-seen) +(defstruct message-struct :nick :content :timestamp) + +(def users (ref {})) +(def messages (ref [])) + +(defn resp-error [message] + {:status 400 :headers {} :body message}) + +(defn resp-success [message] + {:status 200 :headers {} :body (json-str message)}) + +(defn join-success [nick] + (alter users assoc nick (struct user-struct nick (System/currentTimeMillis))) + (let [users (keys @users) + messages (take 20 @messages) + data {"users" users "messages" messages}] + [(session-assoc :nick nick) + (resp-success data)])) + +(defn try-join [params] + (let [nick (params :nick)] + (dosync + (if (contains? @users nick) + (resp-error "NICK_TAKEN") + (join-success nick))))) + +(defn new-messages [since] + (reverse (take-while (fn [m] (> (m :timestamp) since)) @messages))) + +(defn refresh [nick] + (dosync + (let [last-seen (get-in @users [nick :last-seen])] + (alter users assoc-in [nick :last-seen] (System/currentTimeMillis)) + (resp-success {"messages" (new-messages last-seen)})))) + +(defn swap [f] + (fn [& more] (apply f (reverse more)))) + +(defn msg [session params] + (dosync + (let [nick (session :nick) + content (params :content) + msg (struct message-struct nick content (System/currentTimeMillis))] + (if (contains? @users nick) + (do (alter messages (swap cons) msg) + (resp-success "OK")) + (resp-error "UNKNOWN_USER"))))) + +(defroutes pichat + (GET "/" (serve-file "static" "index.html")) + (GET "/static/*" (or (serve-file "static" (params :*)) + :next)) + (GET "/join" (try-join params)) + (GET "/refresh" (refresh (session :nick))) + (GET "/msg" (msg session params)) + (ANY "*" [404 "Page not found"])) + +(decorate pichat + (with-mimetypes) + (with-session {:type :memory, :expires (* 60 60)})) + +(run-server {:port 8080} + "/*" (servlet pichat)) diff --git a/static/index.html b/static/index.html new file mode 100755 index 0000000..bd75ab1 --- /dev/null +++ b/static/index.html @@ -0,0 +1,23 @@ + + + Pichat + + + + + + + +
+

Welcome to Pichat!

+ + + +
+ + diff --git a/static/pichat.css b/static/pichat.css new file mode 100755 index 0000000..383ba7a --- /dev/null +++ b/static/pichat.css @@ -0,0 +1,53 @@ +/* pichat.css */ + +html, body { + padding: 1em; +} + +#content, #chatbox { + position: relative; +} + +#messagePane { + border: 1px solid green; + height: 600px; + padding: 5px; + position: absolute; + width: 540px; +} + +#messageList { + height: 575px; + width: 540px; + overflow-y: auto; + overflow-x: hidden; + border-bottom: 1px solid grey; +} + +#msgInputDiv { + height: 15px; +} + +#msgInput { + width: 85%; + margin-right: 5px; +} + +#msgSubmit { + width: 14%; +} + +.msgDiv { + width: 520px; +} + +#userList { + overflow: auto; + border: 1px solid blue; + height: 600px; + margin: 0px; + position: absolute; + padding: 5px; + left: 550px; + width: 150px; +} \ No newline at end of file diff --git a/static/pichat.js b/static/pichat.js new file mode 100755 index 0000000..b12c772 --- /dev/null +++ b/static/pichat.js @@ -0,0 +1,170 @@ +// pichat.js + +var Nick = null; + +function handleJoinError(resp) { + var respText = resp.responseText ? resp.responseText.trim() : false; + if (respText == 'NICK_TAKEN') { + alert("Nick '" + Nick + "' was taken! Please choose another."); + } else if (respText) { + alert("Cannot join! (" + respText + ")"); + } else { + alert("Cannot join! Please try again later."); + } +} + +function handleMsgError(resp) { + var respText = resp.responseText ? resp.responseText.trim() : false; + if (respText == 'UNKNOWN_USER') { + alert("Can't send message! Please login."); + } else if (respText) { + alert("Cannot send message! (" + respText + ")"); + } else { + alert("Cannot send message!"); + } +} + +function join() { + $('#join, #nick').attr('disabled', true); + $('#loginspinner').show(); + Nick = $('#nick').val(); + + onSuccess = function(json) { + generateChatInterface(json.users, json.messages); + }; + + onError = function(resp, textStatus, errorThrown) { + $('#join, #nick').attr('disabled', false); + $('#loginspinner').hide(); + handleJoinError(resp); + }; + + $.ajax({ + type: 'GET', + timeout: 5000, + url: 'join', + data: {'nick': Nick }, + cache: false, + dataType: 'json', + success: onSuccess, + error: onError + }); +} + +function buildUserDiv(user) { + return '
' + user + '
'; +} + +// http://snippets.dzone.com/posts/show/6995 +var URLRegex = /((http\:\/\/|https\:\/\/|ftp\:\/\/)|(www\.))+(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/i; + +function buildMessageDiv(msg) { + var match = URLRegex.exec(msg.content) + if (match) { + return '
' + msg.nick + ': ' + + '
'; + } else { + return '
' + msg.nick + ": " + + msg.content + "
"; + } +} + +function buildChatInterface(users, messages) { + var userList = '
' + + $.map(users, buildUserDiv).join('') + '
'; + var messageList = '
' + + '
' + + $.map(messages, buildMessageDiv).join('') + + '
' + + '
' + + '' + + '' + + '
' + + '
'; + return '

Pichat

' + userList + messageList + '
'; +} + +function submitMessage() { + var content = $('#msgInput').val(); + var msg = { 'nick': Nick, 'content': content, 'timestamp': new Date() }; + if (content == '') { return; } + + var shouldScroll = isScrolledToBottom($('#messageList')[0]); + + $('#messageList').append($(buildMessageDiv(msg))); + $('#msgInput').val(''); + + if (shouldScroll) { + scrollToBottom($('#messageList')[0]); + } + + var onSuccess = function() {}; + var onError = function(resp, textStatus, errorThrown) { + handleMsgError(resp); + }; + + $.ajax({ + type: 'GET', + timeout: 5000, + url: 'msg', + data: {'content': content }, + cache: false, + dataType: 'json', + success: onSuccess, + error: onError + }); +} + +function ifEnter(fn) { + return function(e) { + if (e.keyCode == 13) { fn(); } + }; +} + +function isScrolledToBottom(div) { + return Math.abs(div.scrollTop - (div.scrollHeight - div.offsetHeight)) <= 3; +} + +function scrollToBottom(div) { + div.scrollTop = div.scrollHeight; +} + +function refresh() { + var onSuccess = function(json) { + if (json.messages.length == 0) { + return; + } + + var shouldScroll = isScrolledToBottom($('#messageList')[0]); + + // Ignore our own messages + var filterFunc = function(m) { return m.nick != Nick }; + var msgStr = $.map($.grep(json.messages, filterFunc), + buildMessageDiv).join(''); + $('#messageList').append(msgStr); + + if (shouldScroll) { + scrollToBottom($('#messageList')[0]); + } + }; + + var onError = function(resp, textStatus, errorThrown) {}; + + $.ajax({ + type: 'GET', + timeout: 5000, + url: 'refresh', + cache: false, + dataType: 'json', + success: onSuccess, + error: onError + }); +} + +function generateChatInterface(users, messages) { + $('#content').html(buildChatInterface(users, messages)); + $('#msgInput').keyup(ifEnter(submitMessage)); + $('#msgSubmit').click(submitMessage); + setInterval(refresh, 1000); +} + diff --git a/static/reset.css b/static/reset.css new file mode 100755 index 0000000..8767cdd --- /dev/null +++ b/static/reset.css @@ -0,0 +1,54 @@ +/* reset.css + From http://meyerweb.com/eric/tools/css/reset/ + v1.0 | 20080212 */ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, font, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-size: 100%; + vertical-align: baseline; + background: transparent; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} + +/* remember to define focus styles! */ +:focus { + outline: 0; +} + +/* remember to highlight inserts somehow! */ +ins { + text-decoration: none; +} +del { + text-decoration: line-through; +} + +/* tables still need 'cellspacing="0"' in the markup */ +table { + border-collapse: collapse; + border-spacing: 0; +} \ No newline at end of file diff --git a/static/spinner.gif b/static/spinner.gif new file mode 100755 index 0000000..5b33f7e Binary files /dev/null and b/static/spinner.gif differ diff --git a/static/test.html b/static/test.html new file mode 100755 index 0000000..1c9bfbe --- /dev/null +++ b/static/test.html @@ -0,0 +1,75 @@ + + + + + + +
+
+ I saw the best minds of my generation destroyed by +madness, starving hysterical naked, +dragging themselves through the negro streets at dawn +looking for an angry fix, +angelheaded hipsters burning for the ancient heavenly +connection to the starry dynamo in the machin- +ery of night, +who poverty and tatters and hollow-eyed and high sat +up smoking in the supernatural darkness of +cold-water flats floating across the tops of cities +contemplating jazz, +who bared their brains to Heaven under the El and +saw Mohammedan angels staggering on tene- +ment roofs illuminated, +who passed through universities with radiant cool eyes +hallucinating Arkansas and Blake-light tragedy +among the scholars of war, +who were expelled from the academies for crazy & +publishing obscene odes on the windows of the +skull, +who cowered in unshaven rooms in underwear, burn- +ing their money in wastebaskets and listening +to the Terror through the wall, +who got busted in their pubic beards returning through +Laredo with a belt of marijuana for New York, +who ate fire in paint hotels or drank turpentine in +Paradise Alley, death, or purgatoried their +torsos night after night +with dreams, with drugs, with waking nightmares, al- +cohol and cock and endless balls, +incomparable blind; streets of shuddering cloud and +lightning in the mind leaping toward poles of +
+ +
+ + + -- cgit v1.2.3-70-g09d2