clementine 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
)
|