clementine 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +1 -0
- data/Gemfile +4 -2
- data/LICENSE.txt +22 -0
- data/README.md +11 -6
- data/Rakefile +22 -0
- data/clementine.gemspec +2 -1
- data/ext/clojure-clojurescript-bef56a7/.gitignore +13 -0
- data/ext/clojure-clojurescript-bef56a7/Clojurescript.iml +12 -0
- data/ext/clojure-clojurescript-bef56a7/README.md +29 -0
- data/ext/clojure-clojurescript-bef56a7/benchmark/cljs/benchmark_runner.cljs +155 -0
- data/ext/clojure-clojurescript-bef56a7/bin/cljsc +21 -0
- data/ext/clojure-clojurescript-bef56a7/bin/cljsc.bat +18 -0
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/bin/cljsc.clj +0 -0
- data/ext/clojure-clojurescript-bef56a7/devnotes/README.org +35 -0
- data/ext/clojure-clojurescript-bef56a7/devnotes/bcrepl.org +13 -0
- data/ext/clojure-clojurescript-bef56a7/devnotes/cljs.org +500 -0
- data/ext/clojure-clojurescript-bef56a7/devnotes/corelib.org +583 -0
- data/ext/clojure-clojurescript-bef56a7/devnotes/day1.org +203 -0
- data/ext/clojure-clojurescript-bef56a7/devnotes/day2.org +44 -0
- data/ext/clojure-clojurescript-bef56a7/devnotes/talk.org +126 -0
- data/ext/clojure-clojurescript-bef56a7/devnotes/testing +13 -0
- data/ext/clojure-clojurescript-bef56a7/devnotes/todo.org +121 -0
- data/ext/clojure-clojurescript-bef56a7/epl-v10.html +261 -0
- data/ext/clojure-clojurescript-bef56a7/pom.template.xml +88 -0
- data/ext/clojure-clojurescript-bef56a7/samples/dom/.gitignore +2 -0
- data/ext/clojure-clojurescript-bef56a7/samples/dom/src/dom/test.cljs +48 -0
- data/ext/clojure-clojurescript-bef56a7/samples/dom/test.html +30 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello-js/.gitignore +2 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello-js/README.md +53 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello-js/externed-lib.js +7 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello-js/externs.js +3 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello-js/hello-extern.html +14 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello-js/hello-js-dev.html +18 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello-js/hello-js.html +17 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello-js/my-external-lib.js +3 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello-js/src/hello-js/core.cljs +9 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello-js/src/hello-js/extern-example.cljs +5 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello/.gitignore +2 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello/README.md +34 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello/hello-dev.html +18 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello/hello.html +13 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello/src/hello/core.cljs +8 -0
- data/ext/clojure-clojurescript-bef56a7/samples/hello/src/hello/foo/bar.cljs +4 -0
- data/ext/clojure-clojurescript-bef56a7/samples/nodehello.cljs +18 -0
- data/ext/clojure-clojurescript-bef56a7/samples/nodels.cljs +17 -0
- data/ext/clojure-clojurescript-bef56a7/samples/repl/.gitignore +2 -0
- data/ext/clojure-clojurescript-bef56a7/samples/repl/README.md +101 -0
- data/ext/clojure-clojurescript-bef56a7/samples/repl/index.html +27 -0
- data/ext/clojure-clojurescript-bef56a7/samples/repl/src/repl/test.cljs +73 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/.gitignore +2 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/README.md +42 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/index-advanced.html +80 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/index.html +88 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/reset.css +48 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/anneal.cljs +66 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/core.cljs +307 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/dom-helpers.cljs +95 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/layout.cljs +100 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/leaderboard.cljs +40 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/radial.cljs +91 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/showgraph.cljs +121 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/src/twitterbuzz/timeline.cljs +39 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/style.css +301 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/test_data.txt +1 -0
- data/ext/clojure-clojurescript-bef56a7/samples/twitterbuzz/tweet_maps.txt +1 -0
- data/ext/clojure-clojurescript-bef56a7/script/benchmark +30 -0
- data/ext/clojure-clojurescript-bef56a7/script/bootstrap +70 -0
- data/ext/clojure-clojurescript-bef56a7/script/browser-repl +16 -0
- data/ext/clojure-clojurescript-bef56a7/script/build +59 -0
- data/ext/clojure-clojurescript-bef56a7/script/clean +5 -0
- data/ext/clojure-clojurescript-bef56a7/script/closure-library-release/google-closure-library-third-party.pom.template +59 -0
- data/ext/clojure-clojurescript-bef56a7/script/closure-library-release/google-closure-library.pom.template +54 -0
- data/ext/clojure-clojurescript-bef56a7/script/closure-library-release/make-closure-library-jars.sh +87 -0
- data/ext/clojure-clojurescript-bef56a7/script/compile +41 -0
- data/ext/clojure-clojurescript-bef56a7/script/repl +13 -0
- data/ext/clojure-clojurescript-bef56a7/script/repl.bat +13 -0
- data/ext/clojure-clojurescript-bef56a7/script/repljs +15 -0
- data/ext/clojure-clojurescript-bef56a7/script/repljs.bat +14 -0
- data/ext/clojure-clojurescript-bef56a7/script/test +38 -0
- data/ext/clojure-clojurescript-bef56a7/script/test-compile +30 -0
- data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/analyzer.clj +975 -0
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/clj/cljs/closure.clj +173 -73
- data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/compiler.clj +1081 -0
- data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/core.clj +1158 -0
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/clj/cljs/repl.clj +51 -25
- data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/repl/browser.clj +258 -0
- data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/repl/reflect.clj +75 -0
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/clj/cljs/repl/rhino.clj +6 -5
- data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/repl/server.clj +173 -0
- data/ext/clojure-clojurescript-bef56a7/src/clj/cljs/tagged_literals.clj +30 -0
- data/ext/clojure-clojurescript-bef56a7/src/cljs/cljs/core.cljs +7197 -0
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/cljs/nodejs.cljs +1 -1
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/cljs/nodejs_externs.js +0 -0
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/cljs/nodejscli.cljs +1 -1
- data/ext/clojure-clojurescript-bef56a7/src/cljs/cljs/reader.cljs +551 -0
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/browser/dom.cljs +59 -13
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/browser/event.cljs +0 -0
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/browser/net.cljs +8 -7
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/browser/repl.cljs +2 -2
- data/ext/clojure-clojurescript-bef56a7/src/cljs/clojure/core/reducers.cljs +298 -0
- data/ext/clojure-clojurescript-bef56a7/src/cljs/clojure/data.cljs +162 -0
- data/ext/clojure-clojurescript-bef56a7/src/cljs/clojure/reflect.cljs +48 -0
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/set.cljs +0 -0
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/string.cljs +4 -10
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/walk.cljs +0 -0
- data/{vendor/assets → ext/clojure-clojurescript-bef56a7}/src/cljs/clojure/zip.cljs +0 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/binding_test.cljs +7 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/binding_test_other_ns.cljs +3 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/core_test.cljs +1678 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/import_test.cljs +11 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/import_test/foo.cljs +5 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/letfn_test.cljs +19 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/macro_test.cljs +6 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/macro_test/macros.clj +5 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/ns_test.cljs +14 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/ns_test/bar.cljs +3 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/ns_test/foo.cljs +7 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/cljs/reader_test.cljs +124 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/clojure/data_test.cljs +22 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/clojure/string_test.cljs +97 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/foo/ns_shadow_test.cljs +9 -0
- data/ext/clojure-clojurescript-bef56a7/test/cljs/test_runner.cljs +26 -0
- data/lib/clementine.rb +3 -24
- data/lib/clementine/clojurescript_engine.rb +9 -48
- data/lib/clementine/clojurescript_engine/base.rb +15 -0
- data/lib/clementine/clojurescript_engine/jruby.rb +46 -0
- data/lib/clementine/{clojurescript_engine_mri.rb → clojurescript_engine/mri.rb} +17 -10
- data/lib/clementine/version.rb +1 -1
- data/test/clojurescript_engine_test.rb +36 -14
- metadata +177 -83
- data/vendor/assets/lib/clojure.jar +0 -0
- data/vendor/assets/lib/compiler.jar +0 -0
- data/vendor/assets/lib/goog.jar +0 -0
- data/vendor/assets/lib/js.jar +0 -0
- data/vendor/assets/src/clj/cljs/compiler.clj +0 -1341
- data/vendor/assets/src/clj/cljs/core.clj +0 -702
- data/vendor/assets/src/clj/cljs/repl/browser.clj +0 -341
- data/vendor/assets/src/cljs/cljs/core.cljs +0 -3330
- data/vendor/assets/src/cljs/cljs/reader.cljs +0 -360
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<!--[if lt IE 7 ]> <html lang="en" class="no-js ie6"> <![endif]-->
|
|
3
|
+
<!--[if IE 7 ]> <html lang="en" class="no-js ie7"> <![endif]-->
|
|
4
|
+
<!--[if IE 8 ]> <html lang="en" class="no-js ie8"> <![endif]-->
|
|
5
|
+
<!--[if IE 9 ]> <html lang="en" class="no-js ie9"> <![endif]-->
|
|
6
|
+
<!--[if (gt IE 9)|!(IE)]><!--> <html lang="en" class="no-js"> <!--<![endif]-->
|
|
7
|
+
<head>
|
|
8
|
+
<meta charset="UTF-8">
|
|
9
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
|
10
|
+
|
|
11
|
+
<title>TwitterBuzz: A ClojureScript Demo</title>
|
|
12
|
+
<meta name="description" content="Demo showing off the ClojureScript hotness">
|
|
13
|
+
<meta name="author" content="Clojure/core">
|
|
14
|
+
|
|
15
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
16
|
+
|
|
17
|
+
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
|
18
|
+
<link rel="stylesheet" href="reset.css">
|
|
19
|
+
<link rel="stylesheet" href="style.css">
|
|
20
|
+
|
|
21
|
+
<!--[if lt IE 9]>
|
|
22
|
+
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
|
23
|
+
<![endif]-->
|
|
24
|
+
|
|
25
|
+
</head>
|
|
26
|
+
<body>
|
|
27
|
+
<header>
|
|
28
|
+
<section id="header-inner">
|
|
29
|
+
<h1>TwitterBuzz: A ClojureScript Demo</h1>
|
|
30
|
+
<div>Learn more:
|
|
31
|
+
<a href="https://github.com/clojure/clojurescript" target="_clojurescript">ClojureScript</a> |
|
|
32
|
+
<a href="https://github.com/clojure/clojurescript/wiki" target="_clojurescript">Documentation</a> |
|
|
33
|
+
<a href="https://github.com/clojure/clojurescript/tree/master/samples/twitterbuzz"
|
|
34
|
+
target="_clojurescript">Source Code</a>
|
|
35
|
+
</div>
|
|
36
|
+
<h2 id="clojure"><img src="http://clojure.org/space/showimage/clojure-icon-50x50.png" width="50" height="50" alt="Clojure"></h2>
|
|
37
|
+
</section>
|
|
38
|
+
</header>
|
|
39
|
+
|
|
40
|
+
<section id="status-bar-wrap">
|
|
41
|
+
<section id="status-bar">
|
|
42
|
+
<input id="twitter-search-tag" value="" type="text" class="search" results="5" autosave="clojure" placeholder="Enter keywords..." autofocus />
|
|
43
|
+
<button id="twitter-search-button" type="submit" class="button green">Track</button>
|
|
44
|
+
<span id="tweet-status" class="tweet-status">Not Connected</span>
|
|
45
|
+
</section>
|
|
46
|
+
</section>
|
|
47
|
+
|
|
48
|
+
<section id="main-wrap">
|
|
49
|
+
<section id="main">
|
|
50
|
+
|
|
51
|
+
<section id="network">
|
|
52
|
+
</section>
|
|
53
|
+
|
|
54
|
+
<section id="leaderboard">
|
|
55
|
+
<h2>Leaderboard</h2>
|
|
56
|
+
<div id="leaderboard-content">
|
|
57
|
+
</div>
|
|
58
|
+
</section>
|
|
59
|
+
|
|
60
|
+
<section id="timeline" class="tweetbox">
|
|
61
|
+
<h2>Timeline</h2>
|
|
62
|
+
<div id="timeline-content">
|
|
63
|
+
</div>
|
|
64
|
+
</section>
|
|
65
|
+
|
|
66
|
+
<div class="clear"></div>
|
|
67
|
+
</section>
|
|
68
|
+
|
|
69
|
+
<div class="clear"></div>
|
|
70
|
+
</section>
|
|
71
|
+
|
|
72
|
+
<section id="avatar-hover" style="display: none">
|
|
73
|
+
<div id="avatar-hover-body" class="tweetbox"></div>
|
|
74
|
+
</section>
|
|
75
|
+
|
|
76
|
+
<script type="text/javascript" src="twitterbuzz.js"></script>
|
|
77
|
+
|
|
78
|
+
</body>
|
|
79
|
+
</html>
|
|
80
|
+
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<!--[if lt IE 7 ]> <html lang="en" class="no-js ie6"> <![endif]-->
|
|
3
|
+
<!--[if IE 7 ]> <html lang="en" class="no-js ie7"> <![endif]-->
|
|
4
|
+
<!--[if IE 8 ]> <html lang="en" class="no-js ie8"> <![endif]-->
|
|
5
|
+
<!--[if IE 9 ]> <html lang="en" class="no-js ie9"> <![endif]-->
|
|
6
|
+
<!--[if (gt IE 9)|!(IE)]><!--> <html lang="en" class="no-js"> <!--<![endif]-->
|
|
7
|
+
<head>
|
|
8
|
+
<meta charset="UTF-8">
|
|
9
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
|
10
|
+
|
|
11
|
+
<title>TwitterBuzz: A ClojureScript Demo</title>
|
|
12
|
+
<meta name="description" content="Demo showing off the ClojureScript hotness">
|
|
13
|
+
<meta name="author" content="Clojure/core">
|
|
14
|
+
|
|
15
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
16
|
+
|
|
17
|
+
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
|
18
|
+
<link rel="stylesheet" href="reset.css">
|
|
19
|
+
<link rel="stylesheet" href="style.css">
|
|
20
|
+
|
|
21
|
+
<!--[if lt IE 9]>
|
|
22
|
+
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
|
23
|
+
<![endif]-->
|
|
24
|
+
|
|
25
|
+
</head>
|
|
26
|
+
<body>
|
|
27
|
+
<header>
|
|
28
|
+
<section id="header-inner">
|
|
29
|
+
<h1>TwitterBuzz: A ClojureScript Demo</h1>
|
|
30
|
+
<div>Learn more:
|
|
31
|
+
<a href="https://github.com/clojure/clojurescript" target="_clojurescript">ClojureScript</a> |
|
|
32
|
+
<a href="https://github.com/clojure/clojurescript/wiki" target="_clojurescript">Documentation</a> |
|
|
33
|
+
<a href="https://github.com/clojure/clojurescript/tree/master/samples/twitterbuzz"
|
|
34
|
+
target="_clojurescript">Source Code</a>
|
|
35
|
+
</div>
|
|
36
|
+
<h2 id="clojure"><img src="http://clojure.org/space/showimage/clojure-icon-50x50.png" width="50" height="50" alt="Clojure"></h2>
|
|
37
|
+
</section>
|
|
38
|
+
</header>
|
|
39
|
+
|
|
40
|
+
<section id="status-bar-wrap">
|
|
41
|
+
<section id="status-bar">
|
|
42
|
+
<input id="twitter-search-tag" value="" type="text" class="search" results="5" autosave="clojure" placeholder="Enter keywords..." autofocus />
|
|
43
|
+
<button id="twitter-search-button" type="submit" class="button green">Track</button>
|
|
44
|
+
<span id="tweet-status">Not Connected</span>
|
|
45
|
+
</section>
|
|
46
|
+
</section>
|
|
47
|
+
|
|
48
|
+
<section id="main-wrap">
|
|
49
|
+
<section id="main">
|
|
50
|
+
|
|
51
|
+
<section id="network">
|
|
52
|
+
</section>
|
|
53
|
+
|
|
54
|
+
<section id="leaderboard">
|
|
55
|
+
<h2>Leaderboard</h2>
|
|
56
|
+
<div id="leaderboard-content">
|
|
57
|
+
</div>
|
|
58
|
+
</section>
|
|
59
|
+
|
|
60
|
+
<section id="timeline" class="tweetbox">
|
|
61
|
+
<h2>Timeline</h2>
|
|
62
|
+
<div id="timeline-content">
|
|
63
|
+
</div>
|
|
64
|
+
</section>
|
|
65
|
+
|
|
66
|
+
<div class="clear"></div>
|
|
67
|
+
</section>
|
|
68
|
+
|
|
69
|
+
<div class="clear"></div>
|
|
70
|
+
</section>
|
|
71
|
+
|
|
72
|
+
<section id="avatar-hover" style="display: none">
|
|
73
|
+
<div id="avatar-hover-body" class="tweetbox"></div>
|
|
74
|
+
</section>
|
|
75
|
+
|
|
76
|
+
<!-- In unoptimized mode, additional script tags are required -->
|
|
77
|
+
<script type="text/javascript" src="out/goog/base.js"></script>
|
|
78
|
+
<script type="text/javascript" src="twitterbuzz.js"></script>
|
|
79
|
+
<script type="text/javascript">
|
|
80
|
+
goog.require('twitterbuzz.core');
|
|
81
|
+
goog.require('twitterbuzz.timeline');
|
|
82
|
+
goog.require('twitterbuzz.leaderboard');
|
|
83
|
+
goog.require('twitterbuzz.showgraph');
|
|
84
|
+
</script>
|
|
85
|
+
|
|
86
|
+
</body>
|
|
87
|
+
</html>
|
|
88
|
+
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/* Eric Meyer's Reset Reloaded */
|
|
2
|
+
html, body, div, span, applet, object, iframe,
|
|
3
|
+
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
|
4
|
+
a, abbr, acronym, address, big, cite, code,
|
|
5
|
+
del, dfn, em, font, img, ins, kbd, q, s, samp,
|
|
6
|
+
small, strike, strong, sub, sup, tt, var,
|
|
7
|
+
dl, dt, dd, ol, ul, li,
|
|
8
|
+
fieldset, form, label, legend,
|
|
9
|
+
table, caption, tbody, tfoot, thead, tr, th, td {
|
|
10
|
+
margin: 0;
|
|
11
|
+
padding: 0;
|
|
12
|
+
border: 0;
|
|
13
|
+
outline: 0;
|
|
14
|
+
font-weight: inherit;
|
|
15
|
+
font-style: inherit;
|
|
16
|
+
font-size: 100%;
|
|
17
|
+
font-family: inherit;
|
|
18
|
+
vertical-align: baseline;
|
|
19
|
+
}
|
|
20
|
+
/* remember to define focus styles! */
|
|
21
|
+
:focus {
|
|
22
|
+
outline: 0;
|
|
23
|
+
}
|
|
24
|
+
body {
|
|
25
|
+
line-height: 1;
|
|
26
|
+
color: black;
|
|
27
|
+
background: white;
|
|
28
|
+
}
|
|
29
|
+
ol, ul {
|
|
30
|
+
list-style: none;
|
|
31
|
+
}
|
|
32
|
+
/* tables still need 'cellspacing="0"' in the markup */
|
|
33
|
+
table {
|
|
34
|
+
border-collapse: separate;
|
|
35
|
+
border-spacing: 0;
|
|
36
|
+
}
|
|
37
|
+
caption, th, td {
|
|
38
|
+
text-align: left;
|
|
39
|
+
font-weight: normal;
|
|
40
|
+
}
|
|
41
|
+
blockquote:before, blockquote:after,
|
|
42
|
+
q:before, q:after {
|
|
43
|
+
content: "";
|
|
44
|
+
}
|
|
45
|
+
blockquote, q {
|
|
46
|
+
quotes: "" "";
|
|
47
|
+
}
|
|
48
|
+
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
; Copyright (c) Rich Hickey. All rights reserved.
|
|
2
|
+
; The use and distribution terms for this software are covered by the
|
|
3
|
+
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
|
4
|
+
; which can be found in the file epl-v10.html at the root of this distribution.
|
|
5
|
+
; By using this software in any fashion, you are agreeing to be bound by
|
|
6
|
+
; the terms of this license.
|
|
7
|
+
; You must not remove this notice, or any other, from this software.
|
|
8
|
+
|
|
9
|
+
(ns twitterbuzz.anneal)
|
|
10
|
+
|
|
11
|
+
(defn exp [x]
|
|
12
|
+
(js/Math.exp x))
|
|
13
|
+
|
|
14
|
+
(defn abs [x]
|
|
15
|
+
(js/Math.abs x))
|
|
16
|
+
|
|
17
|
+
(defn random []
|
|
18
|
+
(js/Math.random))
|
|
19
|
+
|
|
20
|
+
(defn standard-prob [e e1 temp]
|
|
21
|
+
(if (< e1 e)
|
|
22
|
+
1
|
|
23
|
+
(exp (/ (- e e1) temp))))
|
|
24
|
+
|
|
25
|
+
(defn linear-cooling [steps]
|
|
26
|
+
(let [min (- 1 (/ steps (dec steps)))]
|
|
27
|
+
(fn [t]
|
|
28
|
+
(max min (- 1 (/ t steps))))))
|
|
29
|
+
|
|
30
|
+
(defn anneal
|
|
31
|
+
"Given an energy scoring function, a temperature function
|
|
32
|
+
(calculates temp given iteration t), a permutation function (creates
|
|
33
|
+
a candidate next state given a current state and iteration t), and
|
|
34
|
+
an acceptance probability function (of last next energy and temp),
|
|
35
|
+
yields a lazy seq of (accepted?) states of the form
|
|
36
|
+
{:state s :score :best best :best-score :t t}"
|
|
37
|
+
|
|
38
|
+
[energy ;;(energy state) -> score
|
|
39
|
+
temp ;;(temp t) -> 0-1.0
|
|
40
|
+
permute ;;(permute state t) -> new-state
|
|
41
|
+
prob ;;(prob e e1 temp) -> 0-1.0
|
|
42
|
+
state]
|
|
43
|
+
|
|
44
|
+
(let [init state
|
|
45
|
+
init-score (energy state)
|
|
46
|
+
step (fn step [{:keys [state score best best-score t]:as ret}]
|
|
47
|
+
(loop [next (permute state) t (inc t)]
|
|
48
|
+
(let [next-score (energy next)]
|
|
49
|
+
(if (> (prob score next-score (temp t)) (random))
|
|
50
|
+
(let [ret {:state next :score next-score :t t
|
|
51
|
+
:best (if (< next-score best-score) next best)
|
|
52
|
+
:best-score (min next-score best-score)}]
|
|
53
|
+
(lazy-seq (cons ret (step ret))))
|
|
54
|
+
(recur (permute state) (inc t))))))]
|
|
55
|
+
(step {:state init :score init-score :best init :best-score init-score :t 0})))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
(comment
|
|
59
|
+
|
|
60
|
+
(take 10 (take-nth 100
|
|
61
|
+
(anneal #(abs (- % 42))
|
|
62
|
+
(linear-cooling 1000)
|
|
63
|
+
(fn [s _] (+ s (- (random) 0.5)))
|
|
64
|
+
standard-prob
|
|
65
|
+
55)))
|
|
66
|
+
)
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
; Copyright (c) Rich Hickey. All rights reserved.
|
|
2
|
+
; The use and distribution terms for this software are covered by the
|
|
3
|
+
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
|
4
|
+
; which can be found in the file epl-v10.html at the root of this distribution.
|
|
5
|
+
; By using this software in any fashion, you are agreeing to be bound by
|
|
6
|
+
; the terms of this license.
|
|
7
|
+
; You must not remove this notice, or any other, from this software.
|
|
8
|
+
|
|
9
|
+
(ns twitterbuzz.core
|
|
10
|
+
(:require [twitterbuzz.dom-helpers :as dom]
|
|
11
|
+
[clojure.string :as string]
|
|
12
|
+
[goog.string :as gstring]
|
|
13
|
+
[goog.net.Jsonp :as jsonp]
|
|
14
|
+
[goog.Timer :as timer]
|
|
15
|
+
[goog.events :as events]
|
|
16
|
+
[goog.events.EventType :as event-type]
|
|
17
|
+
[goog.dom.classes :as classes]))
|
|
18
|
+
|
|
19
|
+
(def results-per-page 100)
|
|
20
|
+
(def max-missing-query 20)
|
|
21
|
+
|
|
22
|
+
(def initial-state {:max-id 0
|
|
23
|
+
:graph {}
|
|
24
|
+
:listeners {}
|
|
25
|
+
:tweet-count 0
|
|
26
|
+
:search-tag nil
|
|
27
|
+
:ignore-mentions #{}})
|
|
28
|
+
|
|
29
|
+
(def state (atom initial-state))
|
|
30
|
+
|
|
31
|
+
(defn add-listener
|
|
32
|
+
"Add a listener to the graph."
|
|
33
|
+
[graph k f]
|
|
34
|
+
(let [l (-> graph :listeners k)]
|
|
35
|
+
(assoc-in graph [:listeners k] (conj l f))))
|
|
36
|
+
|
|
37
|
+
(defn register
|
|
38
|
+
"Register a function to be called when new data arrives specifying
|
|
39
|
+
the event to receive updates for."
|
|
40
|
+
[event f]
|
|
41
|
+
(swap! state add-listener event f))
|
|
42
|
+
|
|
43
|
+
(def twitter-uri (goog.Uri. "http://search.twitter.com/search.json"))
|
|
44
|
+
|
|
45
|
+
(defn search-tag
|
|
46
|
+
"Get the current tag value from the page."
|
|
47
|
+
[]
|
|
48
|
+
(.-value (dom/get-element :twitter-search-tag)))
|
|
49
|
+
|
|
50
|
+
(defn retrieve
|
|
51
|
+
"Send request to twitter."
|
|
52
|
+
[payload callback error-callback]
|
|
53
|
+
(.send (goog.net.Jsonp. twitter-uri)
|
|
54
|
+
payload
|
|
55
|
+
callback
|
|
56
|
+
error-callback))
|
|
57
|
+
|
|
58
|
+
(defn send-event
|
|
59
|
+
"For the given event, call every listener for that event, passing the
|
|
60
|
+
message."
|
|
61
|
+
([event]
|
|
62
|
+
(send-event event nil))
|
|
63
|
+
([event message]
|
|
64
|
+
(doseq [f (-> @state :listeners event)]
|
|
65
|
+
(f message))))
|
|
66
|
+
|
|
67
|
+
(defn parse-mentions
|
|
68
|
+
"Given a map representing a single tweet, return all mentions that
|
|
69
|
+
are found within the tweet text. Twitter usernames are not case
|
|
70
|
+
sensitive so mentioned usernames are always returned in lower case."
|
|
71
|
+
[tweet]
|
|
72
|
+
(map #(string/lower-case (apply str (drop 1 %)))
|
|
73
|
+
(re-seq (re-pattern "@\\w+") (:text tweet))))
|
|
74
|
+
|
|
75
|
+
(defn add-mentions
|
|
76
|
+
"Add the user to the mentions map for first user she mentions,
|
|
77
|
+
clearing the mentions map of user."
|
|
78
|
+
[graph user mentions]
|
|
79
|
+
(if-let [mention (first mentions)]
|
|
80
|
+
(let [graph (assoc graph mention (get graph mention {:username mention}))
|
|
81
|
+
node (get graph mention)
|
|
82
|
+
mentions-map (get node :mentions {})
|
|
83
|
+
graph (assoc-in graph [mention :mentions user] (inc (get mentions-map user 0)))]
|
|
84
|
+
(assoc-in graph [user :mentions] {}))
|
|
85
|
+
graph))
|
|
86
|
+
|
|
87
|
+
(defn update-graph
|
|
88
|
+
"Given a graph and a sequence of new tweets in chronological order,
|
|
89
|
+
update the graph."
|
|
90
|
+
[graph tweet-maps]
|
|
91
|
+
(reduce (fn [acc tweet]
|
|
92
|
+
(let [user (string/lower-case (:from_user tweet))
|
|
93
|
+
mentions (parse-mentions tweet)
|
|
94
|
+
node (get acc user {:mentions {}})]
|
|
95
|
+
(-> (assoc acc user
|
|
96
|
+
(assoc node :last-tweet (:text tweet)
|
|
97
|
+
:image-url (:profile_image_url tweet)
|
|
98
|
+
:username (:from_user tweet)))
|
|
99
|
+
(add-mentions user mentions))))
|
|
100
|
+
graph
|
|
101
|
+
(map #(select-keys % [:text :from_user :profile_image_url]) tweet-maps)))
|
|
102
|
+
|
|
103
|
+
(defn num-mentions [user]
|
|
104
|
+
(reduce + (vals (:mentions user))))
|
|
105
|
+
|
|
106
|
+
(defn update-state
|
|
107
|
+
"Given an old state, maximum id and a new sequence of tweets, return
|
|
108
|
+
an updated state."
|
|
109
|
+
[old-state max-id tweets]
|
|
110
|
+
(-> old-state
|
|
111
|
+
(assoc :max-id max-id)
|
|
112
|
+
(update-in [:tweet-count] #(+ % (count tweets)))
|
|
113
|
+
(assoc :graph (update-graph (:graph old-state) (reverse tweets)))))
|
|
114
|
+
|
|
115
|
+
(defn new-tweets [max-id tweets]
|
|
116
|
+
(filter #(> (:id %) max-id) tweets))
|
|
117
|
+
|
|
118
|
+
(defn new-tweets-callback
|
|
119
|
+
"Given a json object, update the state with any new information and
|
|
120
|
+
fire events."
|
|
121
|
+
[json]
|
|
122
|
+
(let [{:keys [max_id results]} (js->clj json :keywordize-keys true)
|
|
123
|
+
tweets (new-tweets (:max-id @state) results)]
|
|
124
|
+
(do (swap! state update-state max_id tweets)
|
|
125
|
+
(send-event :new-tweets tweets)
|
|
126
|
+
(send-event :graph-update (:graph @state)))))
|
|
127
|
+
|
|
128
|
+
(defn set-tweet-status [css-class message]
|
|
129
|
+
(doto (dom/set-text :tweet-status message)
|
|
130
|
+
(classes/set (name css-class))))
|
|
131
|
+
|
|
132
|
+
(defn error-callback [error]
|
|
133
|
+
(set-tweet-status :error "Twitter error"))
|
|
134
|
+
|
|
135
|
+
(defn add-missing-tweets
|
|
136
|
+
"Add missing data to the graph."
|
|
137
|
+
[graph tweets]
|
|
138
|
+
(let [new-tweets (reduce (fn [acc next-tweet]
|
|
139
|
+
(assoc acc (string/lower-case (:from_user next-tweet))
|
|
140
|
+
next-tweet))
|
|
141
|
+
{}
|
|
142
|
+
(sort-by :id tweets))]
|
|
143
|
+
(reduce (fn [acc [node-name {:keys [from_user text profile_image_url]}]]
|
|
144
|
+
(if-let [old-tweet (get graph node-name)]
|
|
145
|
+
(if (:last-tweet old-tweet)
|
|
146
|
+
acc
|
|
147
|
+
(assoc acc node-name
|
|
148
|
+
(merge old-tweet {:last-tweet text
|
|
149
|
+
:image-url profile_image_url
|
|
150
|
+
:username from_user})))
|
|
151
|
+
acc))
|
|
152
|
+
graph
|
|
153
|
+
new-tweets)))
|
|
154
|
+
|
|
155
|
+
(defn ignored
|
|
156
|
+
"Given a list of the usernames for missing tweets and the tweets
|
|
157
|
+
which are the result of a query for this missing data, return a set of
|
|
158
|
+
twitter usernames which will be ignored moving forward.
|
|
159
|
+
|
|
160
|
+
Names may be ignored because the twitter user does not exist or
|
|
161
|
+
just doesn't tweet."
|
|
162
|
+
[missing tweets]
|
|
163
|
+
(when (< (count tweets) results-per-page)
|
|
164
|
+
(let [users (set (map #(string/lower-case (:from_user %)) tweets))
|
|
165
|
+
missing (map string/lower-case missing)]
|
|
166
|
+
(reduce (fn [acc next-missing]
|
|
167
|
+
(if (contains? users next-missing)
|
|
168
|
+
acc
|
|
169
|
+
(conj acc next-missing)))
|
|
170
|
+
#{}
|
|
171
|
+
missing))))
|
|
172
|
+
|
|
173
|
+
(defn add-missing-callback
|
|
174
|
+
"Update the graph and the ignore-mentions list when data is received
|
|
175
|
+
from a missing user query."
|
|
176
|
+
[missing json]
|
|
177
|
+
(let [response (js->clj json :keywordize-keys true)
|
|
178
|
+
tweets (:results response)]
|
|
179
|
+
(if-let [error (:error response)]
|
|
180
|
+
(set-tweet-status :error error)
|
|
181
|
+
(do (swap! state (fn [old-state]
|
|
182
|
+
(assoc old-state
|
|
183
|
+
:graph (add-missing-tweets (:graph old-state) tweets)
|
|
184
|
+
:ignore-mentions (into (:ignore-mentions old-state)
|
|
185
|
+
(ignored missing tweets)))))
|
|
186
|
+
(send-event :new-tweets [])
|
|
187
|
+
(send-event :graph-update (:graph @state))))))
|
|
188
|
+
|
|
189
|
+
(defn missing-tweets
|
|
190
|
+
"Return a list of usernames with missing tweets in the graph."
|
|
191
|
+
[graph]
|
|
192
|
+
(->> (map second graph)
|
|
193
|
+
(remove :last-tweet)
|
|
194
|
+
(map :username)
|
|
195
|
+
(remove empty?)
|
|
196
|
+
(remove (:ignore-mentions @state))))
|
|
197
|
+
|
|
198
|
+
(defn fetch-mentioned-tweets
|
|
199
|
+
"Query twitter for usernames which are currently missing data in the
|
|
200
|
+
graph. Limit this query to max-missing-query names."
|
|
201
|
+
[missing]
|
|
202
|
+
(let [q (apply str (interpose " OR " (map #(str "from:" %)
|
|
203
|
+
(take max-missing-query missing))))]
|
|
204
|
+
(set-tweet-status :okay "Fetching mentioned tweets")
|
|
205
|
+
(retrieve (doto (js-obj)
|
|
206
|
+
(aset "q" q)
|
|
207
|
+
(aset "rpp" results-per-page))
|
|
208
|
+
#(add-missing-callback missing %)
|
|
209
|
+
error-callback)))
|
|
210
|
+
|
|
211
|
+
(defn fetch-new-tweets
|
|
212
|
+
"Use the current search tag to fetch new tweets from twitter."
|
|
213
|
+
[]
|
|
214
|
+
(when-let [tag (:search-tag @state)]
|
|
215
|
+
(set-tweet-status :okay "Fetching tweets")
|
|
216
|
+
(retrieve (doto (js-obj)
|
|
217
|
+
(aset "q" tag)
|
|
218
|
+
(aset "rpp" results-per-page))
|
|
219
|
+
new-tweets-callback
|
|
220
|
+
error-callback)))
|
|
221
|
+
|
|
222
|
+
(defn fetch-tweets
|
|
223
|
+
"If there are missing tweets then fetch them, if not fetch new tweets."
|
|
224
|
+
[]
|
|
225
|
+
(let [missing (missing-tweets (:graph @state))]
|
|
226
|
+
(if (seq missing)
|
|
227
|
+
(fetch-mentioned-tweets missing)
|
|
228
|
+
(fetch-new-tweets))))
|
|
229
|
+
|
|
230
|
+
(defn poll
|
|
231
|
+
"Request new data from twitter once every 24 seconds. This will put
|
|
232
|
+
you at the 150 request/hour rate limit. We can speed it up for the demo."
|
|
233
|
+
[]
|
|
234
|
+
(let [timer (goog.Timer. 24000)]
|
|
235
|
+
(do (fetch-tweets)
|
|
236
|
+
(. timer (start))
|
|
237
|
+
(events/listen timer goog.Timer/TICK fetch-tweets))))
|
|
238
|
+
|
|
239
|
+
(defn do-track-button-clicked
|
|
240
|
+
"When the track button is clicked, reset to the initial state
|
|
241
|
+
keeping only the event listeners."
|
|
242
|
+
[]
|
|
243
|
+
(do (let [listeners (:listeners @state)]
|
|
244
|
+
(reset! state (assoc initial-state :listeners listeners :search-tag (search-tag))))
|
|
245
|
+
(fetch-tweets)
|
|
246
|
+
(send-event :track-clicked)))
|
|
247
|
+
|
|
248
|
+
(defn start-app
|
|
249
|
+
"Start polling and listen for UI events."
|
|
250
|
+
[]
|
|
251
|
+
(do (poll)
|
|
252
|
+
(events/listen (dom/get-element :twitter-search-button)
|
|
253
|
+
"click"
|
|
254
|
+
do-track-button-clicked)
|
|
255
|
+
(events/listen (dom/get-element :twitter-search-tag)
|
|
256
|
+
event-type/CHANGE
|
|
257
|
+
do-track-button-clicked)))
|
|
258
|
+
|
|
259
|
+
(start-app)
|
|
260
|
+
|
|
261
|
+
(defn link [url s]
|
|
262
|
+
(str "<a href='" url "' target='_twitterbuzz'>" s "</a>"))
|
|
263
|
+
|
|
264
|
+
(defn markup
|
|
265
|
+
"Add markup to tweet text to activate links."
|
|
266
|
+
[s]
|
|
267
|
+
(let [markup-f (fn [s] (let [w (string/trim s)]
|
|
268
|
+
(cond (gstring/startsWith w "http://")
|
|
269
|
+
(link w w)
|
|
270
|
+
(gstring/startsWith w "@")
|
|
271
|
+
(link (str "http://twitter.com/#!/" (re-find #"\w+" w)) w)
|
|
272
|
+
:else s)))]
|
|
273
|
+
(string/join " " (map markup-f (string/split s #"[ ]")))))
|
|
274
|
+
|
|
275
|
+
(comment
|
|
276
|
+
|
|
277
|
+
(parse-mentions {:text "What's up @sue: and @Larry"})
|
|
278
|
+
|
|
279
|
+
(add-mentions {} "jim" ["sue"])
|
|
280
|
+
(add-mentions {"sue" {}} "jim" ["sue"])
|
|
281
|
+
|
|
282
|
+
(def tweets [{:profile_image_url "url1"
|
|
283
|
+
:from_user "Jim"
|
|
284
|
+
:text "I like cookies!"}
|
|
285
|
+
{:profile_image_url "url2"
|
|
286
|
+
:from_user "sue"
|
|
287
|
+
:text "Me to @jim."}
|
|
288
|
+
{:profile_image_url "url3"
|
|
289
|
+
:from_user "bob"
|
|
290
|
+
:text "You shouldn't eat so many cookies @sue"}
|
|
291
|
+
{:profile_image_url "url4"
|
|
292
|
+
:from_user "sam"
|
|
293
|
+
:text "@Bob that was a cruel thing to say to @Sue."}
|
|
294
|
+
{:profile_image_url "url5"
|
|
295
|
+
:from_user "ted"
|
|
296
|
+
:text "@foo is awesome!"}])
|
|
297
|
+
|
|
298
|
+
(def graph (update-graph {} tweets))
|
|
299
|
+
(count graph)
|
|
300
|
+
|
|
301
|
+
(num-mentions (get graph "sue"))
|
|
302
|
+
(num-mentions (get graph "bob"))
|
|
303
|
+
(num-mentions (get graph "sam"))
|
|
304
|
+
|
|
305
|
+
(take 1 (reverse (sort-by #(num-mentions (second %)) (seq graph))))
|
|
306
|
+
|
|
307
|
+
)
|