├── .gitignore
├── .gitmodules
├── Procfile
├── README.md
├── local-sandbox
├── .gitignore
├── project.clj
├── resources
│ └── public
│ │ ├── css
│ │ └── default.css
│ │ ├── images
│ │ └── favicon.png
│ │ ├── index.html
│ │ └── js
│ │ ├── PhiloGL.cls.js
│ │ ├── PhiloGL.js
│ │ ├── doc.shim.js
│ │ ├── gl-matrix-min.js
│ │ └── webgl-utils.js
└── src
│ ├── bootstrapper.cljs
│ └── enchilada
│ ├── .gitignore
│ └── simple-demo.cljs
├── project.clj
├── resources
├── private
│ ├── externs
│ │ ├── arbor.js
│ │ ├── jquery.js
│ │ ├── react.js
│ │ └── three.js
│ ├── js
│ │ ├── arbor.js
│ │ ├── gl-matrix-min.js
│ │ ├── react.js
│ │ ├── singult.js
│ │ ├── three.js
│ │ └── webgl-utils.js
│ └── robots.txt
└── public
│ ├── css
│ └── default.css
│ ├── images
│ ├── 404.jpg
│ ├── coming-soon.png
│ ├── dirty-shade.png
│ ├── error.png
│ ├── error@2x.png
│ └── favicon.png
│ └── js
│ ├── PhiloGL.cls.js
│ ├── PhiloGL.js
│ ├── create.js
│ └── doc.shim.js
├── src
├── client
│ └── enchilada.cljs
└── enchilada
│ ├── controllers
│ └── canvas.clj
│ ├── handler.clj
│ ├── middleware
│ ├── cache.clj
│ └── fso.clj
│ ├── services
│ ├── gamification.clj
│ └── search.clj
│ ├── util
│ ├── compiler.clj
│ ├── fs.clj
│ ├── gist.clj
│ ├── google_analytics.clj
│ ├── keepalive.clj
│ ├── macros.clj
│ ├── markdown.clj
│ ├── page.clj
│ ├── time_ago.clj
│ └── twitter.clj
│ └── views
│ ├── canvas.clj
│ ├── common.clj
│ ├── create.clj
│ ├── not_found.clj
│ ├── proxy.clj
│ ├── sitemap.clj
│ ├── stats.clj
│ └── welcome.clj
└── system.properties
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /lib
3 | /classes
4 | /checkouts
5 | /work
6 | pom.xml
7 | *.jar
8 | *.class
9 | .lein-deps-sum
10 | .lein-failures
11 | .lein-plugins
12 | .lein-env
13 | .lein-repl-history
14 | setenv.sh
15 | /.nrepl-port
16 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "gists/boids"]
2 | path = gists/boids
3 | url = https://gist.github.com/7145520.git
4 | [submodule "gists/torus"]
5 | path = gists/torus
6 | url = https://gist.github.com/7078992.git
7 | [submodule "gists/quine"]
8 | path = gists/quine
9 | url = https://gist.github.com/7060918.git
10 | [submodule "gists/zebra-puzzle"]
11 | path = gists/zebra-puzzle
12 | url = https://gist.github.com/6952960.git
13 | [submodule "gists/maze"]
14 | path = gists/maze
15 | url = https://gist.github.com/6857333.git
16 | [submodule "gists/autostereogram"]
17 | path = gists/autostereogram
18 | url = https://gist.github.com/5736733.git
19 | [submodule "gists/penrose-tiling"]
20 | path = gists/penrose-tiling
21 | url = https://gist.github.com/5732587.git
22 | [submodule "gists/digraph"]
23 | path = gists/quadratic-residues
24 | url = https://gist.github.com/5694738.git
25 | [submodule "gists/world-choropleth"]
26 | path = gists/world-choropleth
27 | url = https://gist.github.com/5537192.git
28 | [submodule "gists/psychedelic-animation"]
29 | path = gists/psychedelic-animation
30 | url = https://gist.github.com/5522065.git
31 | [submodule "gists/us-choropleth"]
32 | path = gists/us-choropleth
33 | url = https://gist.github.com/5514551.git
34 | [submodule "gists/arnolds-cat-map"]
35 | path = gists/arnolds-cat-map
36 | url = https://gist.github.com/5491968.git
37 | [submodule "gists/heighways-dragon"]
38 | path = gists/heighways-dragon
39 | url = https://gist.github.com/5285431.git
40 | [submodule "gists/chroma-spirals"]
41 | path = gists/chroma-spirals
42 | url = https://gist.github.com/5278162.git
43 | [submodule "gists/turmites"]
44 | path = gists/turmites
45 | url = https://gist.github.com/5259306.git
46 | [submodule "gists/flower-of-life"]
47 | path = gists/flower-of-life
48 | url = https://gist.github.com/5257851.git
49 | [submodule "gists/champernowne"]
50 | path = gists/champernowne
51 | url = https://gist.github.com/5233367.git
52 | [submodule "gists/turtle-demo"]
53 | path = gists/turtle-demo
54 | url = https://gist.github.com/5229369.git
55 | [submodule "gists/simple-demo"]
56 | path = gists/simple-demo
57 | url = https://gist.github.com/5201050.git
58 | [submodule "gist/glass-box"]
59 | path = gists/glass-box
60 | url = https://gist.github.com/c91e2ce2e68ea4969c76.git
61 | [submodule "lorenz-attractor"]
62 | path = gists/lorenz-attractor
63 | url = https://gist.github.com/05e64fa45aea27755240.git
64 | [submodule "radiant-and-simple"]
65 | path = gists/radiant-and-simple
66 | url = https://gist.github.com/896d4599eff07b7493e0.git
67 | [submodule "10-print"]
68 | path = gists/10-print
69 | url = https://gist.github.com/4bf4ce47c4f615e9cfe6.git
70 | [submodule "gist/collatz-orbit"]
71 | path = gists/collatz-orbit
72 | url = https://gist.github.com/11890fb5bb8789847259.git
73 | [submodule "nks1"]
74 | path = gists/nks1
75 | url = https://gist.github.com/968c6f0ab6b14e20e4fa.git
76 | [submodule "gists/plasma"]
77 | path = gists/plasma
78 | url = https://gist.github.com/1be4335002213cce2999.git
79 | [submodule "gists/illusory-cones"]
80 | path = gists/illusory-cones
81 | url = https://gist.github.com/efee7bef4dcc615728a7.git
82 | [submodule "gists/newtons-method"]
83 | path = gists/newtons-method
84 | url = https://gist.github.com/89f92e408c66179914a2.git
85 | [submodule "gists/biomorph-designer"]
86 | path = gists/biomorph-designer
87 | url = https://gist.github.com/4dc725bc783416fbbdda.git
88 | [submodule "gists/cumulative-selection"]
89 | path = gists/cumulative-selection
90 | url = https://gist.github.com/8347c8fd72570d7a8b32.git
91 | [submodule "gists/lissajous-curve"]
92 | path = gists/lissajous-curve
93 | url = https://gist.github.com/0e25ddb2b667f9e24ce9.git
94 | [submodule "gists/recursive-tree"]
95 | path = gists/recursive-tree
96 | url = https://gist.github.com/4a74ef32e6956801b289.git
97 | [submodule "gists/cellular-automata"]
98 | path = gists/cellular-automata
99 | url = https://gist.github.com/2e6bb141d9361fb1af03.git
100 | [submodule "gists/barnesley-fern"]
101 | path = gists/barnesley-fern
102 | url = https://gist.github.com/b971d77087ff873ed7c2.git
103 | [submodule "gists/fractal"]
104 | path = gists/fractal
105 | url = https://gist.github.com/b30f4c0e7d499810e669.git
106 | [submodule "gists/yamátárájabhánasalagám"]
107 | path = gists/yamátárájabhánasalagám
108 | url = https://gist.github.com/bac724292cc7578dc182.git
109 | [submodule "gists/solar-system"]
110 | path = gists/solar-system
111 | url = https://gist.github.com/9904891.git
112 | [submodule "gists/photosphere"]
113 | path = gists/photosphere
114 | url = https://gist.github.com/9842972.git
115 | [submodule "gists/polyhedra"]
116 | path = gists/polyhedra
117 | url = https://gist.github.com/9261323.git
118 | [submodule "chess"]
119 | path = gists/chess
120 | url = https://gist.github.com/8906851.git
121 | [submodule "gists/parametric-equations"]
122 | path = gists/parametric-equations
123 | url = https://gist.github.com/8776719.git
124 | [submodule "gists/big-bang-rps"]
125 | path = gists/big-bang-rps
126 | url = https://gist.github.com/8723389.git
127 | [submodule "gists/unknown-pleasures"]
128 | path = gists/unknown-pleasures
129 | url = https://gist.github.com/rm-hull/ce68010960db55beb9de
130 | [submodule "gists/corewar"]
131 | path = gists/corewar
132 | url = https://gist.github.com/7a7b0656a7a8c95ff853.git
133 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: lein with-profile production trampoline ring server
2 |
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [Programming Enchiladas](http://programming-enchiladas.destructuring-bind.org)
2 | ======================
3 |
4 | A ClojureScript-based HTML5 Canvas and SVG Graphics Playground, much like
5 | http://bl.ocks.org/ but specifically designed for showcasing small
6 | ClojuresScript code demos: The underlying agenda is to show how small simple
7 | functional programs can generate complex behaviour.
8 |
9 | I was getting sick of setting up yet another lein/noir for every new folly, so
10 | it made more sense to have a framework which loads public ClojureScript gists
11 | directly from github, compiles them on the fly and serves them out.
12 |
13 | This allows shared/social and version-tracked editable ClojureScripts to be run
14 | by anyone anywhere with a reasonably modern browser (tested in
15 | Chrome/FF/Safari). In order to compile and run any .cljs files in gist
16 | https://gist.github.com/rm-hull/5278162 (for example), go to
17 | http://programming-enchiladas.destructuring-bind.org/rm-hull/5278162 - see
18 | below for more examples.
19 |
20 | As part of the available 'stack' (for want of a better word), the
21 | following client-side clojureScript bindings are available:
22 |
23 | | Function | Notes |
24 | |:---------|:------|
25 | | enchilada/canvas | A canvas object, which you can resize, move, whatever. |
26 | | enchilada/ctx | The graphics context, on to which you draw your stuff. |
27 | | enchilada/svg | An SVG object, on to which you insert DOM stuff (initially hidden). |
28 | | enchilada/proxy-request | Returns a URL which will be proxied through self. |
29 | | turtle.* | https://github.com/rm-hull/turtle graphics library |
30 | | dommy.template/* | Templating based on Clojure's [Hiccup](https://github.com/weavejester/hiccup/) html templating library. |
31 | | monet.* | All the https://github.com/rm-hull/monet canvas drawing functions. |
32 | | jayq.* | https://github.com/ibdknox/jayq jQuery bindings. |
33 | | c2.* | Kerninglabs C2 data visualization library. |
34 | | vomnibus.* | https://github.com/lynaghk/vomnibus bindings. |
35 | | cljs.core.logic/* | MiniKanren implementation. |
36 | | cljs.core.async/* | Communicating sequential processes implementation. |
37 | | tailrecursion.priority-map/* | Clojurescript implementation of data.priority-map |
38 |
39 | The following javascript libraries are loaded and available:
40 |
41 | * http://jquery.com/ v2.0.1
42 | * http://arborjs.org/ v0.9.2
43 | * http://www.senchalabs.org/philogl/ v1.5.2
44 |
45 | **NOTE**: This software is definitely alpha work-in-progress, please treat as such.
46 |
47 | ## Examples
48 |
49 | * [WebGL Photospheres](http://programming-enchiladas.destructuring-bind.org/rm-hull/9842972)
50 | * [OM - Chess Demo](http://programming-enchiladas.destructuring-bind.org/rm-hull/8906851)
51 | * [3D Polyhedra Taxonomy](http://programming-enchiladas.destructuring-bind.org/rm-hull/9261323)
52 | * [Big-Bang - Rock Paper Scissors](http://programming-enchiladas.destructuring-bind.org/rm-hull/8723389)
53 | * [Big-Bang - Parametric Equations](http://programming-enchiladas.destructuring-bind.org/rm-hull/8776719)
54 | * [OM mouse move](http://programming-enchiladas.destructuring-bind.org/rm-hull/8617445) vs. [Big-Bang mouse move](http://programming-enchiladas.destructuring-bind.org/rm-hull/8617788)
55 | * [OM - Google Maps](http://programming-enchiladas.destructuring-bind.org/nodename/8762403)
56 | * [OM - Contacts Demo](http://programming-enchiladas.destructuring-bind.org/mynomoto/8787765)
57 | * [WebGL Planets](http://programming-enchiladas.destructuring-bind.org/rm-hull/7778650)
58 | * [Tumbling 3D Torus](http://programming-enchiladas.destructuring-bind.org/rm-hull/7098992)
59 | * [Boids!](http://programming-enchiladas.destructuring-bind.org/rm-hull/7145520)
60 | * [Dijkstra's Maze Solver](http://programming-enchiladas.destructuring-bind.org/rm-hull/6857333)
61 | * [US Choropleth C2](http://programming-enchiladas.destructuring-bind.org/rm-hull/5514551)
62 | * [World Choropleth C2](http://programming-enchiladas.destructuring-bind.org/rm-hull/5537192)
63 | * [Psychedelic Animation](http://programming-enchiladas.destructuring-bind.org/rm-hull/5522065)
64 | * [Quadratic Residues & Fixed Points](http://programming-enchiladas.destructuring-bind.org/rm-hull/5694738?optimization-level=simple)
65 | * [3D Auto-stereograms](http://programming-enchiladas.destructuring-bind.org/rm-hull/5736733)
66 | * [Champernowne's Constant and other transcendentals](http://programming-enchiladas.destructuring-bind.org/rm-hull/5233367)
67 | * [Flower of Life](http://programming-enchiladas.destructuring-bind.org/rm-hull/5257851)
68 | * [Turmites](http://programming-enchiladas.destructuring-bind.org/rm-hull/5259306)
69 | * [Chroma-Spirals](http://programming-enchiladas.destructuring-bind.org/rm-hull/5278162)
70 | * [Heighway Dragon](http://programming-enchiladas.destructuring-bind.org/rm-hull/5285431)
71 | * [Penrose Tiling](http://programming-enchiladas.destructuring-bind.org/rm-hull/5732587)
72 | * [Arnold's Cat-map](http://programming-enchiladas.destructuring-bind.org/rm-hull/5491968)
73 | * [ClojureScript says Boo!](http://programming-enchiladas.destructuring-bind.org/rm-hull/5201050)
74 | * [Turtle graphics demo](http://programming-enchiladas.destructuring-bind.org/rm-hull/5229369)
75 | * [Compilation error](http://programming-enchiladas.destructuring-bind.org/rm-hull/5272126)
76 | * [Ajax example (from stack overflow)](http://programming-enchiladas.destructuring-bind.org/mjg123/1098417)
77 | * [Core.async - Daisy chain benchmark](http://programming-enchiladas.destructuring-bind.org/swannodette/6542719)
78 | * [Core.async - Rob Pike](http://programming-enchiladas.destructuring-bind.org/swannodette/5903001)
79 | * [Core.async - Hello world!](http://programming-enchiladas.destructuring-bind.org/swannodette/5882703)
80 | * [Core.async - Martin Trojer's Sine Generator](http://programming-enchiladas.destructuring-bind.org/rm-hull/7758795)
81 | * [Core.async - Timothy Baldridge's Blocks](http://programming-enchiladas.destructuring-bind.org/rm-hull/8262502)
82 | * [Core.logic - simple demo](http://programming-enchiladas.destructuring-bind.org/rm-hull/6816151)
83 | * [Core.logic - Classic AI](http://programming-enchiladas.destructuring-bind.org/rm-hull/6816234)
84 | * [Core.logic - Unit Test Suite](http://programming-enchiladas.destructuring-bind.org/rm-hull/6859633)
85 | * [Core.logic - Einstein's Zebra Puzzle](http://programming-enchiladas.destructuring-bind.org/rm-hull/6952960)
86 | * [ClojureScript Quine](http://programming-enchiladas.destructuring-bind.org/rm-hull/7060918)
87 |
88 | ## Prerequisites
89 |
90 | You will need [Leiningen](https://github.com/technomancy/leiningen) 2.1.2 or
91 | above installed.
92 |
93 | ## Running
94 |
95 | To start a web server for the application, run:
96 |
97 | $ lein ring server
98 |
99 | This will start the server at port 3000 or thereabouts. Then create your
100 | ClojureScript gist, and slot in the login and id, and hack on.
101 |
102 | **Optional:** If a connection to a MongoDB database is supplied via config variable
103 | MONGODB_URL as below (substitute values for user, password, host and db as appropriate),
104 | then minimal usage stats will be written out for gamification purposes:
105 |
106 | $ export MONGODB_URL=mongodb://user:password@host:10046/db
107 |
108 | If using heroku, add a config param:
109 |
110 | $ heroku config:add MONGODB_URL=mongodb://user:password@host:10046/db
111 |
112 | **Optional:** By default, Github aggressively throttles requests if requests are anonymous
113 | (60 requests per hour); Set GITHUB_OAUTH_TOKEN to a generated authentication token to
114 | increase the rate limit to 5000 requests per hour:
115 |
116 | $ export GITHUB_OAUTH_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxx
117 |
118 | or
119 |
120 | $ heroku config:add GITHUB_OAUTH_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxx
121 |
122 | (See application note on authorizing 3rd party access to github here:
123 | http://developer.github.com/v3/oauth/#create-a-new-authorization)
124 |
125 | ## Troubleshooting
126 |
127 | Q. Why doesn't my clojurescript compile, it looks ok?
128 |
129 | A1. Did you include a namespace? e.g. `(ns example)`
130 |
131 | A2. Did you name it with a .cljs extension?
132 |
133 | ### NEW! Now supports source maps
134 |
135 | Clojurescript debugging/line-stepping in Chrome is now supported. And it works really well! :)
136 |
137 | ## TODO
138 |
139 | * ~~UI -> home page (carousel of recently viewed, most viewed, rated, etc)~~
140 | * UI -> allow in-page editing
141 | * ~~Stats / tracking~~
142 | * More examples
143 | * Documentation
144 | * ~~Stop using local storage - using Heroku's ephemeral FS not so good for permanent storage (github, Amazon S3 instead?)~~
145 | * ~~Sitemap based on mongo-db rather than Heroku's ephemeral FS.~~
146 | * Automatic screenshot capability with https://github.com/ariya/phantomjs/wiki/Screen-Capture
147 | * ~~Add CORS headers to proxy~~
148 | * ~~Implement simple big-bang (http://docs.racket-lang.org/teachpack/2htdpuniverse.html#(form._world._((lib._2htdp/universe..rkt)._big-bang)))~~
149 |
150 |
151 | ## Known Bugs
152 |
153 | * ~~Routing does not support private gists properly~~ FIXED: 21/12/2013
154 | * Arbor/PhiloGL externs don't work properly - must be compiled with no optimization
155 | * Capture all error scenarios (inc. Google closure warnings and errors & trap javascript errors)
156 |
157 | ## Contributing
158 |
159 | If there are other ClojureScript (or JavaScript) libraries that would be
160 | useful to include, please create a
161 | [new issue](https://github.com/rm-hull/programming-enchiladas/issues/new).
162 |
163 | There is plenty to do, let me know if you can help out; submit a request
164 | for commit access.
165 |
166 | ## References
167 |
168 | * _The (New) Turing Omnibus_, A.K.Dewdney
169 | * http://himera.herokuapp.com/index.html
170 | * http://codepen.io/stuffit/pen/KrAwx
171 | * http://js1k.com/2013-spring/demo/1362
172 | * http://bl.ocks.org
173 | * [Closure Compiler Externs Extractor](http://www.dotnetwise.com/Code/Externs/)
174 |
175 | ## License
176 |
177 | Copyright © 2013 Richard Hull
178 |
179 | Use/copy/fork as per: [Creative Commons](http://creativecommons.org/licenses/by/3.0/legalcode).
180 |
181 |
182 | [](https://bitdeli.com/free "Bitdeli Badge")
183 |
184 |
--------------------------------------------------------------------------------
/local-sandbox/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 | resources/public/js/compiled
--------------------------------------------------------------------------------
/local-sandbox/project.clj:
--------------------------------------------------------------------------------
1 | (defproject enchilada-sandbox "0.0.1-SNAPSHOT"
2 | :description "FIXME: write description"
3 | :url "http://programming-enchiladas.destructuring-bind.org"
4 | :license {:name "Eclipse Public License"
5 | :url "http://www.eclipse.org/legal/epl-v10.html"}
6 | :dependencies [[org.clojure/clojure "1.8.0"]
7 | [org.clojure/clojurescript "1.9.229"]
8 | [figwheel "0.5.8"]
9 | [tailrecursion/cljs-priority-map "1.2.0" :exclusions [org.clojure/clojurescript]]
10 | [org.clojure/core.async "0.2.391" :exclusions [org.clojure/clojurescript]]
11 | [org.clojure/core.logic "0.8.10" :exclusions [org.clojure/clojurescript]]
12 | [org.clojure/core.typed "0.3.28" :exclusions [org.clojure/clojurescript]]
13 | [jayq "2.5.4" :exclusions [org.clojure/clojurescript]]
14 | [com.keminglabs/c2 "0.2.3" :exclusions [org.clojure/clojurescript]]
15 | [com.keminglabs/vomnibus "0.3.2" :exclusions [org.clojure/clojurescript]]
16 | [com.keminglabs/singult "0.1.7-SNAPSHOT" :exclusions [org.clojure/clojurescript]]
17 | [rm-hull/dommy "0.1.4-SNAPSHOT" :exclusions [org.clojure/clojurescript]]
18 | [spellhouse/clairvoyant "0.0-29-g825d69c"]
19 | [rm-hull/cljs-webgl "0.1.5-SNAPSHOT"]
20 | [rm-hull/monet "0.3.0" :exclusions [org.clojure/clojurescript]]
21 | [rm-hull/turtle "0.1.9" :exclusions [org.clojure/clojurescript]]
22 | [rm-hull/wireframes "0.0.1-SNAPSHOT" :exclusions [org.clojure/clojurescript cljs-webgl]]
23 | [rm-hull/ring-gzip-middleware "0.1.7"]
24 | [rm-hull/big-bang "0.0.1-SNAPSHOT" :exclusions [org.clojure/clojurescript]]
25 | [rm-hull/cljs-dataview "0.0.1-SNAPSHOT" :exclusions [org.clojure/clojurescript]]
26 | [rm-hull/polyhedra "0.0.1-SNAPSHOT" :exclusions [org.clojure/clojurescript]]
27 | [rm-hull/inkspot "0.2.1" :exclusions [org.clojure/clojurescript]]
28 | [om "0.7.3" :exclusions [org.clojure/clojurescript]]]
29 | :plugins [[lein-cljsbuild "1.1.4"]
30 | [lein-figwheel "0.5.8"]]
31 | :min-lein-version "2.4.2"
32 | :global-vars { *warn-on-reflection* true}
33 | :source-paths ["../src/enchilada/client" "src"]
34 | :resource-paths ["resources"]
35 | :figwheel {
36 | :http-server-root "public" ;; default and assumes "resources"
37 | :server-port 3449 ;; default
38 | :css-dirs ["resources/public/css"]}
39 | :cljsbuild {
40 | :builds {
41 | :sandbox {
42 | :id "sandbox"
43 | :source-paths ["../src/client" "src"]
44 | :compiler {
45 | :output-to "resources/public/js/compiled/generated.js"
46 | :output-dir "resources/public/js/compiled/out"
47 | :optimizations :none
48 | :source-map true
49 | :language-in :ecmascript5
50 | :language-out :ecmascript5
51 | :pretty-print true
52 | :static-fns true
53 | :externs ["../resources/private/externs/arbor.js"
54 | "../resources/private/externs/jquery.js"
55 | "../resources/private/externs/three.js"
56 | "../resources/private/externs/react.js"
57 | "../resources/private/externs/PhiloGL.js"]
58 | :foreign-libs [{:file "../resources/private/js/arbor.js" :provides ["arbor"]}
59 | {:file "../resources/private/js/react.js" :provides ["React"]}
60 | {:file "../resources/private/js/three.js" :provides ["THREE"]}
61 | {:file "../resources/public/js/gl-matrix-min.js" :provides ["mat3","mat4","vec3"]}
62 | {:file "../resources/public/js/webgl-utils.js" :provides ["WebGLUtils"]}]
63 | :libs ["../resources/private/js/singult.js"]}}}})
64 |
--------------------------------------------------------------------------------
/local-sandbox/resources/public/css/default.css:
--------------------------------------------------------------------------------
1 | ../../../../resources/public/css/default.css
--------------------------------------------------------------------------------
/local-sandbox/resources/public/images/favicon.png:
--------------------------------------------------------------------------------
1 | ../../../../resources/public/images/favicon.png
--------------------------------------------------------------------------------
/local-sandbox/resources/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Programming Enchiladas - Lighttable Sandbox
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/local-sandbox/resources/public/js/doc.shim.js:
--------------------------------------------------------------------------------
1 | ../../../../resources/public/js/doc.shim.js
--------------------------------------------------------------------------------
/local-sandbox/resources/public/js/gl-matrix-min.js:
--------------------------------------------------------------------------------
1 | ../../../../resources/private/js/gl-matrix-min.js
--------------------------------------------------------------------------------
/local-sandbox/resources/public/js/webgl-utils.js:
--------------------------------------------------------------------------------
1 | ../../../../resources/private/js/webgl-utils.js
--------------------------------------------------------------------------------
/local-sandbox/src/bootstrapper.cljs:
--------------------------------------------------------------------------------
1 | (ns bootstrapper
2 | (:require
3 | [figwheel.client :as fw :include-macros true]
4 | [enchilada :refer [canvas ctx]]
5 |
6 | ; Add some content in files under src/enchilada, and view http://localhost:3449
7 | ; Require some def from the target source file here
8 | [enchilada.simple-demo :refer [dummy]]
9 |
10 | ))
11 |
12 | (fw/watch-and-reload :jsload-callback (fn [] (print "reloaded")))
13 |
--------------------------------------------------------------------------------
/local-sandbox/src/enchilada/.gitignore:
--------------------------------------------------------------------------------
1 | *
--------------------------------------------------------------------------------
/local-sandbox/src/enchilada/simple-demo.cljs:
--------------------------------------------------------------------------------
1 | (ns enchilada.simple-demo
2 | (:require
3 | [jayq.core :refer [show]]
4 | [enchilada :refer [ctx canvas]]
5 | [monet.canvas :refer [fill-style fill-rect]]))
6 |
7 | (def dummy)
8 |
9 | (show canvas)
10 | (->
11 | ctx
12 | (fill-style :red)
13 | (fill-rect {:x 40 :y 70 :w 55 :h 47}))
14 |
--------------------------------------------------------------------------------
/project.clj:
--------------------------------------------------------------------------------
1 | (defproject programming-enchiladas "0.1.0"
2 | :description "FIXME: write description"
3 | :url "http://programming-enchiladas.destructuring-bind.org"
4 | :license {:name "Eclipse Public License"
5 | :url "http://www.eclipse.org/legal/epl-v10.html"}
6 | :dependencies [[org.clojure/clojure "1.6.0"]
7 | [org.clojure/clojurescript "0.0-2411"]
8 | [org.clojure/data.json "0.2.5"]
9 | [org.clojure/data.xml "0.0.8"]
10 | [tailrecursion/cljs-priority-map "1.1.0" :exclusions [org.clojure/clojurescript]]
11 | [org.clojure/core.async "0.1.338.0-5c5012-alpha" :exclusions [org.clojure/clojurescript]]
12 | [org.clojure/core.logic "0.8.9" :exclusions [org.clojure/clojurescript]]
13 | ;[org.clojure/core.match "0.2.0"]
14 | [org.clojure/core.typed "0.2.72" :exclusions [org.clojure/clojurescript]]
15 | [clj-stacktrace "0.2.8"]
16 | [clj-http "1.0.1"]
17 | [clj-time "0.8.0"]
18 | [compojure "1.3.1"]
19 | [ring/ring-core "1.3.2"]
20 | [hiccup "1.0.5"]
21 | [caponia "0.3.3"]
22 | [jayq "2.5.2" :exclusions [org.clojure/clojurescript]]
23 | [com.keminglabs/c2 "0.2.3" :exclusions [org.clojure/clojurescript]]
24 | [com.keminglabs/vomnibus "0.3.2" :exclusions [org.clojure/clojurescript]]
25 | [com.keminglabs/singult "0.1.7-SNAPSHOT" :exclusions [org.clojure/clojurescript]]
26 | [rm-hull/dommy "0.1.3-SNAPSHOT" :exclusions [org.clojure/clojurescript]]
27 | [com.novemberain/monger "1.8.0"]
28 | [spellhouse/clairvoyant "0.0-29-g825d69c"]
29 | [me.raynes/fs "1.4.5"]
30 | [cljs-webgl "0.1.5-SNAPSHOT"]
31 | [rm-hull/monet "0.2.1" :exclusions [org.clojure/clojurescript]]
32 | [rm-hull/turtle "0.1.9-SNAPSHOT" :exclusions [org.clojure/clojurescript]]
33 | [rm-hull/wireframes "0.0.1-SNAPSHOT" :exclusions [org.clojure/clojurescript]]
34 | [rm-hull/ring-gzip-middleware "0.1.7"]
35 | [rm-hull/big-bang "0.0.1-SNAPSHOT" :exclusions [org.clojure/clojurescript]]
36 | [rm-hull/cljs-dataview "0.0.1-SNAPSHOT" :exclusions [org.clojure/clojurescript]]
37 | [rm-hull/polyhedra "0.0.1-SNAPSHOT" :exclusions [org.clojure/clojurescript]]
38 | [rm-hull/inkspot "0.0.1-SNAPSHOT" :exclusions [org.clojure/clojurescript]]
39 | [om "0.3.0" :exclusions [org.clojure/clojurescript]]]
40 | :plugins [[lein-ring "0.8.11"]]
41 | :ring {:handler enchilada.handler/app}
42 | :profiles
43 | {:dev {:dependencies [[ring-mock "0.1.5"]]}}
44 | :min-lein-version "2.4.2"
45 | :global-vars { *warn-on-reflection* true})
46 |
--------------------------------------------------------------------------------
/resources/private/externs/arbor.js:
--------------------------------------------------------------------------------
1 | var arbor = {
2 | "ParticleSystem": function () {},
3 | "Point": function () {},
4 | "etc": {
5 | "trace": function () {},
6 | "dirname": function () {},
7 | "basename": function () {},
8 | "ordinalize": function () {},
9 | "objcopy": function () {},
10 | "objcmp": function () {},
11 | "objkeys": function () {},
12 | "objmerge": function () {},
13 | "uniq": function () {},
14 | "arbor_path": function () {}
15 | },
16 | "Tween": function () {},
17 | "colors": {
18 | "CSS": {
19 | "aliceblue": {},
20 | "antiquewhite": {},
21 | "aqua": {},
22 | "aquamarine": {},
23 | "azure": {},
24 | "beige": {},
25 | "bisque": {},
26 | "black": {},
27 | "blanchedalmond": {},
28 | "blue": {},
29 | "blueviolet": {},
30 | "brown": {},
31 | "burlywood": {},
32 | "cadetblue": {},
33 | "chartreuse": {},
34 | "chocolate": {},
35 | "coral": {},
36 | "cornflowerblue": {},
37 | "cornsilk": {},
38 | "crimson": {},
39 | "cyan": {},
40 | "darkblue": {},
41 | "darkcyan": {},
42 | "darkgoldenrod": {},
43 | "darkgray": {},
44 | "darkgrey": {},
45 | "darkgreen": {},
46 | "darkkhaki": {},
47 | "darkmagenta": {},
48 | "darkolivegreen": {},
49 | "darkorange": {},
50 | "darkorchid": {},
51 | "darkred": {},
52 | "darksalmon": {},
53 | "darkseagreen": {},
54 | "darkslateblue": {},
55 | "darkslategray": {},
56 | "darkslategrey": {},
57 | "darkturquoise": {},
58 | "darkviolet": {},
59 | "deeppink": {},
60 | "deepskyblue": {},
61 | "dimgray": {},
62 | "dimgrey": {},
63 | "dodgerblue": {},
64 | "firebrick": {},
65 | "floralwhite": {},
66 | "forestgreen": {},
67 | "fuchsia": {},
68 | "gainsboro": {},
69 | "ghostwhite": {},
70 | "gold": {},
71 | "goldenrod": {},
72 | "gray": {},
73 | "grey": {},
74 | "green": {},
75 | "greenyellow": {},
76 | "honeydew": {},
77 | "hotpink": {},
78 | "indianred": {},
79 | "indigo": {},
80 | "ivory": {},
81 | "khaki": {},
82 | "lavender": {},
83 | "lavenderblush": {},
84 | "lawngreen": {},
85 | "lemonchiffon": {},
86 | "lightblue": {},
87 | "lightcoral": {},
88 | "lightcyan": {},
89 | "lightgoldenrodyellow": {},
90 | "lightgray": {},
91 | "lightgrey": {},
92 | "lightgreen": {},
93 | "lightpink": {},
94 | "lightsalmon": {},
95 | "lightseagreen": {},
96 | "lightskyblue": {},
97 | "lightslategray": {},
98 | "lightslategrey": {},
99 | "lightsteelblue": {},
100 | "lightyellow": {},
101 | "lime": {},
102 | "limegreen": {},
103 | "linen": {},
104 | "magenta": {},
105 | "maroon": {},
106 | "mediumaquamarine": {},
107 | "mediumblue": {},
108 | "mediumorchid": {},
109 | "mediumpurple": {},
110 | "mediumseagreen": {},
111 | "mediumslateblue": {},
112 | "mediumspringgreen": {},
113 | "mediumturquoise": {},
114 | "mediumvioletred": {},
115 | "midnightblue": {},
116 | "mintcream": {},
117 | "mistyrose": {},
118 | "moccasin": {},
119 | "navajowhite": {},
120 | "navy": {},
121 | "oldlace": {},
122 | "olive": {},
123 | "olivedrab": {},
124 | "orange": {},
125 | "orangered": {},
126 | "orchid": {},
127 | "palegoldenrod": {},
128 | "palegreen": {},
129 | "paleturquoise": {},
130 | "palevioletred": {},
131 | "papayawhip": {},
132 | "peachpuff": {},
133 | "peru": {},
134 | "pink": {},
135 | "plum": {},
136 | "powderblue": {},
137 | "purple": {},
138 | "red": {},
139 | "rosybrown": {},
140 | "royalblue": {},
141 | "saddlebrown": {},
142 | "salmon": {},
143 | "sandybrown": {},
144 | "seagreen": {},
145 | "seashell": {},
146 | "sienna": {},
147 | "silver": {},
148 | "skyblue": {},
149 | "slateblue": {},
150 | "slategray": {},
151 | "slategrey": {},
152 | "snow": {},
153 | "springgreen": {},
154 | "steelblue": {},
155 | "tan": {},
156 | "teal": {},
157 | "thistle": {},
158 | "tomato": {},
159 | "turquoise": {},
160 | "violet": {},
161 | "wheat": {},
162 | "white": {},
163 | "whitesmoke": {},
164 | "yellow": {},
165 | "yellowgreen": {}
166 | },
167 | "validate": function () {},
168 | "decode": function () {},
169 | "encode": function () {},
170 | "blend": function () {}
171 | }
172 | }
--------------------------------------------------------------------------------
/resources/private/externs/react.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Externs for React 0.5.1.
3 | *
4 | * @see http://facebook.github.io/react
5 | * @externs
6 | */
7 |
8 | /**
9 | * @type {Object}
10 | * @const
11 | */
12 | var React = {};
13 |
14 | /**
15 | * @type {string}
16 | */
17 | React.version;
18 |
19 | /**
20 | * @param {boolean} shouldUseTouch
21 | */
22 | React.initializeTouchEvents = function(shouldUseTouch) {};
23 |
24 | /**
25 | * @param {Object} specification
26 | * @return {function(
27 | Object=,
28 | (string|React.ReactComponent|Array.)=
29 | ): React.ReactComponent}
30 | */
31 | React.createClass = function(specification) {};
32 |
33 | /**
34 | * @param {*} componentClass
35 | * @return {boolean}
36 | */
37 | React.isValidClass = function(componentClass) {};
38 |
39 | /**
40 | * @param {React.ReactComponent} container
41 | * @param {Element} mountPoint
42 | * @param {Function=} callback
43 | * @return {React.ReactComponent}
44 | */
45 | React.renderComponent = function(container, mountPoint, callback) {};
46 |
47 | /**
48 | * @param {Element} container
49 | */
50 | React.unmountComponentAtNode = function(container) {};
51 |
52 | /**
53 | * @param {React.ReactComponent} component
54 | * @param {Function} callback
55 | */
56 | React.renderComponentToString = function(component, callback) {};
57 |
58 | /**
59 | * @interface
60 | */
61 | React.ReactComponent = function() {};
62 |
63 | /**
64 | * @type {Object}
65 | */
66 | React.ReactComponent.prototype.props;
67 |
68 | /**
69 | * @type {Object}
70 | */
71 | React.ReactComponent.prototype.state;
72 |
73 | /**
74 | * @type {Object}
75 | */
76 | React.ReactComponent.prototype.refs;
77 |
78 | /**
79 | * @type {Object}
80 | * @protected
81 | */
82 | React.ReactComponent.prototype.propTypes;
83 |
84 | /**
85 | * @param {Object} nextProps
86 | * @param {Function=} callback
87 | */
88 | React.ReactComponent.prototype.setProps = function(nextProps, callback) {};
89 |
90 | /**
91 | * @return {Object}
92 | */
93 | React.ReactComponent.prototype.getInitialState = function() {};
94 |
95 | /**
96 | * @return {Object}
97 | */
98 | React.ReactComponent.prototype.getDefaultProps = function() {};
99 |
100 | /**
101 | * @return {Element}
102 | */
103 | React.ReactComponent.prototype.getDOMNode = function() {};
104 |
105 | /**
106 | * @param {Object} nextProps
107 | * @param {Function=} callback
108 | */
109 | React.ReactComponent.prototype.replaceProps = function(nextProps, callback) {};
110 |
111 | /**
112 | * @param {React.ReactComponent} targetComponent
113 | * @return {React.ReactComponent}
114 | * @protected
115 | */
116 | React.ReactComponent.prototype.transferPropsTo = function(targetComponent) {};
117 |
118 | /**
119 | * @param {Function=} callback
120 | * @protected
121 | */
122 | React.ReactComponent.prototype.forceUpdate = function(callback) {};
123 |
124 | /**
125 | * @param {Object} nextState
126 | * @param {Function=} callback
127 | * @protected
128 | */
129 | React.ReactComponent.prototype.setState = function(nextState, callback) {};
130 |
131 | /**
132 | * @param {Object} nextState
133 | * @param {Function=} callback
134 | * @protected
135 | */
136 | React.ReactComponent.prototype.replaceState = function(nextState, callback) {};
137 |
138 | /**
139 | * @param {Element} element
140 | * @protected
141 | */
142 | React.ReactComponent.prototype.componentDidMount = function(element) {};
143 |
144 | /**
145 | * @param {Object} nextProps
146 | * @protected
147 | */
148 | React.ReactComponent.prototype.componentWillReceiveProps = function(
149 | nextProps) {};
150 |
151 | /**
152 | * @param {Object} nextProps
153 | * @param {Object} nextState
154 | * @return {boolean}
155 | * @protected
156 | */
157 | React.ReactComponent.prototype.shouldComponentUpdate = function(
158 | nextProps, nextState) {};
159 |
160 | /**
161 | * @param {Object} nextProps
162 | * @param {Object} nextState
163 | * @protected
164 | */
165 | React.ReactComponent.prototype.componentWillUpdate = function(
166 | nextProps, nextState) {};
167 |
168 | /**
169 | * @return {React.ReactComponent}
170 | * @protected
171 | */
172 | React.ReactComponent.prototype.render = function() {};
173 |
174 | /**
175 | * @param {Object} prevProps
176 | * @param {Object} prevState
177 | * @param {Element} element
178 | * @protected
179 | */
180 | React.ReactComponent.prototype.componentDidUpdate = function(
181 | prevProps, prevState, element) {};
182 |
183 | /**
184 | * @protected
185 | */
186 | React.ReactComponent.prototype.componentWillUnmount = function() {};
187 |
188 | /**
189 | * @type {Object}
190 | * @const
191 | */
192 | React.DOM = {};
193 |
194 | /**
195 | * @param {Object=} props
196 | * @param {...string|React.ReactComponent|Array.} children
197 | * @return {React.ReactComponent}
198 | * @protected
199 | */
200 | React.DOM.a = function(props, children) {};
201 |
202 | /**
203 | * @param {Object=} props
204 | * @param {...string|React.ReactComponent|Array.} children
205 | * @return {React.ReactComponent}
206 | * @protected
207 | */
208 | React.DOM.abbr = function(props, children) {};
209 |
210 | /**
211 | * @param {Object=} props
212 | * @param {...string|React.ReactComponent|Array.} children
213 | * @return {React.ReactComponent}
214 | * @protected
215 | */
216 | React.DOM.address = function(props, children) {};
217 |
218 | /**
219 | * @param {Object=} props
220 | * @param {...string|React.ReactComponent|Array.} children
221 | * @return {React.ReactComponent}
222 | * @protected
223 | */
224 | React.DOM.audio = function(props, children) {};
225 |
226 | /**
227 | * @param {Object=} props
228 | * @param {...string|React.ReactComponent|Array.} children
229 | * @return {React.ReactComponent}
230 | * @protected
231 | */
232 | React.DOM.b = function(props, children) {};
233 |
234 | /**
235 | * @param {Object=} props
236 | * @param {...string|React.ReactComponent|Array.} children
237 | * @return {React.ReactComponent}
238 | * @protected
239 | */
240 | React.DOM.body = function(props, children) {};
241 |
242 | /**
243 | * @param {Object=} props
244 | * @param {...string|React.ReactComponent|Array.} children
245 | * @return {React.ReactComponent}
246 | * @protected
247 | */
248 | React.DOM.br = function(props, children) {};
249 |
250 | /**
251 | * @param {Object=} props
252 | * @param {...string|React.ReactComponent|Array.} children
253 | * @return {React.ReactComponent}
254 | * @protected
255 | */
256 | React.DOM.button = function(props, children) {};
257 |
258 | /**
259 | * @param {Object=} props
260 | * @param {...string|React.ReactComponent|Array.} children
261 | * @return {React.ReactComponent}
262 | * @protected
263 | */
264 | React.DOM.code = function(props, children) {};
265 |
266 | /**
267 | * @param {Object=} props
268 | * @param {...string|React.ReactComponent|Array.} children
269 | * @return {React.ReactComponent}
270 | * @protected
271 | */
272 | React.DOM.col = function(props, children) {};
273 |
274 | /**
275 | * @param {Object=} props
276 | * @param {...string|React.ReactComponent|Array.} children
277 | * @return {React.ReactComponent}
278 | * @protected
279 | */
280 | React.DOM.colgroup = function(props, children) {};
281 |
282 | /**
283 | * @param {Object=} props
284 | * @param {...string|React.ReactComponent|Array.} children
285 | * @return {React.ReactComponent}
286 | * @protected
287 | */
288 | React.DOM.dd = function(props, children) {};
289 |
290 | /**
291 | * @param {Object=} props
292 | * @param {...string|React.ReactComponent|Array.} children
293 | * @return {React.ReactComponent}
294 | * @protected
295 | */
296 | React.DOM.div = function(props, children) {};
297 |
298 | /**
299 | * @param {Object=} props
300 | * @param {...string|React.ReactComponent|Array.} children
301 | * @return {React.ReactComponent}
302 | * @protected
303 | */
304 | React.DOM.section = function(props, children) {};
305 |
306 | /**
307 | * @param {Object=} props
308 | * @param {...string|React.ReactComponent|Array.} children
309 | * @return {React.ReactComponent}
310 | * @protected
311 | */
312 | React.DOM.dl = function(props, children) {};
313 |
314 | /**
315 | * @param {Object=} props
316 | * @param {...string|React.ReactComponent|Array.} children
317 | * @return {React.ReactComponent}
318 | * @protected
319 | */
320 | React.DOM.dt = function(props, children) {};
321 |
322 | /**
323 | * @param {Object=} props
324 | * @param {...string|React.ReactComponent|Array.} children
325 | * @return {React.ReactComponent}
326 | * @protected
327 | */
328 | React.DOM.em = function(props, children) {};
329 |
330 | /**
331 | * @param {Object=} props
332 | * @param {...string|React.ReactComponent|Array.} children
333 | * @return {React.ReactComponent}
334 | * @protected
335 | */
336 | React.DOM.embed = function(props, children) {};
337 |
338 | /**
339 | * @param {Object=} props
340 | * @param {...string|React.ReactComponent|Array.} children
341 | * @return {React.ReactComponent}
342 | * @protected
343 | */
344 | React.DOM.fieldset = function(props, children) {};
345 |
346 | /**
347 | * @param {Object=} props
348 | * @param {...string|React.ReactComponent|Array.} children
349 | * @return {React.ReactComponent}
350 | * @protected
351 | */
352 | React.DOM.footer = function(props, children) {};
353 |
354 | /**
355 | * @param {Object=} props
356 | * @param {...string|React.ReactComponent|Array.} children
357 | * @return {React.ReactComponent}
358 | * @protected
359 | */
360 | React.DOM.form = function(props, children) {};
361 |
362 | /**
363 | * @param {Object=} props
364 | * @param {...string|React.ReactComponent|Array.} children
365 | * @return {React.ReactComponent}
366 | * @protected
367 | */
368 | React.DOM.h1 = function(props, children) {};
369 |
370 | /**
371 | * @param {Object=} props
372 | * @param {...string|React.ReactComponent|Array.} children
373 | * @return {React.ReactComponent}
374 | * @protected
375 | */
376 | React.DOM.h2 = function(props, children) {};
377 |
378 | /**
379 | * @param {Object=} props
380 | * @param {...string|React.ReactComponent|Array.} children
381 | * @return {React.ReactComponent}
382 | * @protected
383 | */
384 | React.DOM.h3 = function(props, children) {};
385 |
386 | /**
387 | * @param {Object=} props
388 | * @param {...string|React.ReactComponent|Array.} children
389 | * @return {React.ReactComponent}
390 | * @protected
391 | */
392 | React.DOM.h4 = function(props, children) {};
393 |
394 | /**
395 | * @param {Object=} props
396 | * @param {...string|React.ReactComponent|Array.} children
397 | * @return {React.ReactComponent}
398 | * @protected
399 | */
400 | React.DOM.h5 = function(props, children) {};
401 |
402 | /**
403 | * @param {Object=} props
404 | * @param {...string|React.ReactComponent|Array.} children
405 | * @return {React.ReactComponent}
406 | * @protected
407 | */
408 | React.DOM.h6 = function(props, children) {};
409 |
410 | /**
411 | * @param {Object=} props
412 | * @param {...string|React.ReactComponent|Array.} children
413 | * @return {React.ReactComponent}
414 | * @protected
415 | */
416 | React.DOM.header = function(props, children) {};
417 |
418 | /**
419 | * @param {Object=} props
420 | * @param {...string|React.ReactComponent|Array.} children
421 | * @return {React.ReactComponent}
422 | * @protected
423 | */
424 | React.DOM.hr = function(props, children) {};
425 |
426 | /**
427 | * @param {Object=} props
428 | * @param {...string|React.ReactComponent|Array.} children
429 | * @return {React.ReactComponent}
430 | * @protected
431 | */
432 | React.DOM.i = function(props, children) {};
433 |
434 | /**
435 | * @param {Object=} props
436 | * @param {...string|React.ReactComponent|Array.} children
437 | * @return {React.ReactComponent}
438 | * @protected
439 | */
440 | React.DOM.iframe = function(props, children) {};
441 |
442 | /**
443 | * @param {Object=} props
444 | * @param {...string|React.ReactComponent|Array.} children
445 | * @return {React.ReactComponent}
446 | * @protected
447 | */
448 | React.DOM.img = function(props, children) {};
449 |
450 | /**
451 | * @param {Object=} props
452 | * @param {...string|React.ReactComponent|Array.} children
453 | * @return {React.ReactComponent}
454 | * @protected
455 | */
456 | React.DOM.input = function(props, children) {};
457 |
458 | /**
459 | * @param {Object=} props
460 | * @param {...string|React.ReactComponent|Array.} children
461 | * @return {React.ReactComponent}
462 | * @protected
463 | */
464 | React.DOM.label = function(props, children) {};
465 |
466 | /**
467 | * @param {Object=} props
468 | * @param {...string|React.ReactComponent|Array.} children
469 | * @return {React.ReactComponent}
470 | * @protected
471 | */
472 | React.DOM.legend = function(props, children) {};
473 |
474 | /**
475 | * @param {Object=} props
476 | * @param {...string|React.ReactComponent|Array.} children
477 | * @return {React.ReactComponent}
478 | * @protected
479 | */
480 | React.DOM.li = function(props, children) {};
481 |
482 | /**
483 | * @param {Object=} props
484 | * @param {...string|React.ReactComponent|Array.} children
485 | * @return {React.ReactComponent}
486 | * @protected
487 | */
488 | React.DOM.line = function(props, children) {};
489 |
490 | /**
491 | * @param {Object=} props
492 | * @param {...string|React.ReactComponent|Array.} children
493 | * @return {React.ReactComponent}
494 | * @protected
495 | */
496 | React.DOM.nav = function(props, children) {};
497 |
498 | /**
499 | * @param {Object=} props
500 | * @param {...string|React.ReactComponent|Array.} children
501 | * @return {React.ReactComponent}
502 | * @protected
503 | */
504 | React.DOM.object = function(props, children) {};
505 |
506 | /**
507 | * @param {Object=} props
508 | * @param {...string|React.ReactComponent|Array.} children
509 | * @return {React.ReactComponent}
510 | * @protected
511 | */
512 | React.DOM.ol = function(props, children) {};
513 |
514 | /**
515 | * @param {Object=} props
516 | * @param {...string|React.ReactComponent|Array.} children
517 | * @return {React.ReactComponent}
518 | * @protected
519 | */
520 | React.DOM.optgroup = function(props, children) {};
521 |
522 | /**
523 | * @param {Object=} props
524 | * @param {...string|React.ReactComponent|Array.} children
525 | * @return {React.ReactComponent}
526 | * @protected
527 | */
528 | React.DOM.option = function(props, children) {};
529 |
530 | /**
531 | * @param {Object=} props
532 | * @param {...string|React.ReactComponent|Array.} children
533 | * @return {React.ReactComponent}
534 | * @protected
535 | */
536 | React.DOM.p = function(props, children) {};
537 |
538 | /**
539 | * @param {Object=} props
540 | * @param {...string|React.ReactComponent|Array.} children
541 | * @return {React.ReactComponent}
542 | * @protected
543 | */
544 | React.DOM.param = function(props, children) {};
545 |
546 | /**
547 | * @param {Object=} props
548 | * @param {...string|React.ReactComponent|Array.} children
549 | * @return {React.ReactComponent}
550 | * @protected
551 | */
552 | React.DOM.pre = function(props, children) {};
553 |
554 | /**
555 | * @param {Object=} props
556 | * @param {...string|React.ReactComponent|Array.} children
557 | * @return {React.ReactComponent}
558 | * @protected
559 | */
560 | React.DOM.select = function(props, children) {};
561 |
562 | /**
563 | * @param {Object=} props
564 | * @param {...string|React.ReactComponent|Array.} children
565 | * @return {React.ReactComponent}
566 | * @protected
567 | */
568 | React.DOM.small = function(props, children) {};
569 |
570 | /**
571 | * @param {Object=} props
572 | * @param {...string|React.ReactComponent|Array.} children
573 | * @return {React.ReactComponent}
574 | * @protected
575 | */
576 | React.DOM.source = function(props, children) {};
577 |
578 | /**
579 | * @param {Object=} props
580 | * @param {...string|React.ReactComponent|Array.} children
581 | * @return {React.ReactComponent}
582 | * @protected
583 | */
584 | React.DOM.span = function(props, children) {};
585 |
586 | /**
587 | * @param {Object=} props
588 | * @param {...string|React.ReactComponent|Array.} children
589 | * @return {React.ReactComponent}
590 | * @protected
591 | */
592 | React.DOM.sub = function(props, children) {};
593 |
594 | /**
595 | * @param {Object=} props
596 | * @param {...string|React.ReactComponent|Array.} children
597 | * @return {React.ReactComponent}
598 | * @protected
599 | */
600 | React.DOM.sup = function(props, children) {};
601 |
602 | /**
603 | * @param {Object=} props
604 | * @param {...string|React.ReactComponent|Array.} children
605 | * @return {React.ReactComponent}
606 | * @protected
607 | */
608 | React.DOM.strong = function(props, children) {};
609 |
610 | /**
611 | * @param {Object=} props
612 | * @param {...string|React.ReactComponent|Array.} children
613 | * @return {React.ReactComponent}
614 | * @protected
615 | */
616 | React.DOM.table = function(props, children) {};
617 |
618 | /**
619 | * @param {Object=} props
620 | * @param {...string|React.ReactComponent|Array.} children
621 | * @return {React.ReactComponent}
622 | * @protected
623 | */
624 | React.DOM.tbody = function(props, children) {};
625 |
626 | /**
627 | * @param {Object=} props
628 | * @param {...string|React.ReactComponent|Array.} children
629 | * @return {React.ReactComponent}
630 | * @protected
631 | */
632 | React.DOM.td = function(props, children) {};
633 |
634 | /**
635 | * @param {Object=} props
636 | * @param {...string|React.ReactComponent|Array.} children
637 | * @return {React.ReactComponent}
638 | * @protected
639 | */
640 | React.DOM.textarea = function(props, children) {};
641 |
642 | /**
643 | * @param {Object=} props
644 | * @param {...string|React.ReactComponent|Array.} children
645 | * @return {React.ReactComponent}
646 | * @protected
647 | */
648 | React.DOM.tfoot = function(props, children) {};
649 |
650 | /**
651 | * @param {Object=} props
652 | * @param {...string|React.ReactComponent|Array.} children
653 | * @return {React.ReactComponent}
654 | * @protected
655 | */
656 | React.DOM.th = function(props, children) {};
657 |
658 | /**
659 | * @param {Object=} props
660 | * @param {...string|React.ReactComponent|Array.} children
661 | * @return {React.ReactComponent}
662 | * @protected
663 | */
664 | React.DOM.thead = function(props, children) {};
665 |
666 | /**
667 | * @param {Object=} props
668 | * @param {...string|React.ReactComponent|Array.} children
669 | * @return {React.ReactComponent}
670 | * @protected
671 | */
672 | React.DOM.time = function(props, children) {};
673 |
674 | /**
675 | * @param {Object=} props
676 | * @param {...string|React.ReactComponent|Array.} children
677 | * @return {React.ReactComponent}
678 | * @protected
679 | */
680 | React.DOM.title = function(props, children) {};
681 |
682 | /**
683 | * @param {Object=} props
684 | * @param {...string|React.ReactComponent|Array.} children
685 | * @return {React.ReactComponent}
686 | * @protected
687 | */
688 | React.DOM.tr = function(props, children) {};
689 |
690 | /**
691 | * @param {Object=} props
692 | * @param {...string|React.ReactComponent|Array.} children
693 | * @return {React.ReactComponent}
694 | * @protected
695 | */
696 | React.DOM.u = function(props, children) {};
697 |
698 | /**
699 | * @param {Object=} props
700 | * @param {...string|React.ReactComponent|Array.} children
701 | * @return {React.ReactComponent}
702 | * @protected
703 | */
704 | React.DOM.ul = function(props, children) {};
705 |
706 | /**
707 | * @param {Object=} props
708 | * @param {...string|React.ReactComponent|Array.} children
709 | * @return {React.ReactComponent}
710 | * @protected
711 | */
712 | React.DOM.video = function(props, children) {};
713 |
714 | /**
715 | * @param {Object=} props
716 | * @param {...string|React.ReactComponent|Array.} children
717 | * @return {React.ReactComponent}
718 | * @protected
719 | */
720 | React.DOM.wbr = function(props, children) {};
721 |
722 | /**
723 | * @param {Object=} props
724 | * @param {...string|React.ReactComponent|Array.} children
725 | * @return {React.ReactComponent}
726 | * @protected
727 | */
728 | React.DOM.circle = function(props, children) {};
729 |
730 | /**
731 | * @param {Object=} props
732 | * @param {...string|React.ReactComponent|Array.} children
733 | * @return {React.ReactComponent}
734 | * @protected
735 | */
736 | React.DOM.g = function(props, children) {};
737 |
738 | /**
739 | * @param {Object=} props
740 | * @param {...string|React.ReactComponent|Array.} children
741 | * @return {React.ReactComponent}
742 | * @protected
743 | */
744 | React.DOM.path = function(props, children) {};
745 |
746 | /**
747 | * @param {Object=} props
748 | * @param {...string|React.ReactComponent|Array.} children
749 | * @return {React.ReactComponent}
750 | * @protected
751 | */
752 | React.DOM.polyline = function(props, children) {};
753 |
754 | /**
755 | * @param {Object=} props
756 | * @param {...string|React.ReactComponent|Array.} children
757 | * @return {React.ReactComponent}
758 | * @protected
759 | */
760 | React.DOM.rect = function(props, children) {};
761 |
762 | /**
763 | * @param {Object=} props
764 | * @param {...string|React.ReactComponent|Array.} children
765 | * @return {React.ReactComponent}
766 | * @protected
767 | */
768 | React.DOM.svg = function(props, children) {};
769 |
770 | /**
771 | * @param {Object=} props
772 | * @param {...string|React.ReactComponent|Array.} children
773 | * @return {React.ReactComponent}
774 | * @protected
775 | */
776 | React.DOM.text = function(props, children) {};
777 |
--------------------------------------------------------------------------------
/resources/private/js/arbor.js:
--------------------------------------------------------------------------------
1 | //
2 | // arbor.js - version 0.91
3 | // a graph vizualization toolkit
4 | //
5 | // Copyright (c) 2012 Samizdat Drafting Co.
6 | // Physics code derived from springy.js, copyright (c) 2010 Dennis Hotson
7 | //
8 | // Permission is hereby granted, free of charge, to any person
9 | // obtaining a copy of this software and associated documentation
10 | // files (the "Software"), to deal in the Software without
11 | // restriction, including without limitation the rights to use,
12 | // copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the
14 | // Software is furnished to do so, subject to the following
15 | // conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be
18 | // included in all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | // OTHER DEALINGS IN THE SOFTWARE.
28 | //
29 |
30 | (function($){
31 |
32 | /* etc.js */ var trace=function(msg){if(typeof(window)=="undefined"||!window.console){return}var len=arguments.length;var args=[];for(var i=0;i0){return a[0]}else{return null}};
33 | /* kernel.js */ var Kernel=function(b){var k=window.location.protocol=="file:"&&navigator.userAgent.toLowerCase().indexOf("chrome")>-1;var a=(window.Worker!==undefined&&!k);var i=null;var c=null;var f=[];f.last=new Date();var l=null;var e=null;var d=null;var h=null;var g=false;var j={system:b,tween:null,nodes:{},init:function(){if(typeof(Tween)!="undefined"){c=Tween()}else{if(typeof(arbor.Tween)!="undefined"){c=arbor.Tween()}else{c={busy:function(){return false},tick:function(){return true},to:function(){trace("Please include arbor-tween.js to enable tweens");c.to=function(){};return}}}}j.tween=c;var m=b.parameters();if(a){trace("arbor.js/web-workers",m);l=setInterval(j.screenUpdate,m.timeout);i=new Worker(arbor_path()+"generated.js");i.onmessage=j.workerMsg;i.onerror=function(n){trace("physics:",n)};i.postMessage({type:"physics",physics:objmerge(m,{timeout:Math.ceil(m.timeout)})})}else{trace("arbor.js/single-threaded",m);i=Physics(m.dt,m.stiffness,m.repulsion,m.friction,j.system._updateGeometry,m.integrator);j.start()}return j},graphChanged:function(m){if(a){i.postMessage({type:"changes",changes:m})}else{i._update(m)}j.start()},particleModified:function(n,m){if(a){i.postMessage({type:"modify",id:n,mods:m})}else{i.modifyNode(n,m)}j.start()},physicsModified:function(m){if(!isNaN(m.timeout)){if(a){clearInterval(l);l=setInterval(j.screenUpdate,m.timeout)}else{clearInterval(d);d=null}}if(a){i.postMessage({type:"sys",param:m})}else{i.modifyPhysics(m)}j.start()},workerMsg:function(n){var m=n.data.type;if(m=="geometry"){j.workerUpdate(n.data)}else{trace("physics:",n.data)}},_lastPositions:null,workerUpdate:function(m){j._lastPositions=m;j._lastBounds=m.bounds},_lastFrametime:new Date().valueOf(),_lastBounds:null,_currentRenderer:null,screenUpdate:function(){var n=new Date().valueOf();var m=false;if(j._lastPositions!==null){j.system._updateGeometry(j._lastPositions);j._lastPositions=null;m=true}if(c&&c.busy()){m=true}if(j.system._updateBounds(j._lastBounds)){m=true}if(m){var o=j.system.renderer;if(o!==undefined){if(o!==e){o.init(j.system);e=o}if(c){c.tick()}o.redraw();var p=f.last;f.last=new Date();f.push(f.last-p);if(f.length>50){f.shift()}}}},physicsUpdate:function(){if(c){c.tick()}i.tick();var n=j.system._updateBounds();if(c&&c.busy()){n=true}var o=j.system.renderer;var m=new Date();var o=j.system.renderer;if(o!==undefined){if(o!==e){o.init(j.system);e=o}o.redraw({timestamp:m})}var q=f.last;f.last=m;f.push(f.last-q);if(f.length>50){f.shift()}var p=i.systemEnergy();if((p.mean+p.max)/2<0.05){if(h===null){h=new Date().valueOf()}if(new Date().valueOf()-h>1000){clearInterval(d);d=null}else{}}else{h=null}},fps:function(n){if(n!==undefined){var q=1000/Math.max(1,targetFps);j.physicsModified({timeout:q})}var r=0;for(var p=0,o=f.length;p0);if(x){$.extend(d.adjacency[C][D].data,y.data);return}else{d.edges[y._id]=y;d.adjacency[C][D].push(y);var w=(y.length!==undefined)?y.length:1;k.push({t:"addSpring",id:y._id,fm:C,to:D,l:w});h._notify()}return y},pruneEdge:function(B){k.push({t:"dropSpring",id:B._id});delete d.edges[B._id];for(var w in d.adjacency){for(var C in d.adjacency[w]){var z=d.adjacency[w][C];for(var A=z.length-1;A>=0;A--){if(d.adjacency[w][C][A]._id===B._id){d.adjacency[w][C].splice(A,1)}}}}h._notify()},getEdges:function(x,w){x=h.getNode(x);w=h.getNode(w);if(!x||!w){return[]}if(typeof(d.adjacency[x._id])!=="undefined"&&typeof(d.adjacency[x._id][w._id])!=="undefined"){return d.adjacency[x._id][w._id]}return[]},getEdgesFrom:function(w){w=h.getNode(w);if(!w){return[]}if(typeof(d.adjacency[w._id])!=="undefined"){var x=[];$.each(d.adjacency[w._id],function(z,y){x=x.concat(y)});return x}return[]},getEdgesTo:function(w){w=h.getNode(w);if(!w){return[]}var x=[];$.each(d.edges,function(z,y){if(y.target==w){x.push(y)}});return x},eachEdge:function(w){$.each(d.edges,function(A,y){var z=d.nodes[y.source._id]._p;var x=d.nodes[y.target._id]._p;if(z.x==null||x.x==null){return}z=(v!==null)?h.toScreen(z):z;x=(v!==null)?h.toScreen(x):x;if(z&&x){w.call(h,y,z,x)}})},prune:function(x){var w={dropped:{nodes:[],edges:[]}};if(x===undefined){$.each(d.nodes,function(z,y){w.dropped.nodes.push(y);h.pruneNode(y)})}else{h.eachNode(function(z){var y=x.call(h,z,{from:h.getEdgesFrom(z),to:h.getEdgesTo(z)});if(y){w.dropped.nodes.push(z);h.pruneNode(z)}})}return w},graft:function(x){var w={added:{nodes:[],edges:[]}};if(x.nodes){$.each(x.nodes,function(z,y){var A=h.getNode(z);if(A){A.data=y}else{w.added.nodes.push(h.addNode(z,y))}d.kernel.start()})}if(x.edges){$.each(x.edges,function(A,y){var z=h.getNode(A);if(!z){w.added.nodes.push(h.addNode(A,{}))}$.each(y,function(E,B){var D=h.getNode(E);if(!D){w.added.nodes.push(h.addNode(E,{}))}var C=h.getEdges(A,E);if(C.length>0){C[0].data=B}else{w.added.edges.push(h.addEdge(A,E,B))}})})}return w},merge:function(x){var w={added:{nodes:[],edges:[]},dropped:{nodes:[],edges:[]}};$.each(d.edges,function(B,A){if((x.edges[A.source.name]===undefined||x.edges[A.source.name][A.target.name]===undefined)){h.pruneEdge(A);w.dropped.edges.push(A)}});var z=h.prune(function(B,A){if(x.nodes[B.name]===undefined){w.dropped.nodes.push(B);return true}});var y=h.graft(x);w.added.nodes=w.added.nodes.concat(y.added.nodes);w.added.edges=w.added.edges.concat(y.added.edges);w.dropped.nodes=w.dropped.nodes.concat(z.dropped.nodes);w.dropped.edges=w.dropped.edges.concat(z.dropped.edges);return w},tweenNode:function(z,w,y){var x=h.getNode(z);if(x){d.tween.to(x,w,y)}},tweenEdge:function(x,w,A,z){if(z===undefined){h._tweenEdge(x,w,A)}else{var y=h.getEdges(x,w);$.each(y,function(B,C){h._tweenEdge(C,A,z)})}},_tweenEdge:function(x,w,y){if(x&&x._id!==undefined){d.tween.to(x,w,y)}},_updateGeometry:function(z){if(z!=undefined){var w=(z.epoch1||B.y*v.height>1){o=_newBounds;return true}else{return false}},energy:function(){return b},bounds:function(){var x=null;var w=null;$.each(d.nodes,function(A,z){if(!x){x=new Point(z._p);w=new Point(z._p);return}var y=z._p;if(y.x===null||y.y===null){return}if(y.x>x.x){x.x=y.x}if(y.y>x.y){x.y=y.y}if(y.x0){d.kernel.graphChanged(k);k=[];i=null}},};d.kernel=Kernel(h);d.tween=d.kernel.tween||null;Node.prototype.__defineGetter__("p",function(){var x=this;var w={};w.__defineGetter__("x",function(){return x._p.x});w.__defineSetter__("x",function(y){d.kernel.particleModified(x._id,{x:y})});w.__defineGetter__("y",function(){return x._p.y});w.__defineSetter__("y",function(y){d.kernel.particleModified(x._id,{y:y})});w.__proto__=Point.prototype;return w});Node.prototype.__defineSetter__("p",function(w){this._p.x=w.x;this._p.y=w.y;d.kernel.particleModified(this._id,{x:w.x,y:w.y})});Node.prototype.__defineGetter__("mass",function(){return this._mass});Node.prototype.__defineSetter__("mass",function(w){this._mass=w;d.kernel.particleModified(this._id,{m:w})});Node.prototype.__defineSetter__("tempMass",function(w){d.kernel.particleModified(this._id,{_m:w})});Node.prototype.__defineGetter__("fixed",function(){return this._fixed});Node.prototype.__defineSetter__("fixed",function(w){this._fixed=w;d.kernel.particleModified(this._id,{f:w?1:0})});return h};
36 | /* barnes-hut.js */ var BarnesHutTree=function(){var b=[];var a=0;var e=null;var d=0.5;var c={init:function(g,h,f){d=f;a=0;e=c._newBranch();e.origin=g;e.size=h.subtract(g)},insert:function(j){var f=e;var g=[j];while(g.length){var h=g.shift();var m=h._m||h.m;var p=c._whichQuad(h,f);if(f[p]===undefined){f[p]=h;f.mass+=m;if(f.p){f.p=f.p.add(h.p.multiply(m))}else{f.p=h.p.multiply(m)}}else{if("origin" in f[p]){f.mass+=(m);if(f.p){f.p=f.p.add(h.p.multiply(m))}else{f.p=h.p.multiply(m)}f=f[p];g.unshift(h)}else{var l=f.size.divide(2);var n=new Point(f.origin);if(p[0]=="s"){n.y+=l.y}if(p[1]=="e"){n.x+=l.x}var o=f[p];f[p]=c._newBranch();f[p].origin=n;f[p].size=l;f.mass=m;f.p=h.p.multiply(m);f=f[p];if(o.p.x===h.p.x&&o.p.y===h.p.y){var k=l.x*0.08;var i=l.y*0.08;o.p.x=Math.min(n.x+l.x,Math.max(n.x,o.p.x-k/2+Math.random()*k));o.p.y=Math.min(n.y+l.y,Math.max(n.y,o.p.y-i/2+Math.random()*i))}g.push(o);g.unshift(h)}}}},applyForces:function(m,g){var f=[e];while(f.length){node=f.shift();if(node===undefined){continue}if(m===node){continue}if("f" in node){var k=m.p.subtract(node.p);var l=Math.max(1,k.magnitude());var i=((k.magnitude()>0)?k:Point.random(1)).normalize();m.applyForce(i.multiply(g*(node._m||node.m)).divide(l*l))}else{var j=m.p.subtract(node.p.divide(node.mass)).magnitude();var h=Math.sqrt(node.size.x*node.size.y);if(h/j>d){f.push(node.ne);f.push(node.nw);f.push(node.se);f.push(node.sw)}else{var k=m.p.subtract(node.p.divide(node.mass));var l=Math.max(1,k.magnitude());var i=((k.magnitude()>0)?k:Point.random(1)).normalize();m.applyForce(i.multiply(g*(node.mass)).divide(l*l))}}}},_whichQuad:function(i,f){if(i.p.exploded()){return null}var h=i.p.subtract(f.origin);var g=f.size.divide(2);if(h.y=0?o:"verlet",stiffness:(m!==undefined)?m:1000,repulsion:(n!==undefined)?n:600,friction:(e!==undefined)?e:0.3,gravity:false,dt:(a!==undefined)?a:0.02,theta:0.4,init:function(){return i},modifyPhysics:function(q){$.each(["stiffness","repulsion","friction","gravity","dt","precision","integrator"],function(s,t){if(q[t]!==undefined){if(t=="precision"){i.theta=1-q[t];return}i[t]=q[t];if(t=="stiffness"){var r=q[t];$.each(c.springs,function(v,u){u.k=r})}}})},addNode:function(v){var u=v.id;var r=v.m;var q=g.bottomright.x-g.topleft.x;var t=g.bottomright.y-g.topleft.y;var s=new Point((v.x!=null)?v.x:g.topleft.x+q*Math.random(),(v.y!=null)?v.y:g.topleft.y+t*Math.random());c.particles[u]=new Particle(s,r);c.particles[u].connections=0;c.particles[u].fixed=(v.f===1);l.particles[u]=c.particles[u];p.push(c.particles[u])},dropNode:function(t){var s=t.id;var r=c.particles[s];var q=$.inArray(r,p);if(q>-1){p.splice(q,1)}delete c.particles[s];delete l.particles[s]},modifyNode:function(s,q){if(s in c.particles){var r=c.particles[s];if("x" in q){r.p.x=q.x}if("y" in q){r.p.y=q.y}if("m" in q){r.m=q.m}if("f" in q){r.fixed=(q.f===1)}if("_m" in q){if(r._m===undefined){r._m=r.m}r.m=q._m}}},addSpring:function(u){var t=u.id;var q=u.l;var s=c.particles[u.fm];var r=c.particles[u.to];if(s!==undefined&&r!==undefined){c.springs[t]=new Spring(s,r,q,i.stiffness);k.push(c.springs[t]);s.connections++;r.connections++;delete l.particles[u.fm];delete l.particles[u.to]}},dropSpring:function(t){var s=t.id;var r=c.springs[s];r.point1.connections--;r.point2.connections--;var q=$.inArray(r,k);if(q>-1){k.splice(q,1)}delete c.springs[s]},_update:function(q){d++;$.each(q,function(r,s){if(s.t in i){i[s.t](s)}});return d},tick:function(){i.tendParticles();if(i.integrator=="euler"){i.updateForces();i.updateVelocity(i.dt);i.updatePosition(i.dt)}else{i.updateForces();i.cacheForces();i.updatePosition(i.dt);i.updateForces();i.updateVelocity(i.dt)}i.tock()},tock:function(){var q=[];$.each(c.particles,function(s,r){q.push(s);q.push(r.p.x);q.push(r.p.y)});if(h){h({geometry:q,epoch:d,energy:b,bounds:g})}},tendParticles:function(){$.each(c.particles,function(r,q){if(q._m!==undefined){if(Math.abs(q.m-q._m)<1){q.m=q._m;delete q._m}else{q.m*=0.98}}q.v.x=q.v.y=0})},updateForces:function(){if(i.repulsion>0){if(i.theta>0){i.applyBarnesHutRepulsion()}else{i.applyBruteForceRepulsion()}}if(i.stiffness>0){i.applySprings()}i.applyCenterDrift();if(i.gravity){i.applyCenterGravity()}},cacheForces:function(){$.each(c.particles,function(r,q){q._F=q.f})},applyBruteForceRepulsion:function(){$.each(c.particles,function(r,q){$.each(c.particles,function(t,s){if(q!==s){var v=q.p.subtract(s.p);var w=Math.max(1,v.magnitude());var u=((v.magnitude()>0)?v:Point.random(1)).normalize();q.applyForce(u.multiply(i.repulsion*(s._m||s.m)*0.5).divide(w*w*0.5));s.applyForce(u.multiply(i.repulsion*(q._m||q.m)*0.5).divide(w*w*-0.5))}})})},applyBarnesHutRepulsion:function(){if(!g.topleft||!g.bottomright){return}var r=new Point(g.bottomright);var q=new Point(g.topleft);f.init(q,r,i.theta);$.each(c.particles,function(t,s){f.insert(s)});$.each(c.particles,function(t,s){f.applyForces(s,i.repulsion)})},applySprings:function(){$.each(c.springs,function(u,q){var t=q.point2.p.subtract(q.point1.p);var r=q.length-t.magnitude();var s=((t.magnitude()>0)?t:Point.random(1)).normalize();q.point1.applyForce(s.multiply(q.k*r*-0.5));q.point2.applyForce(s.multiply(q.k*r*0.5))})},applyCenterDrift:function(){var r=0;var s=new Point(0,0);$.each(c.particles,function(u,t){s.add(t.p);r++});if(r==0){return}var q=s.divide(-r);$.each(c.particles,function(u,t){t.applyForce(q)})},applyCenterGravity:function(){$.each(c.particles,function(s,q){var r=q.p.multiply(-1);q.applyForce(r.multiply(i.repulsion/100))})},updateVelocity:function(r){var s=0,q=0,t=0;$.each(c.particles,function(x,u){if(u.fixed){u.v=new Point(0,0);u.f=new Point(0,0);return}if(i.integrator=="euler"){u.v=u.v.add(u.f.multiply(r)).multiply(1-i.friction)}else{u.v=u.v.add(u.f.add(u._F.divide(u._m)).multiply(r*0.5)).multiply(1-i.friction)}u.f.x=u.f.y=0;var v=u.v.magnitude();if(v>j){u.v=u.v.divide(v*v)}var v=u.v.magnitude();var w=v*v;s+=w;q=Math.max(w,q);t++});b={sum:s,max:q,mean:s/t,n:t}},updatePosition:function(q){var s=null;var r=null;$.each(c.particles,function(v,u){if(i.integrator=="euler"){u.p=u.p.add(u.v.multiply(q))}else{var t=u.f.multiply(0.5*q*q).divide(u.m);u.p=u.p.add(u.v.multiply(q)).add(t)}if(!s){s=new Point(u.p.x,u.p.y);r=new Point(u.p.x,u.p.y);return}var w=u.p;if(w.x===null||w.y===null){return}if(w.x>s.x){s.x=w.x}if(w.y>s.y){s.y=w.y}if(w.x1000){e.stop()}else{}}else{c=null}},tock:function(h){h.type="geometry";postMessage(h)},modifyNode:function(i,h){a.modifyNode(i,h);e.go()},modifyPhysics:function(h){a.modifyPhysics(h)},update:function(h){var i=a._update(h)}};return e};var physics=PhysicsWorker();onmessage=function(a){if(!a.data.type){postMessage("¿kérnèl?");return}if(a.data.type=="physics"){var b=a.data.physics;physics.init(a.data.physics);return}switch(a.data.type){case"modify":physics.modifyNode(a.data.id,a.data.mods);break;case"changes":physics.update(a.data.changes);physics.go();break;case"start":physics.go();break;case"stop":physics.stop();break;case"sys":var b=a.data.param||{};if(!isNaN(b.timeout)){physics.timeout(b.timeout)}physics.modifyPhysics(b);physics.go();break}};
43 | })()
44 |
45 |
46 | arbor = (typeof(arbor)!=='undefined') ? arbor : {}
47 | $.extend(arbor, {
48 | // object constructors (don't use ‘new’, just call them)
49 | ParticleSystem:ParticleSystem,
50 | Point:function(x, y){ return new Point(x, y) },
51 |
52 | // immutable object with useful methods
53 | etc:{
54 | trace:trace, // ƒ(msg) -> safe console logging
55 | dirname:dirname, // ƒ(path) -> leading part of path
56 | basename:basename, // ƒ(path) -> trailing part of path
57 | ordinalize:ordinalize, // ƒ(num) -> abbrev integers (and add commas)
58 | objcopy:objcopy, // ƒ(old) -> clone an object
59 | objcmp:objcmp, // ƒ(a, b, strict_ordering) -> t/f comparison
60 | objkeys:objkeys, // ƒ(obj) -> array of all keys in obj
61 | objmerge:objmerge, // ƒ(dst, src) -> like $.extend but non-destructive
62 | uniq:uniq, // ƒ(arr) -> array of unique items in arr
63 | arbor_path:arbor_path, // ƒ() -> guess the directory of the lib code
64 | }
65 | })
66 |
67 | })(this.jQuery)
--------------------------------------------------------------------------------
/resources/private/js/gl-matrix-min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview gl-matrix - High performance matrix and vector operations
3 | * @author Brandon Jones
4 | * @author Colin MacKenzie IV
5 | * @version 2.2.1
6 | */
7 | /* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved.
8 |
9 | Redistribution and use in source and binary forms, with or without modification,
10 | are permitted provided that the following conditions are met:
11 |
12 | * Redistributions of source code must retain the above copyright notice, this
13 | list of conditions and the following disclaimer.
14 | * Redistributions in binary form must reproduce the above copyright notice,
15 | this list of conditions and the following disclaimer in the documentation
16 | and/or other materials provided with the distribution.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
28 | (function(e){"use strict";var t={};typeof exports=="undefined"?typeof define=="function"&&typeof define.amd=="object"&&define.amd?(t.exports={},define(function(){return t.exports})):t.exports=typeof window!="undefined"?window:e:t.exports=exports,function(e){if(!t)var t=1e-6;if(!n)var n=typeof Float32Array!="undefined"?Float32Array:Array;if(!r)var r=Math.random;var i={};i.setMatrixArrayType=function(e){n=e},typeof e!="undefined"&&(e.glMatrix=i);var s=Math.PI/180;i.toRadian=function(e){return e*s};var o={};o.create=function(){var e=new n(2);return e[0]=0,e[1]=0,e},o.clone=function(e){var t=new n(2);return t[0]=e[0],t[1]=e[1],t},o.fromValues=function(e,t){var r=new n(2);return r[0]=e,r[1]=t,r},o.copy=function(e,t){return e[0]=t[0],e[1]=t[1],e},o.set=function(e,t,n){return e[0]=t,e[1]=n,e},o.add=function(e,t,n){return e[0]=t[0]+n[0],e[1]=t[1]+n[1],e},o.subtract=function(e,t,n){return e[0]=t[0]-n[0],e[1]=t[1]-n[1],e},o.sub=o.subtract,o.multiply=function(e,t,n){return e[0]=t[0]*n[0],e[1]=t[1]*n[1],e},o.mul=o.multiply,o.divide=function(e,t,n){return e[0]=t[0]/n[0],e[1]=t[1]/n[1],e},o.div=o.divide,o.min=function(e,t,n){return e[0]=Math.min(t[0],n[0]),e[1]=Math.min(t[1],n[1]),e},o.max=function(e,t,n){return e[0]=Math.max(t[0],n[0]),e[1]=Math.max(t[1],n[1]),e},o.scale=function(e,t,n){return e[0]=t[0]*n,e[1]=t[1]*n,e},o.scaleAndAdd=function(e,t,n,r){return e[0]=t[0]+n[0]*r,e[1]=t[1]+n[1]*r,e},o.distance=function(e,t){var n=t[0]-e[0],r=t[1]-e[1];return Math.sqrt(n*n+r*r)},o.dist=o.distance,o.squaredDistance=function(e,t){var n=t[0]-e[0],r=t[1]-e[1];return n*n+r*r},o.sqrDist=o.squaredDistance,o.length=function(e){var t=e[0],n=e[1];return Math.sqrt(t*t+n*n)},o.len=o.length,o.squaredLength=function(e){var t=e[0],n=e[1];return t*t+n*n},o.sqrLen=o.squaredLength,o.negate=function(e,t){return e[0]=-t[0],e[1]=-t[1],e},o.normalize=function(e,t){var n=t[0],r=t[1],i=n*n+r*r;return i>0&&(i=1/Math.sqrt(i),e[0]=t[0]*i,e[1]=t[1]*i),e},o.dot=function(e,t){return e[0]*t[0]+e[1]*t[1]},o.cross=function(e,t,n){var r=t[0]*n[1]-t[1]*n[0];return e[0]=e[1]=0,e[2]=r,e},o.lerp=function(e,t,n,r){var i=t[0],s=t[1];return e[0]=i+r*(n[0]-i),e[1]=s+r*(n[1]-s),e},o.random=function(e,t){t=t||1;var n=r()*2*Math.PI;return e[0]=Math.cos(n)*t,e[1]=Math.sin(n)*t,e},o.transformMat2=function(e,t,n){var r=t[0],i=t[1];return e[0]=n[0]*r+n[2]*i,e[1]=n[1]*r+n[3]*i,e},o.transformMat2d=function(e,t,n){var r=t[0],i=t[1];return e[0]=n[0]*r+n[2]*i+n[4],e[1]=n[1]*r+n[3]*i+n[5],e},o.transformMat3=function(e,t,n){var r=t[0],i=t[1];return e[0]=n[0]*r+n[3]*i+n[6],e[1]=n[1]*r+n[4]*i+n[7],e},o.transformMat4=function(e,t,n){var r=t[0],i=t[1];return e[0]=n[0]*r+n[4]*i+n[12],e[1]=n[1]*r+n[5]*i+n[13],e},o.forEach=function(){var e=o.create();return function(t,n,r,i,s,o){var u,a;n||(n=2),r||(r=0),i?a=Math.min(i*n+r,t.length):a=t.length;for(u=r;u0&&(s=1/Math.sqrt(s),e[0]=t[0]*s,e[1]=t[1]*s,e[2]=t[2]*s),e},u.dot=function(e,t){return e[0]*t[0]+e[1]*t[1]+e[2]*t[2]},u.cross=function(e,t,n){var r=t[0],i=t[1],s=t[2],o=n[0],u=n[1],a=n[2];return e[0]=i*a-s*u,e[1]=s*o-r*a,e[2]=r*u-i*o,e},u.lerp=function(e,t,n,r){var i=t[0],s=t[1],o=t[2];return e[0]=i+r*(n[0]-i),e[1]=s+r*(n[1]-s),e[2]=o+r*(n[2]-o),e},u.random=function(e,t){t=t||1;var n=r()*2*Math.PI,i=r()*2-1,s=Math.sqrt(1-i*i)*t;return e[0]=Math.cos(n)*s,e[1]=Math.sin(n)*s,e[2]=i*t,e},u.transformMat4=function(e,t,n){var r=t[0],i=t[1],s=t[2];return e[0]=n[0]*r+n[4]*i+n[8]*s+n[12],e[1]=n[1]*r+n[5]*i+n[9]*s+n[13],e[2]=n[2]*r+n[6]*i+n[10]*s+n[14],e},u.transformMat3=function(e,t,n){var r=t[0],i=t[1],s=t[2];return e[0]=r*n[0]+i*n[3]+s*n[6],e[1]=r*n[1]+i*n[4]+s*n[7],e[2]=r*n[2]+i*n[5]+s*n[8],e},u.transformQuat=function(e,t,n){var r=t[0],i=t[1],s=t[2],o=n[0],u=n[1],a=n[2],f=n[3],l=f*r+u*s-a*i,c=f*i+a*r-o*s,h=f*s+o*i-u*r,p=-o*r-u*i-a*s;return e[0]=l*f+p*-o+c*-a-h*-u,e[1]=c*f+p*-u+h*-o-l*-a,e[2]=h*f+p*-a+l*-u-c*-o,e},u.rotateX=function(e,t,n,r){var i=[],s=[];return i[0]=t[0]-n[0],i[1]=t[1]-n[1],i[2]=t[2]-n[2],s[0]=i[0],s[1]=i[1]*Math.cos(r)-i[2]*Math.sin(r),s[2]=i[1]*Math.sin(r)+i[2]*Math.cos(r),e[0]=s[0]+n[0],e[1]=s[1]+n[1],e[2]=s[2]+n[2],e},u.rotateY=function(e,t,n,r){var i=[],s=[];return i[0]=t[0]-n[0],i[1]=t[1]-n[1],i[2]=t[2]-n[2],s[0]=i[2]*Math.sin(r)+i[0]*Math.cos(r),s[1]=i[1],s[2]=i[2]*Math.cos(r)-i[0]*Math.sin(r),e[0]=s[0]+n[0],e[1]=s[1]+n[1],e[2]=s[2]+n[2],e},u.rotateZ=function(e,t,n,r){var i=[],s=[];return i[0]=t[0]-n[0],i[1]=t[1]-n[1],i[2]=t[2]-n[2],s[0]=i[0]*Math.cos(r)-i[1]*Math.sin(r),s[1]=i[0]*Math.sin(r)+i[1]*Math.cos(r),s[2]=i[2],e[0]=s[0]+n[0],e[1]=s[1]+n[1],e[2]=s[2]+n[2],e},u.forEach=function(){var e=u.create();return function(t,n,r,i,s,o){var u,a;n||(n=3),r||(r=0),i?a=Math.min(i*n+r,t.length):a=t.length;for(u=r;u 0&&(o=1/Math.sqrt(o),e[0]=t[0]*o,e[1]=t[1]*o,e[2]=t[2]*o,e[3]=t[3]*o),e},a.dot=function(e,t){return e[0]*t[0]+e[1]*t[1]+e[2]*t[2]+e[3]*t[3]},a.lerp=function(e,t,n,r){var i=t[0],s=t[1],o=t[2],u=t[3];return e[0]=i+r*(n[0]-i),e[1]=s+r*(n[1]-s),e[2]=o+r*(n[2]-o),e[3]=u+r*(n[3]-u),e},a.random=function(e,t){return t=t||1,e[0]=r(),e[1]=r(),e[2]=r(),e[3]=r(),a.normalize(e,e),a.scale(e,e,t),e},a.transformMat4=function(e,t,n){var r=t[0],i=t[1],s=t[2],o=t[3];return e[0]=n[0]*r+n[4]*i+n[8]*s+n[12]*o,e[1]=n[1]*r+n[5]*i+n[9]*s+n[13]*o,e[2]=n[2]*r+n[6]*i+n[10]*s+n[14]*o,e[3]=n[3]*r+n[7]*i+n[11]*s+n[15]*o,e},a.transformQuat=function(e,t,n){var r=t[0],i=t[1],s=t[2],o=n[0],u=n[1],a=n[2],f=n[3],l=f*r+u*s-a*i,c=f*i+a*r-o*s,h=f*s+o*i-u*r,p=-o*r-u*i-a*s;return e[0]=l*f+p*-o+c*-a-h*-u,e[1]=c*f+p*-u+h*-o-l*-a,e[2]=h*f+p*-a+l*-u-c*-o,e},a.forEach=function(){var e=a.create();return function(t,n,r,i,s,o){var u,a;n||(n=4),r||(r=0),i?a=Math.min(i*n+r,t.length):a=t.length;for(u=r;u .999999?(r[0]=0,r[1]=0,r[2]=0,r[3]=1,r):(u.cross(e,i,s),r[0]=e[0],r[1]=e[1],r[2]=e[2],r[3]=1+o,p.normalize(r,r))}}(),p.setAxes=function(){var e=c.create();return function(t,n,r,i){return e[0]=r[0],e[3]=r[1],e[6]=r[2],e[1]=i[0],e[4]=i[1],e[7]=i[2],e[2]=-n[0],e[5]=-n[1],e[8]=-n[2],p.normalize(t,p.fromMat3(t,e))}}(),p.clone=a.clone,p.fromValues=a.fromValues,p.copy=a.copy,p.set=a.set,p.identity=function(e){return e[0]=0,e[1]=0,e[2]=0,e[3]=1,e},p.setAxisAngle=function(e,t,n){n*=.5;var r=Math.sin(n);return e[0]=r*t[0],e[1]=r*t[1],e[2]=r*t[2],e[3]=Math.cos(n),e},p.add=a.add,p.multiply=function(e,t,n){var r=t[0],i=t[1],s=t[2],o=t[3],u=n[0],a=n[1],f=n[2],l=n[3];return e[0]=r*l+o*u+i*f-s*a,e[1]=i*l+o*a+s*u-r*f,e[2]=s*l+o*f+r*a-i*u,e[3]=o*l-r*u-i*a-s*f,e},p.mul=p.multiply,p.scale=a.scale,p.rotateX=function(e,t,n){n*=.5;var r=t[0],i=t[1],s=t[2],o=t[3],u=Math.sin(n),a=Math.cos(n);return e[0]=r*a+o*u,e[1]=i*a+s*u,e[2]=s*a-i*u,e[3]=o*a-r*u,e},p.rotateY=function(e,t,n){n*=.5;var r=t[0],i=t[1],s=t[2],o=t[3],u=Math.sin(n),a=Math.cos(n);return e[0]=r*a-s*u,e[1]=i*a+o*u,e[2]=s*a+r*u,e[3]=o*a-i*u,e},p.rotateZ=function(e,t,n){n*=.5;var r=t[0],i=t[1],s=t[2],o=t[3],u=Math.sin(n),a=Math.cos(n);return e[0]=r*a+i*u,e[1]=i*a-r*u,e[2]=s*a+o*u,e[3]=o*a-s*u,e},p.calculateW=function(e,t){var n=t[0],r=t[1],i=t[2];return e[0]=n,e[1]=r,e[2]=i,e[3]=-Math.sqrt(Math.abs(1-n*n-r*r-i*i)),e},p.dot=a.dot,p.lerp=a.lerp,p.slerp=function(e,t,n,r){var i=t[0],s=t[1],o=t[2],u=t[3],a=n[0],f=n[1],l=n[2],c=n[3],h,p,d,v,m;return p=i*a+s*f+o*l+u*c,p<0&&(p=-p,a=-a,f=-f,l=-l,c=-c),1-p>1e-6?(h=Math.acos(p),d=Math.sin(h),v=Math.sin((1-r)*h)/d,m=Math.sin(r*h)/d):(v=1-r,m=r),e[0]=v*i+m*a,e[1]=v*s+m*f,e[2]=v*o+m*l,e[3]=v*u+m*c,e},p.invert=function(e,t){var n=t[0],r=t[1],i=t[2],s=t[3],o=n*n+r*r+i*i+s*s,u=o?1/o:0;return e[0]=-n*u,e[1]=-r*u,e[2]=-i*u,e[3]=s*u,e},p.conjugate=function(e,t){return e[0]=-t[0],e[1]=-t[1],e[2]=-t[2],e[3]=t[3],e},p.length=a.length,p.len=p.length,p.squaredLength=a.squaredLength,p.sqrLen=p.squaredLength,p.normalize=a.normalize,p.fromMat3=function(e,t){var n=t[0]+t[4]+t[8],r;if(n>0)r=Math.sqrt(n+1),e[3]=.5*r,r=.5/r,e[0]=(t[7]-t[5])*r,e[1]=(t[2]-t[6])*r,e[2]=(t[3]-t[1])*r;else{var i=0;t[4]>t[0]&&(i=1),t[8]>t[i*3+i]&&(i=2);var s=(i+1)%3,o=(i+2)%3;r=Math.sqrt(t[i*3+i]-t[s*3+s]-t[o*3+o]+1),e[i]=.5*r,r=.5/r,e[3]=(t[o*3+s]-t[s*3+o])*r,e[s]=(t[s*3+i]+t[i*3+s])*r,e[o]=(t[o*3+i]+t[i*3+o])*r}return e},p.str=function(e){return"quat("+e[0]+", "+e[1]+", "+e[2]+", "+e[3]+")"},typeof e!="undefined"&&(e.quat=p)}(t.exports)})(this);
29 |
--------------------------------------------------------------------------------
/resources/private/js/singult.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.6.1
2 | var array_p, dom_p, explode_p, ignore_p, key_prefix, map_p, namespace_tag, number_p, p, re_namespace_sep, re_svg_tags, re_tag, re_whitespace, string_p, unify_p, whitespace_node_p, xmlns,
3 | __hasProp = {}.hasOwnProperty;
4 |
5 | goog.require("goog.string");
6 |
7 | goog.provide("singult.coffee");
8 |
9 | goog.provide("singult.coffee.Unify");
10 |
11 | goog.provide("singult.coffee.Ignore");
12 |
13 | p = function(x) {
14 | console.log(x);
15 | return x;
16 | };
17 |
18 | re_tag = /([^\s\.#]+)(?:#([^\s\.#]+))?(?:\.([^\s#]+))?/;
19 |
20 | re_svg_tags = /^(svg|g|rect|circle|clipPath|path|line|polygon|polyline|text|textPath)$/;
21 |
22 | re_whitespace = /^\s+$/;
23 |
24 | re_namespace_sep = /:(.+)/;
25 |
26 | key_prefix = "\0";
27 |
28 | xmlns = {
29 | xhtml: "http://www.w3.org/1999/xhtml",
30 | xlink: "http://www.w3.org/1999/xlink",
31 | svg: "http://www.w3.org/2000/svg",
32 | xml: "http://www.w3.org/XML/1998/namespace",
33 | xmlns: "http://www.w3.org/2000/xmlns"
34 | };
35 |
36 | namespace_tag = function(tag_str) {
37 | var nsp, tag, _ref;
38 | _ref = tag_str.split(":"), nsp = _ref[0], tag = _ref[1];
39 | if (tag != null) {
40 | return [xmlns[nsp] || nsp, tag];
41 | } else {
42 | if (tag_str.match(re_svg_tags)) {
43 | return [xmlns.svg, tag_str];
44 | } else {
45 | return [xmlns.xhtml, tag_str];
46 | }
47 | }
48 | };
49 |
50 | explode_p = function(v) {
51 | return v[0] === ":*:";
52 | };
53 |
54 | unify_p = function(x) {
55 | return (x != null) && (x instanceof singult.coffee.Unify);
56 | };
57 |
58 | ignore_p = function(x) {
59 | return (x != null) && (x instanceof singult.coffee.Ignore);
60 | };
61 |
62 | array_p = function(x) {
63 | return (x != null) && (x.forEach != null);
64 | };
65 |
66 | map_p = function(x) {
67 | return (x != null) && (!array_p(x)) && (!unify_p(x)) && (!ignore_p(x)) && (x instanceof Object);
68 | };
69 |
70 | dom_p = function(x) {
71 | return (x != null) && (x.nodeType != null);
72 | };
73 |
74 | string_p = function(x) {
75 | return (x != null) && (x.substring != null);
76 | };
77 |
78 | number_p = function(x) {
79 | return (x != null) && (x.toFixed != null);
80 | };
81 |
82 | whitespace_node_p = function($n) {
83 | return $n.nodeType === 8 || ($n.nodeType === 3 && $n.textContent.match(re_whitespace));
84 | };
85 |
86 | singult.coffee.style = function($e, m) {
87 | var k, v, _results;
88 | _results = [];
89 | for (k in m) {
90 | if (!__hasProp.call(m, k)) continue;
91 | v = m[k];
92 | _results.push($e.style[goog.string.toCamelCase(k)] = v);
93 | }
94 | return _results;
95 | };
96 |
97 | singult.coffee.properties = function($e, m) {
98 | var prop, v, _results;
99 | _results = [];
100 | for (prop in m) {
101 | if (!__hasProp.call(m, prop)) continue;
102 | v = m[prop];
103 | _results.push($e[prop] = v);
104 | }
105 | return _results;
106 | };
107 |
108 | singult.coffee.attr = function($e, attr_map) {
109 | var attr, k, ns, v, _ref, _results;
110 | if (attr_map["style"] != null) {
111 | singult.coffee.style($e, attr_map["style"]);
112 | delete attr_map["style"];
113 | }
114 | if (attr_map["properties"] != null) {
115 | singult.coffee.properties($e, attr_map["properties"]);
116 | delete attr_map["properties"];
117 | }
118 | if (array_p(attr_map["class"])) {
119 | $e.setAttribute("class", attr_map["class"].join(" "));
120 | delete attr_map["class"];
121 | }
122 | _results = [];
123 | for (k in attr_map) {
124 | if (!__hasProp.call(attr_map, k)) continue;
125 | v = attr_map[k];
126 | if (v != null) {
127 | _ref = k.split(re_namespace_sep), ns = _ref[0], attr = _ref[1];
128 | if (attr != null) {
129 | _results.push($e.setAttributeNS(xmlns[ns] || ns, attr, v));
130 | } else {
131 | _results.push($e.setAttribute(k, v));
132 | }
133 | } else {
134 | _results.push($e.removeAttribute(k));
135 | }
136 | }
137 | return _results;
138 | };
139 |
140 | singult.coffee.node_data = function($e, d) {
141 | if (d != null) {
142 | return $e["__singult_data__"] = d;
143 | } else {
144 | return $e["__singult_data__"];
145 | }
146 | };
147 |
148 | singult.coffee.canonicalize = function(x) {
149 | if (number_p(x)) {
150 | return x.toString();
151 | } else if (array_p(x)) {
152 | return singult.coffee.canonicalize_hiccup(x);
153 | } else {
154 | return x;
155 | }
156 | };
157 |
158 | singult.coffee.canonicalize_hiccup = function(v) {
159 | var abbreviated_classes, attr, canonical, canonical_children, children, cls_str, id, nsp, tag, tag_str, _, _ref, _ref1, _ref2;
160 | tag = v[0];
161 | _ref = map_p(v[1]) ? [v[1], v.slice(2)] : [{}, v.slice(1)], attr = _ref[0], children = _ref[1];
162 | _ref1 = tag.match(re_tag), _ = _ref1[0], tag_str = _ref1[1], id = _ref1[2], cls_str = _ref1[3];
163 | if (id != null) {
164 | attr["id"] = id;
165 | }
166 | if (cls_str != null) {
167 | abbreviated_classes = cls_str.split(".");
168 | if (array_p(attr["class"])) {
169 | attr["class"] = attr["class"].concat(abbreviated_classes);
170 | } else if (string_p(attr["class"])) {
171 | attr["class"] = abbreviated_classes.concat([attr["class"]]);
172 | } else if (attr["class"] == null) {
173 | attr["class"] = abbreviated_classes;
174 | }
175 | }
176 | _ref2 = namespace_tag(tag_str), nsp = _ref2[0], tag = _ref2[1];
177 | canonical_children = [];
178 | children.forEach(function(v) {
179 | if (v != null) {
180 | if (explode_p(v)) {
181 | return v.slice(1).forEach(function(v) {
182 | return canonical_children.push(singult.coffee.canonicalize(v));
183 | });
184 | } else {
185 | return canonical_children.push(singult.coffee.canonicalize(v));
186 | }
187 | }
188 | });
189 | canonical = {
190 | nsp: nsp,
191 | tag: tag,
192 | attr: attr,
193 | children: canonical_children
194 | };
195 | return canonical;
196 | };
197 |
198 | singult.coffee.render = function(m) {
199 | var $e, c;
200 | if (unify_p(m)) {
201 | throw new Error("Unify must be the first and only child of its parent.");
202 | } else if (ignore_p(m)) {
203 | return null;
204 | } else if (string_p(m)) {
205 | return document.createTextNode(m);
206 | } else if (dom_p(m)) {
207 | return m;
208 | } else {
209 | $e = document.createElementNS(m.nsp, m.tag);
210 | singult.coffee.attr($e, m.attr);
211 | if (unify_p((c = m.children[0]))) {
212 | if (c.enter != null) {
213 | c.data.forEach(function(d) {
214 | var $el;
215 | $el = c.enter(d);
216 | singult.coffee.node_data($el, d);
217 | return $e.appendChild($el);
218 | });
219 | } else {
220 | c.data.forEach(function(d) {
221 | var $el;
222 | $el = singult.coffee.render(singult.coffee.canonicalize(c.mapping(d)));
223 | singult.coffee.node_data($el, d);
224 | return $e.appendChild($el);
225 | });
226 | }
227 | } else {
228 | m.children.forEach(function(c) {
229 | var $c;
230 | $c = singult.coffee.render(c);
231 | if ($c != null) {
232 | return $e.appendChild($c);
233 | }
234 | });
235 | }
236 | return $e;
237 | }
238 | };
239 |
240 | /**
241 | * @constructor
242 | */;
243 |
244 | singult.coffee.Unify = function(data, mapping, key_fn, enter, update, exit, force_update_p) {
245 | this.data = data;
246 | this.mapping = mapping;
247 | this.key_fn = key_fn;
248 | this.enter = enter;
249 | this.update = update;
250 | this.exit = exit;
251 | this.force_update_p = force_update_p;
252 | return this;
253 | };
254 |
255 | /**
256 | * @constructor
257 | */;
258 |
259 | singult.coffee.Ignore = function() {
260 | return this;
261 | };
262 |
263 | singult.coffee.unify_ = function($container, u) {
264 | var $nodes, data_len, data_map, data_to_key, enter, exit, i, insert_at, key, key_fn, maybe_do_update, node_to_key, nodes_to_keep, update;
265 | enter = u.enter || function(d) {
266 | var $el;
267 | $el = singult.coffee.render(singult.coffee.canonicalize(u.mapping(d)));
268 | $container.appendChild($el);
269 | return $el;
270 | };
271 | update = u.update || function($n, d) {
272 | return singult.coffee.merge($n, singult.coffee.canonicalize(u.mapping(d)));
273 | };
274 | exit = u.exit || function($n) {
275 | return $container.removeChild($n);
276 | };
277 | key_fn = u.key_fn || function(d, idx) {
278 | return idx;
279 | };
280 | $nodes = $container.childNodes;
281 | data_to_key = function(d, i) {
282 | return key_prefix + key_fn(d, i);
283 | };
284 | node_to_key = function($n, i) {
285 | return data_to_key(singult.coffee.node_data($n), i);
286 | };
287 | maybe_do_update = function($n, d) {
288 | var $el, identical_data_p, old_data;
289 | if (u.force_update_p) {
290 | $el = update($n, d);
291 | return singult.coffee.node_data($el, d);
292 | } else {
293 | old_data = singult.coffee.node_data($n);
294 | identical_data_p = old_data.cljs$core$IEquiv$_equiv$arity$2 != null ? old_data.cljs$core$IEquiv$_equiv$arity$2(old_data, d) : old_data === d;
295 | if (!identical_data_p) {
296 | $el = update($n, d);
297 | return singult.coffee.node_data($el, d);
298 | }
299 | }
300 | };
301 | insert_at = function($n, i) {
302 | if (i < $nodes.length) {
303 | return $container.insertBefore($n, $nodes[i]);
304 | } else {
305 | return $container.appendChild($n);
306 | }
307 | };
308 | data_map = {};
309 | u.data.forEach(function(d, i) {
310 | var key;
311 | key = data_to_key(d, i);
312 | return data_map[key] = d;
313 | });
314 | nodes_to_keep = {};
315 | i = 0;
316 | while (i < $nodes.length) {
317 | key = node_to_key($nodes[i], i);
318 | if (data_map[key]) {
319 | nodes_to_keep[key] = $nodes[i];
320 | }
321 | i += 1;
322 | }
323 | u.data.forEach(function(d, i) {
324 | var $el, $n, d_key, n_key;
325 | $n = i < $nodes.length ? $nodes[i] : void 0;
326 | n_key = $n ? node_to_key($n, i) : void 0;
327 | d_key = data_to_key(d, i);
328 | if ($n == null) {
329 | $el = enter(d);
330 | return singult.coffee.node_data($el, d);
331 | } else if (n_key === d_key) {
332 | return maybe_do_update($nodes[i], d);
333 | } else {
334 | if (!nodes_to_keep[n_key]) {
335 | exit($n);
336 | }
337 | if (nodes_to_keep[d_key]) {
338 | $el = nodes_to_keep[d_key];
339 | insert_at($el, i);
340 | return maybe_do_update($el, d);
341 | } else {
342 | $el = enter(d);
343 | insert_at($el, i);
344 | return singult.coffee.node_data($el, d);
345 | }
346 | }
347 | });
348 | data_len = u.data.length;
349 | while (data_len < $nodes.length) {
350 | exit($nodes[data_len]);
351 | }
352 | return null;
353 | };
354 |
355 | singult.coffee.merge = function($e, m) {
356 | var $c, c, i, _i, _j, _ref, _ref1;
357 | if (unify_p(m)) {
358 | singult.coffee.unify_($e, m);
359 | } else if (ignore_p(m)) {
360 |
361 | } else {
362 | if ($e.nodeName.toLowerCase() !== m.tag.toLowerCase()) {
363 | p($e);
364 | p(m);
365 | throw new Error("Cannot merge $e into node of different type");
366 | }
367 | singult.coffee.attr($e, m.attr);
368 | if ($e.hasChildNodes()) {
369 | for (i = _i = _ref = $e.childNodes.length - 1; _ref <= 0 ? _i <= 0 : _i >= 0; i = _ref <= 0 ? ++_i : --_i) {
370 | $c = $e.childNodes[i];
371 | if (whitespace_node_p($c)) {
372 | $e.removeChild($c);
373 | }
374 | }
375 | }
376 | if (unify_p(m.children[0])) {
377 | singult.coffee.merge($e, m.children[0]);
378 | } else {
379 | if ($e.childNodes.length > m.children.length) {
380 | for (i = _j = _ref1 = $e.childNodes.length - 1; _ref1 <= 0 ? _j <= 0 : _j >= 0; i = _ref1 <= 0 ? ++_j : --_j) {
381 | $e.removeChild($e.childNodes[i]);
382 | }
383 | }
384 | i = 0;
385 | while (i < m.children.length) {
386 | c = m.children[i] || "";
387 | $c = $e.childNodes[i];
388 | if (string_p(c)) {
389 | if ($c != null) {
390 | $c.textContent = c;
391 | } else {
392 | $e.appendChild(document.createTextNode(c));
393 | }
394 | } else if (ignore_p(c)) {
395 |
396 | } else if (map_p(c)) {
397 | if ($c != null) {
398 | singult.coffee.merge($c, c);
399 | } else {
400 | $e.appendChild(singult.coffee.render(c));
401 | }
402 | } else {
403 | p($c);
404 | p(c);
405 | throw new Error("Cannot merge children");
406 | }
407 | i += 1;
408 | }
409 | }
410 | }
411 | return $e;
412 | };
413 |
--------------------------------------------------------------------------------
/resources/private/js/webgl-utils.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2010, Google Inc.
3 | * All rights reserved.
4 | *
5 | * Redistribution and use in source and binary forms, with or without
6 | * modification, are permitted provided that the following conditions are
7 | * met:
8 | *
9 | * * Redistributions of source code must retain the above copyright
10 | * notice, this list of conditions and the following disclaimer.
11 | * * Redistributions in binary form must reproduce the above
12 | * copyright notice, this list of conditions and the following disclaimer
13 | * in the documentation and/or other materials provided with the
14 | * distribution.
15 | * * Neither the name of Google Inc. nor the names of its
16 | * contributors may be used to endorse or promote products derived from
17 | * this software without specific prior written permission.
18 | *
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 | */
31 |
32 |
33 | /**
34 | * @fileoverview This file contains functions every webgl program will need
35 | * a version of one way or another.
36 | *
37 | * Instead of setting up a context manually it is recommended to
38 | * use. This will check for success or failure. On failure it
39 | * will attempt to present an approriate message to the user.
40 | *
41 | * gl = WebGLUtils.setupWebGL(canvas);
42 | *
43 | * For animated WebGL apps use of setTimeout or setInterval are
44 | * discouraged. It is recommended you structure your rendering
45 | * loop like this.
46 | *
47 | * function render() {
48 | * window.requestAnimFrame(render, canvas);
49 | *
50 | * // do rendering
51 | * ...
52 | * }
53 | * render();
54 | *
55 | * This will call your rendering function up to the refresh rate
56 | * of your display but will stop rendering if your app is not
57 | * visible.
58 | */
59 |
60 | WebGLUtils = function() {
61 |
62 | /**
63 | * Creates the HTLM for a failure message
64 | * @param {string} canvasContainerId id of container of th
65 | * canvas.
66 | * @return {string} The html.
67 | */
68 | var makeFailHTML = function(msg) {
69 | return '' +
70 | ' ' +
71 | '' +
72 | '' +
73 | '
' + msg + '
' +
74 | '
' +
75 | '
';
76 | };
77 |
78 | /**
79 | * Mesasge for getting a webgl browser
80 | * @type {string}
81 | */
82 | var GET_A_WEBGL_BROWSER = '' +
83 | 'This page requires a browser that supports WebGL. ' +
84 | 'Click here to upgrade your browser. ';
85 |
86 | /**
87 | * Mesasge for need better hardware
88 | * @type {string}
89 | */
90 | var OTHER_PROBLEM = '' +
91 | "It doesn't appear your computer can support WebGL. " +
92 | 'Click here for more information. ';
93 |
94 | /**
95 | * Creates a webgl context. If creation fails it will
96 | * change the contents of the container of the
97 | * tag to an error message with the correct links for WebGL.
98 | * @param {Element} canvas. The canvas element to create a
99 | * context from.
100 | * @param {WebGLContextCreationAttirbutes} opt_attribs Any
101 | * creation attributes you want to pass in.
102 | * @param {function:(msg)} opt_onError An function to call
103 | * if there is an error during creation.
104 | * @return {WebGLRenderingContext} The created context.
105 | */
106 | var setupWebGL = function(canvas, opt_attribs, opt_onError) {
107 | function handleCreationError(msg) {
108 | var container = canvas.parentNode;
109 | if (container) {
110 | var str = window.WebGLRenderingContext ?
111 | OTHER_PROBLEM :
112 | GET_A_WEBGL_BROWSER;
113 | if (msg) {
114 | str += " Status: " + msg;
115 | }
116 | container.innerHTML = makeFailHTML(str);
117 | }
118 | };
119 |
120 | opt_onError = opt_onError || handleCreationError;
121 |
122 | if (canvas.addEventListener) {
123 | canvas.addEventListener("webglcontextcreationerror", function(event) {
124 | opt_onError(event.statusMessage);
125 | }, false);
126 | }
127 | var context = create3DContext(canvas, opt_attribs);
128 | if (!context) {
129 | if (!window.WebGLRenderingContext) {
130 | opt_onError("");
131 | }
132 | }
133 | return context;
134 | };
135 |
136 | /**
137 | * Creates a webgl context.
138 | * @param {!Canvas} canvas The canvas tag to get context
139 | * from. If one is not passed in one will be created.
140 | * @return {!WebGLContext} The created context.
141 | */
142 | var create3DContext = function(canvas, opt_attribs) {
143 | var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"];
144 | var context = null;
145 | for (var ii = 0; ii < names.length; ++ii) {
146 | try {
147 | context = canvas.getContext(names[ii], opt_attribs);
148 | } catch(e) {}
149 | if (context) {
150 | break;
151 | }
152 | }
153 | return context;
154 | }
155 |
156 | return {
157 | create3DContext: create3DContext,
158 | setupWebGL: setupWebGL
159 | };
160 | }();
161 |
162 | /**
163 | * Provides requestAnimationFrame in a cross browser way.
164 | */
165 | window.requestAnimFrame = (function() {
166 | return window.requestAnimationFrame ||
167 | window.webkitRequestAnimationFrame ||
168 | window.mozRequestAnimationFrame ||
169 | window.oRequestAnimationFrame ||
170 | window.msRequestAnimationFrame ||
171 | function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) {
172 | window.setTimeout(callback, 1000/60);
173 | };
174 | })();
175 |
176 |
--------------------------------------------------------------------------------
/resources/private/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
4 | Sitemap: http://programming-enchiladas.destructuring-bind.org/sitemap.xml
5 |
--------------------------------------------------------------------------------
/resources/public/css/default.css:
--------------------------------------------------------------------------------
1 | html {
2 | margin:0;
3 | padding:0;
4 | border:0;
5 | }
6 |
7 | body {
8 | overflow-y: scroll;
9 | }
10 |
11 | body, div, span, object, iframe,
12 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
13 | a, abbr, acronym, address, code,
14 | del, dfn, em, img, q, dl, dt, dd, ol, ul, li,
15 | fieldset, form, label, legend,
16 | table, caption, tbody, tfoot, thead, tr, th, td,
17 | article, aside, dialog, figure, footer, header,
18 | hgroup, nav, section {
19 | margin: 0;
20 | border: 0;
21 | font-weight: inherit;
22 | font-style: inherit;
23 | font-size: 100%;
24 | font-family: inherit;
25 | vertical-align: baseline;
26 | }
27 |
28 | article, aside, dialog, figure, footer, header,
29 | hgroup, nav, section {
30 | display:block;
31 | }
32 |
33 | html,body {
34 | line-height: 1.5;
35 | background: white;
36 | color: black;
37 | width: 100%;
38 | height: 100%;
39 | }
40 |
41 | table {
42 | border-collapse: separate;
43 | border-spacing: 0;
44 | }
45 |
46 | caption, th, td {
47 | text-align: left;
48 | font-weight: normal;
49 | float:none !important;
50 | }
51 | table, th, td {
52 | vertical-align: middle;
53 | }
54 |
55 | blockquote:before, blockquote:after, q:before, q:after { content: ''; }
56 | blockquote, q { quotes: "" ""; }
57 |
58 | a img { border: none; }
59 |
60 | /*div.wrapper {
61 | min-height: 98%;
62 | }*/
63 |
64 | div#info {
65 | position: absolute;
66 | display: block;
67 | color: yellow;
68 | font: bold 81.25% 'Helvetiva Neue', Helvetica, Arial, sans-serif;
69 | top: 0;
70 | right: 0;
71 | z-index: 10;
72 | background-color: rgba(0,0,0, 0.25);
73 | }
74 |
75 | .gist {
76 | font-size: 75% !important;
77 | width: 800px;
78 | }
79 |
80 | div#not-found {
81 | position: fixed;
82 | top: 40px;
83 | bottom: -60px;
84 | left: 0;
85 | right: 0;
86 | background-image: url("../images/404.jpg");
87 | background-size: cover;
88 | }
89 | div#not-found p {
90 | font-weight: bold;
91 | font-size: 36pt;
92 | font-family: sans-serif;
93 | color: #fff;
94 | text-shadow: 1px 0 0 #555, 0 -1px 0 #555, 0 1px 0 #555, -1px 0 0 #555;
95 | margin: 10px;
96 | }
97 | div#not-found p.err404 {
98 | font-size: 250pt;
99 | text-align: center;
100 | vertical-align: middle;
101 | }
102 |
103 | section.container {
104 | margin: 0 auto;
105 | width: 960px;
106 | }
107 |
108 | div.meta {
109 | font-family: Helvetica,arial,freesans,clean,sans-serif;
110 | font-size: 20px;
111 | font-weight: normal;
112 | letter-spacing: -1px;
113 | line-height: 28px;
114 | text-shadow: 1px 1px 0 #fff;
115 | color: #495961;
116 | }
117 |
118 | div.meta {
119 | color: #666666;
120 | }
121 |
122 | div.meta div.author img {
123 | border-radius: 3px;
124 | margin-right: 2px;
125 | position: relative;
126 | top: -1px;
127 | vertical-align: middle;
128 | }
129 |
130 | div.meta a {
131 | color: #4183c4;
132 | white-space: nowrap;
133 | text-decoration: none;
134 | }
135 |
136 | div.author span {
137 | margin-left: 5px;
138 | }
139 |
140 | .gist-timestamp {
141 | color: #999999;
142 | font-size: 11px;
143 | line-height: 20px;
144 | letter-spacing: 0;
145 | display: block;
146 | margin-top: -5px;
147 | margin-left: 29px;
148 | }
149 |
150 | .stats {
151 | float: right;
152 | }
153 | .stats > span {
154 | vertical-align: top;
155 | }
156 |
157 | .gist-header {
158 | border-bottom: 1px solid #e5e5e5;
159 | position: relative;
160 | display: block;
161 | margin-bottom: 10px;
162 | padding: 20px 0 0 0;
163 | }
164 |
165 | .stats,
166 | .gist-description {
167 | color: #666666;
168 | font-family: Helvetica,arial,freesans,clean,sans-serif;
169 | font-size: 14px;
170 | line-height: 1.4;
171 | word-wrap: break-word;
172 | }
173 |
174 | .stars>a {
175 | color: #666666!important;
176 | vertical-align: top;
177 | }
178 |
179 | .meta h1 {
180 | margin: 0 0 10px 0;
181 | font-size: 20px;
182 | font-weight: normal;
183 | letter-spacing: -1px;
184 | line-height: 28px;
185 | text-shadow: 1px 1px 0 #fff;
186 | color: #495961;
187 | z-index: 0;
188 | }
189 |
190 | div#error {
191 | display: none;
192 | position: relative;
193 | left: auto;
194 | right: auto;
195 | padding: 1em;
196 | background: #FECBCB;
197 | -webkit-border-radius: 0.75em;
198 | -moz-border-radius: 0.75em;
199 | border-radius: 0.75em;
200 | margin: 0.5em;
201 | }
202 |
203 | div#stacktrace {
204 | height: 400px;
205 | overflow-y: auto;
206 | }
207 |
208 | div#error h1 {
209 | font-family: Helvetica,arial,freesans,clean,sans-serif;
210 | font-size: 20px;
211 | font-weight: bold;
212 | color: black;
213 | background: url("../images/error.png") no-repeat;
214 | background-size: 24px;
215 | padding-left: 24px;
216 | }
217 |
218 | @media all and (-webkit-min-device-pixel-ratio: 2) {
219 | div#error h1 {background: url("../images/error@2x.png") no-repeat;}
220 | }
221 |
222 | div#error h3.info {
223 | font-family: Helvetica,arial,freesans,clean,sans-serif;
224 | font-size: 16px;
225 | font-weight: bold;
226 | color: #666666;
227 | }
228 |
229 | div#error td.method, div#error td.source {
230 | font-family: monospace;
231 | font-size: 12px;
232 | padding-left: 20px;
233 | width: 250px;
234 | color: #666666;
235 | }
236 |
237 | div#error tr:hover {
238 | background-color: rgba(255,0,0,0.25);
239 | cursor: pointer;
240 | }
241 |
242 | div#console,
243 | canvas#canvas-area,
244 | canvas#webgl-area,
245 | svg#svg-area {
246 | display: none;
247 | }
248 |
249 | div#console {
250 | border: 1px solid #ddd;
251 | border-bottom: 1px solid #ccc;
252 | border-radius: 3px;
253 | font-family: Consolas, "Liberation Mono", Courier, monospace;
254 | margin-bottom: 1em;
255 | width: 800px;
256 | height: 150px;
257 | overflow-y: scroll;
258 | }
259 |
260 | #console {
261 | font-size: 75% !important;
262 | color: #999988;
263 | }
264 | .markdown-body {
265 | padding: 20px;
266 | font: 13.34px Helvetica, arial, freesans, clean, sans-serif;
267 | background-color: white;
268 | font-size: 15px;
269 | line-height: 1.7;
270 | }
271 | .markdown-body>*:first-child {
272 | margin-top: 0 !important;
273 | }
274 | .markdown-body p, .markdown-body blockquote, .markdown-body ul, .markdown-body ol, .markdown-body dl, .markdown-body table, .markdown-body pre {
275 | margin: 15px 0;
276 | }
277 | .markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 {
278 | margin: 1em 0 15px;
279 | padding: 0;
280 | font-weight: bold;
281 | line-height: 1.7;
282 | cursor: text;
283 | position: relative;
284 | }
285 |
286 | .markdown-body h1 {
287 | font-size: 2.5em;
288 | border-bottom: 1px solid #eee;
289 | }
290 | .markdown-body h2 {
291 | font-size: 2em;
292 | border-bottom: 1px solid #eee;
293 | }
294 | .markdown-body h3 {
295 | font-size: 1.5em;
296 | }
297 | .markdown-body hr {
298 | background: transparent url(../images/dirty-shade.png) repeat-x 0 0;
299 | border: 0 none;
300 | color: #ccc;
301 | height: 4px;
302 | padding: 0;
303 | margin: 15px 0;
304 | }
305 | .markdown-body code, .markdown-body tt {
306 | margin: 0 2px;
307 | padding: 0px 5px;
308 | border: 1px solid #ddd;
309 | background-color: #f8f8f8;
310 | border-radius: 3px;
311 | }
312 | pre, code {
313 | font: 12px Consolas, "Liberation Mono", Courier, monospace;
314 | }
315 | i, cite, em, var, address, dfn {
316 | font-style: italic;
317 | }
318 | .gallery-parent {
319 | }
320 | .gallery-panel {
321 | display: inline-block;
322 | width: 450px;
323 | vertical-align: top;
324 | padding: 20px 20px 0 0;
325 | }
326 | h1 {
327 | color: #4183c4;
328 | font-weight: bold;
329 | font-size: 28px;
330 | letter-spacing: -1px;
331 | font-family: Helvetica, arial, freesans, clean, sans-serif;
332 | }
333 | .gist-description a {
334 | color: #4183c4;
335 | word-break: break-all;
336 | }
337 | .gist-description p {
338 | padding-bottom: 10px;
339 | }
340 | .content {
341 | padding-top: 45px;
342 | padding-bottom: 30px;
343 | }
344 | .header, .footer {
345 | background-color: #f3f3f3;
346 | background-image: -moz-linear-gradient(#f9f9f9, #f3f3f3);
347 | background-image: -webkit-linear-gradient(#f9f9f9, #f3f3f3);
348 | background-image: linear-gradient(#f9f9f9, #f3f3f3);
349 | background-repeat: repeat-x;
350 | text-shadow: 0 1px 0 #fff;
351 | border-bottom: 1px solid #e5e5e5;
352 | line-height: 40px;
353 | height: 40px;
354 | width: 100%;
355 | position: fixed;
356 | z-index: 5;
357 | }
358 |
359 | .footer {
360 | padding-top: 3px;
361 | line-height: 18px;
362 | height: 18px;
363 | border-top: 1px solid #e5e5e5;
364 | bottom: 0;
365 | right: 0;
366 | font: 13px Helvetica, arial, freesans, clean, sans-serif;
367 | }
368 |
369 | .header a,
370 | .footer a {
371 | text-decoration: none;
372 | padding-top: 5px;
373 | font: 13px Helvetica, arial, freesans, clean, sans-serif;
374 | font-weight: bold;
375 | color: #333333;
376 | transition: all 0.1s ease-in;
377 | -webkit-transition: all 0.1s ease-in 0;
378 | }
379 |
380 | .header a:hover,
381 | .footer a:hover {
382 | color: #4183c4;
383 | }
384 |
385 | .command-bar form,
386 | .command-bar ul {
387 | padding-left: 40px;
388 | font: 13px Helvetica, arial, freesans, clean, sans-serif;
389 | font-weight: normal!important;
390 | display: inline;
391 | color: #333333;
392 | }
393 | .command-bar li {
394 | list-style-type: none;
395 | display: inline;
396 | color: #aaaaaa;
397 | }
398 | .command-bar li > a {
399 | font-weight: normal!important;
400 | color: #333333;
401 | }
402 | .command-bar li:after {
403 | content: "|";
404 | margin: 0 6px;
405 | }
406 | .command-bar li:last-child:after {
407 | content:"";
408 | margin: 0;
409 | }
410 | .datetime time {
411 | cursor: help;
412 | }
413 | #editor {
414 | width: 1024px;
415 | height: 600px;
416 | border: 1px lightgrey solid;
417 | margin-bottom: 10px;
418 | }
419 | #spinner {
420 | z-index: 4;
421 | }
422 | .hidden {
423 | display: none!important;
424 | }
425 | .github-ribbon {
426 | background-color:#090;
427 | top:3.2em;
428 | right:-3.7em;
429 | -webkit-transform:rotate(45deg);
430 | -moz-transform:rotate(45deg);
431 | -ms-transform:rotate(45deg);
432 | -o-transform:rotate(45deg);
433 | transform:rotate(45deg);
434 | -webkit-box-shadow:0 0 0 1px #090 inset,0 0 2px 1px #fff inset,0 0 1em #888;
435 | -moz-box-shadow:0 0 0 1px #090 inset,0 0 2px 1px #fff inset,0 0 1em #888;
436 | -ms-box-shadow:0 0 0 1px #090 inset,0 0 2px 1px #fff inset,0 0 1em #888;
437 | -o-box-shadow:0 0 0 1px #090 inset,0 0 2px 1px #fff inset,0 0 1em #888;
438 | box-shadow:0 0 0 1px #090 inset,0 0 2px 1px #fff inset,0 0 1em #888;
439 | color:rgba(255,255,255,0.9);
440 | display:block;
441 | padding:.6em 3.5em;
442 | position:fixed;
443 | font:bold .82em sans-serif;
444 | text-align:center;
445 | text-decoration:none;
446 | text-shadow:1px -1px 8px rgba(0,0,0,0.6);
447 | -webkit-user-select:none;
448 | -moz-user-select:none;
449 | -ms-user-select:none;
450 | -o-user-select:none;
451 | user-select:none;
452 | z-index:10;
453 | }
454 | div.spinner {
455 | position: relative;
456 | width: 100px;
457 | height: 100px;
458 | display: inline-block;
459 | }
460 |
461 | div.spinner div {
462 | width: 12%;
463 | height: 26%;
464 | background: #000;
465 | position: absolute;
466 | left: 44.5%;
467 | top: 37%;
468 | opacity: 0;
469 | -webkit-animation: fade 1s linear infinite;
470 | -webkit-border-radius: 50px;
471 | -webkit-box-shadow: 0 0 3px rgba(0,0,0,0.2);
472 | -moz-animation: fade 1s linear infinite;
473 | -moz-border-radius: 50px;
474 | -moz-box-shadow: 0 0 3px rgba(0,0,0,0.2);
475 | border-radius: 50px;
476 | }
477 |
478 | div.spinner div.bar1 {
479 | -webkit-transform:rotate(0deg) translate(0, -142%);
480 | -webkit-animation-delay: 0s;
481 | -moz-transform:rotate(0deg) translate(0, -142%);
482 | -moz-animation-delay: 0s;
483 | -ms-transform:rotate(0deg) translate(0, -142%);
484 | -ms-animation-delay: 0s;}
485 | div.spinner div.bar2 {
486 | -webkit-transform:rotate(30deg) translate(0, -142%);
487 | -webkit-animation-delay: -0.9167s;
488 | -moz-transform:rotate(30deg) translate(0, -142%);
489 | -moz-animation-delay: -0.9167s;}
490 | div.spinner div.bar3 {
491 | -webkit-transform:rotate(60deg) translate(0, -142%);
492 | -webkit-animation-delay: -0.833s;
493 | -moz-transform:rotate(60deg) translate(0, -142%);
494 | -moz-animation-delay: -0.833s;}
495 | div.spinner div.bar4 {
496 | -webkit-transform:rotate(90deg) translate(0, -142%);
497 | -webkit-animation-delay: -0.75s;
498 | -moz-transform:rotate(90deg) translate(0, -142%);
499 | -moz-animation-delay: -0.75s;}
500 | div.spinner div.bar5 {
501 | -webkit-transform:rotate(120deg) translate(0, -142%);
502 | -webkit-animation-delay: -0.667s;
503 | -moz-transform:rotate(120deg) translate(0, -142%);
504 | -moz-animation-delay: -0.667s;}
505 | div.spinner div.bar6 {
506 | -webkit-transform:rotate(150deg) translate(0, -142%);
507 | -webkit-animation-delay: -0.5833s;
508 | -moz-transform:rotate(150deg) translate(0, -142%);
509 | -moz-animation-delay: -0.5833s;}
510 | div.spinner div.bar7 {
511 | -webkit-transform:rotate(180deg) translate(0, -142%);
512 | -webkit-animation-delay: -0.5s;
513 | -moz-transform:rotate(180deg) translate(0, -142%);
514 | -moz-animation-delay: -0.5s;}
515 | div.spinner div.bar8 {
516 | -webkit-transform:rotate(210deg) translate(0, -142%);
517 | -webkit-animation-delay: -0.41667s;
518 | -moz-transform:rotate(210deg) translate(0, -142%);
519 | -moz-animation-delay: -0.41667s;}
520 | div.spinner div.bar9 {
521 | -webkit-transform:rotate(240deg) translate(0, -142%);
522 | -webkit-animation-delay: -0.333s;
523 | -moz-transform:rotate(240deg) translate(0, -142%);
524 | -moz-animation-delay: -0.333s;}
525 | div.spinner div.bar10 {
526 | -webkit-transform:rotate(270deg) translate(0, -142%);
527 | -webkit-animation-delay: -0.25s;
528 | -moz-transform:rotate(270deg) translate(0, -142%);
529 | -moz-animation-delay: -0.25s;}
530 | div.spinner div.bar11 {
531 | -webkit-transform:rotate(300deg) translate(0, -142%);
532 | -webkit-animation-delay: -0.1667s;
533 | -moz-transform:rotate(300deg) translate(0, -142%);
534 | -moz-animation-delay: -0.1667s;}
535 | div.spinner div.bar12 {
536 | -webkit-transform:rotate(330deg) translate(0, -142%);
537 | -webkit-animation-delay: -0.0833s;
538 | -moz-transform:rotate(330deg) translate(0, -142%);
539 | -moz-animation-delay: -0.0833s;}
540 |
541 | @-webkit-keyframes fade {
542 | from {opacity: 1;}
543 | to {opacity: 0.25;}
544 | }
545 |
546 | @-moz-keyframes fade {
547 | from {opacity: 1;}
548 | to {opacity: 0.25;}
549 | }
550 |
551 | div.container {
552 | position: absolute;
553 | top: 40%;
554 | left: 45%;
555 | display: block;
556 | padding: 1.5em 1.5em 1.25em;
557 | background: rgba(0,0,0,0.8);
558 | -webkit-border-radius: 1em;
559 | -moz-border-radius: 1em;
560 | border-radius: 1em;
561 | margin: 1em;
562 | }
563 |
564 | div.container.grey {background: rgba(0,0,0,0.6);}
565 |
566 | div.container.grey div.spinner {
567 | width: 60px;
568 | height: 60px;
569 | }
570 |
571 | div.container div.spinner div {background: #fff;}
572 |
573 | span.search {
574 | position: relative;
575 | }
576 |
577 | .search div {
578 | display: inline-block;
579 | color: #333333;
580 | padding: 13px 8px;
581 | position: absolute;
582 | z-index:3;
583 | }
584 |
585 | .search input {
586 | padding-left: 20px
587 | }
588 |
589 | span.num-items {
590 | padding-left: 40px;
591 | }
592 |
593 | .gist-syntax .MathJax .mi,
594 | .gist-syntax .MathJax .mo {
595 | color: #333;
596 | }
597 |
598 |
--------------------------------------------------------------------------------
/resources/public/images/404.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rm-hull/programming-enchiladas/0b567db14c78c9042d1edc5afaaa4f0c250841fa/resources/public/images/404.jpg
--------------------------------------------------------------------------------
/resources/public/images/coming-soon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rm-hull/programming-enchiladas/0b567db14c78c9042d1edc5afaaa4f0c250841fa/resources/public/images/coming-soon.png
--------------------------------------------------------------------------------
/resources/public/images/dirty-shade.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rm-hull/programming-enchiladas/0b567db14c78c9042d1edc5afaaa4f0c250841fa/resources/public/images/dirty-shade.png
--------------------------------------------------------------------------------
/resources/public/images/error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rm-hull/programming-enchiladas/0b567db14c78c9042d1edc5afaaa4f0c250841fa/resources/public/images/error.png
--------------------------------------------------------------------------------
/resources/public/images/error@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rm-hull/programming-enchiladas/0b567db14c78c9042d1edc5afaaa4f0c250841fa/resources/public/images/error@2x.png
--------------------------------------------------------------------------------
/resources/public/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rm-hull/programming-enchiladas/0b567db14c78c9042d1edc5afaaa4f0c250841fa/resources/public/images/favicon.png
--------------------------------------------------------------------------------
/resources/public/js/create.js:
--------------------------------------------------------------------------------
1 |
2 | $(document).ready(function() {
3 | var editor = ace.edit("editor");
4 | editor.setTheme("ace/theme/clouds");
5 | editor.getSession().setMode("ace/mode/clojure");
6 | editor.setFontSize(14);
7 | editor.setShowInvisibles(false);
8 | editor.setShowFoldWidgets(false);
9 | editor.setDisplayIndentGuides(true);
10 | editor.setValue(window.localStorage.lastUnsavedSource);
11 |
12 | var $spinner = $('#spinner');
13 |
14 | var $compile = $("#compile");
15 | $compile.bind('click', function(event) {
16 | if (!$spinner.is(':visible')) {
17 | editor.setReadOnly(true);
18 | $spinner.show();
19 | $compile.attr('disabled', true);
20 | $.ajax(
21 | '/_create/compile?_=' + new Date().getTime(),
22 | {
23 | type: 'POST',
24 | dataType: 'script',
25 | data: {source: editor.getValue()}
26 | }
27 | ).always(function() {
28 | $spinner.hide();
29 | $compile.attr('disabled', false);
30 | editor.setReadOnly(false);
31 | });
32 | }
33 |
34 | return false;
35 | });
36 |
37 | setInterval(function() {
38 | window.localStorage.lastUnsavedSource = editor.getValue();
39 | }, 5000);
40 |
41 | $spinner.hide();
42 | });
--------------------------------------------------------------------------------
/resources/public/js/doc.shim.js:
--------------------------------------------------------------------------------
1 | // Hijack getElementById to always return an item
2 | // If one isnt there, just create it before the canvas
3 | document._getElementById = document.getElementById;
4 |
5 | document.getElementById = function(id) {
6 | var elem = document._getElementById(id);
7 | if (elem === null) {
8 | if (id.indexOf('MathJax') < 0) {
9 | var container = document._getElementById('main-arena');
10 | if (container !== null) {
11 | elem = document.createElement('div');
12 | elem.setAttribute('id', id);
13 | elem.setAttribute('style', 'width:800px; height:600px;overflow:hidden;');
14 | container.appendChild(elem);
15 | }
16 | }
17 | }
18 | return elem;
19 | };
--------------------------------------------------------------------------------
/src/client/enchilada.cljs:
--------------------------------------------------------------------------------
1 | (ns enchilada
2 | (:use
3 | [clojure.string :only [split]]
4 | [monet.canvas :only [get-context]]
5 | [monet.core :only [animation-frame]]
6 | [jayq.core :only [$ hide show append]]))
7 |
8 | (def console ($ "div#console"))
9 | (def canvas ($ :#canvas-area))
10 | (def svg ($ :#svg-area))
11 | (def webgl ($ :#webgl-area))
12 |
13 | (def ctx (get-context (.get canvas 0) "2d"))
14 |
15 | (set! *print-fn*
16 | (fn [s]
17 | (.log js/console s)
18 | (show console)
19 | (append console (str "" s "
"))))
20 |
21 | (defn proxy-request [url]
22 | (str "/proxy?url=" (js/encodeURI url)))
23 |
24 | (defn canvas-size []
25 | [(.width canvas) (.height canvas)])
26 |
27 | (defn- pair
28 | "Pulls apart a string separated by a = into constituent vector parts,
29 | coverts the key into a keyword"
30 | [s]
31 | (let [[k v] (split s #"=")]
32 | [(keyword k) v]))
33 |
34 | (defn- get-params
35 | "Extracts a set of key-value pairs separated by & into a map"
36 | [s]
37 | (let [drop1 (apply str (drop 1 (seq s)))]
38 | (->>
39 | (split drop1 #"&")
40 | (map pair)
41 | (into {}))))
42 |
43 | (defn value-of [k default]
44 | (let [params (get-params (.-search (.-location js/window)))]
45 | (get params k default)))
46 |
47 | (defn url-params []
48 | (get-params (.-search (.-location js/window))))
49 |
50 | (defn to-js [x]
51 | (cond
52 | (map? x)
53 | (let [out (js/Object.)]
54 | (doseq [[k v] x]
55 | (aset out (name k) (to-js v)))
56 | out)
57 |
58 | (= (type x) (type []))
59 | (apply array (map to-js x))
60 |
61 | :else x))
62 |
63 | (hide ($ :div#spinner))
64 |
--------------------------------------------------------------------------------
/src/enchilada/controllers/canvas.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.controllers.canvas
2 | (:require
3 | [compojure.core :refer [defroutes GET]]
4 | [ring.util.response :refer [status file-response response content-type header]]
5 | [hiccup.core :refer :all]
6 | [enchilada.util.compiler :refer [regenerate-if-stale]]
7 | [enchilada.util.fs :refer [output-file output-dir]]
8 | [enchilada.util.gist :refer [fetch]]
9 | [enchilada.util.time-ago :refer [latest-commit-date]]
10 | [enchilada.views.common :refer [html-exception]]
11 | [enchilada.views.canvas :refer [render-page]]
12 | [enchilada.services.gamification :as gamification]))
13 |
14 |
15 | (defn- debug? [req]
16 | (= "true" (get-in req [:params :debug])))
17 |
18 | (defn- serve-js [{:keys [gist] :as build-opts}]
19 | (->
20 | (regenerate-if-stale gist build-opts)
21 | (output-file)
22 | (file-response)
23 | (content-type "application/javascript")))
24 |
25 | (defn- serve-source-file [path file-type]
26 | (->
27 | (file-response path)
28 | (content-type file-type)))
29 |
30 | (defn- serve-error [ex]
31 | (->
32 | (str "$('div#spinner').hide();"
33 | "$('canvas#world').slideUp();"
34 | "$('div#error').html('" (html [:h1 "Compilation failed:"]) (html-exception ex) "').fadeIn();")
35 | (response)
36 | (content-type "application/javascript")))
37 |
38 | (defn- wrap-error-handler [model f]
39 | (try
40 | (f model)
41 | (catch Exception ex (serve-error ex))))
42 |
43 | (defn- create-model [user id req]
44 | (let [gist (fetch user id)]
45 | { :debug (debug? req)
46 | :optimization-level (get-in req [:params :optimization-level])
47 | :gist gist
48 | :request req
49 | :stats (gamification/view gist)}))
50 |
51 | (defn- perform-audits! [{:keys [gist stats] :as model}]
52 | (gamification/increment-visits gist)
53 | (let [delta (gamification/staleness stats (latest-commit-date gist))]
54 | (when-not (zero? delta)
55 | (gamification/set-last-updated gist delta)))
56 | model)
57 |
58 | (defroutes routes
59 | (GET ["/_cljs/:owner/:id/generated.js", :id #"[a-f0-9]+"] [owner id :as req]
60 | (-> (create-model owner id req) (wrap-error-handler serve-js)))
61 |
62 | (GET "/_cljs/*" [:as req]
63 | (let [path (subs (:uri req) 6)]
64 | (serve-source-file
65 | (str (output-dir nil) path)
66 | (if (.endsWith path ".js.map")
67 | "application/json"
68 | "text/plain"))))
69 |
70 | (GET "/:owner/:id" [owner id :as req]
71 | (-> (create-model owner id req) perform-audits! render-page)))
72 |
--------------------------------------------------------------------------------
/src/enchilada/handler.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.handler
2 | (:use [compojure.core]
3 | [ring.middleware.params :only [wrap-params]]
4 | [ring.middleware.gzip :only [wrap-gzip]]
5 | [enchilada.middleware.cache :only [wrap-cache-control]]
6 | [hiccup.middleware :only [wrap-base-url]]
7 | [enchilada.util.keepalive :only [ping]]
8 | [enchilada.util.fs])
9 | (:require [compojure.handler :as handler]
10 | [compojure.route :as route]
11 | [enchilada.controllers.canvas :as canvas]
12 | [enchilada.services.search :as search]
13 | [enchilada.views.not-found :as not-found]
14 | [enchilada.views.proxy :as proxy]
15 | [enchilada.views.create :as create]
16 | [enchilada.views.stats :as stats]
17 | [enchilada.views.sitemap :as sitemap]
18 | [enchilada.views.welcome :as welcome]))
19 |
20 | (def keep-alive
21 | (when-let [url (System/getenv "KEEPALIVE_URL")]
22 | (ping url 47)))
23 |
24 | (search/index-all)
25 |
26 | (defroutes app-routes
27 | welcome/routes
28 | create/routes
29 | stats/routes
30 | canvas/routes
31 | sitemap/routes
32 | proxy/routes
33 | (route/resources "/_assets")
34 | (route/not-found (not-found/page)))
35 |
36 | (def app
37 | (->
38 | (handler/site app-routes)
39 | (wrap-gzip)
40 | (wrap-cache-control
41 | {:max-age 2592000 :public true :must-revalidate true}
42 | #{"/_assets/images" "/_assets/css" "/_assets/js" "/_images"})
43 | (wrap-base-url)
44 | (wrap-params)))
45 |
46 |
--------------------------------------------------------------------------------
/src/enchilada/middleware/cache.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.middleware.cache
2 | "Adds cache control headers to response"
3 | (:require
4 | [clojure.string :as str]
5 | [clj-time.core :refer [now plus seconds]]
6 | [clj-time.format :refer [formatters unparse]]))
7 |
8 | (def rfc822-fmt (formatters :rfc822))
9 |
10 | (defn as-str
11 | "Like clojure.core/str, but if an argument is a keyword or symbol,
12 | its name will be used instead of its literal representation.
13 |
14 | Example:
15 | (str :foo :bar) ;;=> \":foo:bar\"
16 | (as-str :foo :bar) ;;=> \"foobar\"
17 |
18 | Note that this does not apply to keywords or symbols nested within
19 | data structures; they will be rendered as with str.
20 |
21 | Example:
22 | (str {:foo :bar}) ;;=> \"{:foo :bar}\"
23 | (as-str {:foo :bar}) ;;=> \"{:foo :bar}\" "
24 | ([] "")
25 | ([x] (if (instance? clojure.lang.Named x)
26 | (name x)
27 | (str x)))
28 | ([x & ys]
29 | ((fn [#^StringBuilder sb more]
30 | (if more
31 | (recur (. sb (append (as-str (first more)))) (next more))
32 | (str sb)))
33 | (new StringBuilder #^String (as-str x)) ys)))
34 |
35 | (defn get-path [^String uri]
36 | (when uri
37 | (let [idx (.lastIndexOf uri "/")]
38 | (if (neg? idx)
39 | uri
40 | (.substring uri 0 idx)))))
41 |
42 | (defn header-option
43 | "Converts a header option KeyValue into a string."
44 | [[key val]]
45 | (cond
46 | (true? val) (as-str key)
47 | (false? val) nil
48 | :otherwise (as-str key "=" val)))
49 |
50 | (defn header-options
51 | "Converts a map into an HTTP header options string."
52 | [m delimiter]
53 | (str/join delimiter
54 | (remove nil? (map header-option m))))
55 |
56 | (defn overwrite [handler request headers]
57 | (if-let [response (handler request)]
58 | (assoc response :headers
59 | (merge (:headers response) headers))))
60 |
61 | (defn wrap-cache-control
62 | "Middleware to set the Cache-Control http header. Map entries with boolean
63 | values either write their key if true, or nothing if false.
64 | Example:
65 | {:max-age 3600 :public false :must-revalidate true}
66 | => Cache-Control: max-age=3600, must-revalidate"
67 | [handler header-map cacheable]
68 | (let [seconds (seconds (or (:max-age header-map) 2592000))
69 | cache-ctrl (header-options header-map ", ")]
70 | (fn [req]
71 | (if-not (cacheable (get-path (:uri req)))
72 | (handler req)
73 | (overwrite
74 | handler req
75 | {"Cache-Control" cache-ctrl
76 | "Exprires" (unparse rfc822-fmt (plus (now) seconds))})))))
77 |
--------------------------------------------------------------------------------
/src/enchilada/middleware/fso.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.middleware.fso
2 | (:use [ring.util.response :only [file-response content-type]]))
3 |
4 | (defn wrap-filesystem-object [handler regex]
5 | (fn [req]
6 | (let [path (:uri req)]
7 | (if (re-matches regex path)
8 | (-> (file-response path) (content-type "text/plain"))
9 | (handler req)))))
10 |
11 |
--------------------------------------------------------------------------------
/src/enchilada/services/gamification.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.services.gamification
2 | (:refer-clojure :exclude [sort find])
3 | (:require
4 | [monger.collection :as mc]
5 | [monger.core :as mg]
6 | [monger.operators :refer :all]
7 | [monger.query :refer :all]
8 | [enchilada.util.gist :refer :all]
9 | [clj-time.coerce :refer [to-long]]))
10 |
11 | (defn- id [gist] {:gist (login-id gist)})
12 |
13 | (def mongo-client
14 | (when-let [connection-details (System/getenv "MONGODB_URL")]
15 | (mg/connect-via-uri! connection-details)))
16 |
17 | (def doc "gamification")
18 |
19 | (defn increment-visits [gist]
20 | (when mongo-client
21 | (mc/update doc (id gist) {$inc {:visits 1}} :upsert true)))
22 |
23 | (defn view [gist]
24 | (when mongo-client
25 | (mc/find-one-as-map doc (id gist))))
26 |
27 | (defn star
28 | ([gist]
29 | (star gist 1))
30 | ([gist n]
31 | (when mongo-client
32 | (mc/update doc (id gist) {$inc {:stars n}} :upsert true))))
33 |
34 | (defn unstar [gist]
35 | (when mongo-client
36 | (star gist -1)))
37 |
38 | (defn staleness [stats last-updated-dt]
39 | (- (to-long last-updated-dt) (get stats :last_updated 0)))
40 |
41 | (defn set-last-updated [gist delta]
42 | (when mongo-client
43 | (mc/update doc (id gist) {$inc {:last_updated delta}} :upsert true)))
44 |
45 | (defn top-n [field order n]
46 | (when mongo-client
47 | (->>
48 | (with-collection doc
49 | (find {})
50 | (fields [:gist :hidden])
51 | (sort (sorted-map field order))
52 | (limit n))
53 | (remove :hidden))))
54 |
--------------------------------------------------------------------------------
/src/enchilada/services/search.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.services.search
2 | (:require
3 | [clojure.string :as str]
4 | [me.raynes.fs :as fs]
5 | [caponia.index :as idx]
6 | [caponia.query :as qry]
7 | [clojure.java.io :as io]
8 | [enchilada.util.gist :refer [filename->gist]]
9 | [enchilada.util.fs :refer [work-files* is-json? fetch-gist]]))
10 |
11 | (def ^:private fti (idx/make-index))
12 |
13 | (defn ^:private text-file? [^String uri]
14 | (or (.endsWith uri ".txt") (.endsWith uri ".md")))
15 |
16 | (defn index
17 | ([gist] (index gist true))
18 | ([gist clear-existing?]
19 | (let [id (:id gist)
20 | user (:login (or (:owner gist) (:user gist)))
21 | descr (:description gist)
22 | fnames (str/join " " (keys (:files gist)))
23 | content (str/join " " (map :content (filter (comp text-file? :filename) (vals (:files gist)))))
24 | doc-id {:id id :owner { :login user}}
25 | terms (filter first [[user 3] [descr 2] [fnames 1] [content 1]])]
26 | (future
27 | (when clear-existing?
28 | (idx/unindex-all fti doc-id))
29 | (idx/index-text fti doc-id terms))
30 | gist)))
31 |
32 | (defn ^:private drop-all [index]
33 | (send index assoc-in [:data] {}))
34 |
35 | (defn index-all []
36 | (drop-all fti)
37 | (doseq [gist (->>
38 | (io/file "work/gists/cache")
39 | (work-files* is-json?)
40 | (map (comp fetch-gist filename->gist)))]
41 | (index gist false)))
42 |
43 | (defn query [search-text]
44 | (qry/do-search fti search-text))
45 |
--------------------------------------------------------------------------------
/src/enchilada/util/compiler.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.util.compiler
2 | (:use [enchilada.util.fs])
3 | (:require
4 | [cljs.closure :as cljsc]
5 | [enchilada.services.search :as search]))
6 |
7 | (def cljs-build-opts
8 | (let [valid-opts #{:simple :whitespace :advanced}]
9 | (fn [gist {:keys [debug optimization-level]}]
10 | (let [output-file (output-file gist)
11 | defaults { :output-to output-file
12 | :source-map (str output-file ".map")
13 | :output-dir (output-dir gist)
14 | :optimizations (get valid-opts (keyword optimization-level) :advanced)
15 | :language-in :ecmascript5
16 | :language-out :ecmascript5
17 | :pretty-print false
18 | :static-fns true
19 | :externs ["resources/private/externs/arbor.js"
20 | "resources/private/externs/jquery.js"
21 | "resources/private/externs/three.js"
22 | "resources/private/externs/react.js"
23 | "resources/private/externs/PhiloGL.js"]
24 | :foreign-libs [{:file "resources/private/js/arbor.js" :provides ["arbor"]}
25 | {:file "resources/private/js/react.js" :provides ["React"]}
26 | {:file "resources/private/js/three.js" :provides ["THREE"]}
27 | {:file "resources/private/js/gl-matrix-min.js" :provides ["mat3","mat4","vec3"]}
28 | {:file "resources/private/js/webgl-utils.js" :provides ["WebGLUtils"]}]
29 | :libs ["resources/private/js/singult.js"]
30 | :closure-warnings {:externs-validation :off
31 | :non-standard-jsdoc :off}}]
32 | (if debug
33 | (merge defaults {:optimizations :simple, :pretty-print true})
34 | defaults)))))
35 |
36 | (defn generate-js [gist build-opts]
37 | (prepare gist)
38 | (cljsc/build
39 | (src-dir gist)
40 | (cljs-build-opts gist build-opts))
41 | gist)
42 |
43 | (defn regenerate-if-stale [gist build-opts]
44 | (if (or (:debug build-opts) (stale? gist))
45 | (-> (search/index gist) (generate-js build-opts))
46 | gist))
47 |
--------------------------------------------------------------------------------
/src/enchilada/util/fs.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.util.fs
2 | (:require
3 | [clojure.java.io :as io]
4 | [clojure.data.json :as json]
5 | [clj-time.format :refer [parse]]
6 | [clj-time.coerce :refer [to-long]]
7 | [me.raynes.fs :as fs]
8 | [enchilada.util.time-ago :refer [latest-commit-date]])
9 | (:import
10 | [java.io File FileNotFoundException]))
11 |
12 | (defn- paths [& paths]
13 | (interpose "/" (flatten paths)))
14 |
15 | (defn filename-template [prefix & suffix]
16 | (fn [gist]
17 | (apply str (concat (paths "work" "gists" prefix (get-in gist [:owner :login]) (:id gist)) suffix))))
18 |
19 | (def src-dir (filename-template "src" "/"))
20 | (def temp-dir (filename-template "tmp" "/"))
21 | (def cache-file (filename-template "cache" ".json"))
22 | (def png-img-file (filename-template "images" ".png"))
23 | (def jpg-img-file (filename-template "images" ".jpg"))
24 | (def output-file (filename-template "out" "/generated.js"))
25 | (def output-dir (filename-template "out" "/"))
26 |
27 | ; Attribution: http://clojurescriptone.com/
28 | (defn delete
29 | [& paths]
30 | (doseq [path paths
31 | file (reverse (file-seq (io/file path)))]
32 | (fs/delete file)))
33 |
34 | (defn clean [f gist]
35 | (let [dir (io/file (f gist))]
36 | (delete dir)
37 | (fs/mkdirs dir)))
38 |
39 | (defn write-file [^File file contents & [last-modified]]
40 | (spit file contents)
41 | (when last-modified
42 | (.setLastModified file last-modified)))
43 |
44 | (defn- add-namespace [^String content]
45 | (if (neg? (.indexOf content "(ns"))
46 | (str "(ns " (gensym) ")\n\n" content)
47 | content))
48 |
49 | (defn persist [gist]
50 | (clean src-dir gist)
51 | (let [last-modified (to-long (latest-commit-date gist))
52 | dir (src-dir gist)]
53 | (doseq [{filename :filename content :content} (vals (:files gist))]
54 | (write-file (io/file dir filename) (add-namespace content) last-modified)))
55 |
56 | (fs/mkdirs (fs/parent (cache-file gist)))
57 | (with-open [w (io/writer (cache-file gist))]
58 | (json/write gist w :key-fn name)))
59 |
60 | (defn fetch-gist
61 | "Fetches a gist from a local filestore, and parses it into a keyword hash"
62 | [gist]
63 | (try
64 | (with-open [r (io/reader (cache-file gist))]
65 | (json/read r :key-fn keyword))
66 | (catch FileNotFoundException fnfe
67 | nil)))
68 |
69 | (defn prepare [gist]
70 | (fs/mkdirs (fs/parent (io/file (output-file gist))))
71 | (persist gist)
72 | (io/copy (io/file "src/client/enchilada.cljs") (io/file (src-dir gist) "__init.cljs"))
73 | (clean temp-dir gist))
74 |
75 | (defn stale?
76 | "Checks to see if the generated javascript is stale (older than)
77 | compared to the last modified time on the gist"
78 | [gist]
79 | (<
80 | (fs/mod-time (io/file (output-file gist)))
81 | (to-long (latest-commit-date gist))))
82 |
83 | (defn is-filetype? [suffix ^File f]
84 | (and
85 | (fs/file? f)
86 | (.endsWith (.getName f) suffix)))
87 |
88 | (def is-json? (partial is-filetype? ".json"))
89 |
90 | (defn work-files* [pred ^File dir]
91 | (filter pred (file-seq dir)))
92 |
--------------------------------------------------------------------------------
/src/enchilada/util/gist.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.util.gist
2 | (:require
3 | [clojure.string :as str]
4 | [clj-http.client :as http]
5 | [clojure.data.json :as json]
6 | [me.raynes.fs :refer [absolute-path]]
7 | [enchilada.util.fs :refer [fetch-gist]])
8 | (:import
9 | [java.util UUID]))
10 |
11 | (defn- url [id] (str "https://api.github.com/gists/" id))
12 |
13 | (def github-headers
14 | (merge
15 | {"Accept" "application/vnd.github.v3+json"}
16 | (when-let [token (get (System/getenv) "GITHUB_OAUTH_TOKEN")]
17 | {"authorization" (str "token " token)})))
18 |
19 | (defn fetch
20 | "Fetches a gist from the mothership and parses it from JSON into a keyword hash"
21 | [owner id]
22 | (try
23 | (let [{:keys [status headers body]} (http/get (url id) {:headers github-headers})]
24 | (when (= status 200)
25 | (json/read-str body :key-fn keyword)))
26 | (catch Exception e
27 | (fetch-gist {:owner {:login owner} :id id}))))
28 |
29 | (defn login-id
30 | "Constructs the github \"login/id\" path element"
31 | [gist]
32 | (str
33 | (or
34 | (get-in gist [:owner :login])
35 | (get-in gist [:user :login]))
36 | "/"
37 | (:id gist)))
38 |
39 | (defn anonymous [src]
40 | {:owner {:login "__anonymous"}
41 | :id (str (UUID/randomUUID))
42 | :history []
43 | :files {"not_yet_created.cljs" {:filename "__temp.cljs" :content src }}})
44 |
45 | (def user-id-regex #".*/(.*)/(.*).(json|png)")
46 |
47 | (defn filename->gist [file]
48 | (let [[_ user id] (re-find user-id-regex (absolute-path file))]
49 | {:id id :owner { :login user}}))
50 |
51 | (defn mongo->gist [mongo-record]
52 | (let [[user id] (str/split (:gist mongo-record) #"/")]
53 | {:id id :owner { :login user}}))
54 |
--------------------------------------------------------------------------------
/src/enchilada/util/google_analytics.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.util.google-analytics
2 | (:require
3 | [clojure.string :as str]
4 | [hiccup.page :refer [include-js]]))
5 |
6 | (def template
7 | "")
18 |
19 | (def generate-tracking-script
20 | (memoize
21 | (fn [tracking-id site-url]
22 | (when-not (empty? tracking-id)
23 | (->
24 | template
25 | (str/replace "TRACKING_ID" tracking-id)
26 | (str/replace "SITE_URL" site-url))))))
27 |
28 | (def google-maps-jscript-api
29 | (memoize
30 | (fn [api-key sensor?]
31 | (when-not (empty? api-key)
32 | (include-js
33 | (str "https://maps.googleapis.com/maps/api/js?key=" api-key "&sensor=" sensor?))))))
34 |
--------------------------------------------------------------------------------
/src/enchilada/util/keepalive.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.util.keepalive
2 | (:require [clj-http.client :as http]))
3 |
4 | (defn ping [url delay-mins]
5 | (future
6 | (Thread/sleep (* delay-mins 60 1000))
7 | (ping url delay-mins)
8 | (http/get url)))
9 |
--------------------------------------------------------------------------------
/src/enchilada/util/macros.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.util.macros)
2 |
3 | (defmacro inc! [place delta]
4 | `(set! ~place (+ ~place ~delta)))
5 |
--------------------------------------------------------------------------------
/src/enchilada/util/markdown.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.util.markdown
2 | (:require
3 | [clojure.string :as str]
4 | [hiccup.core :refer [html]]
5 | [hiccup.util :refer [to-uri]]))
6 |
7 | (def ^:private url-regex )
8 |
9 | (def ^:private transformations [
10 | ; URL's
11 | [#"((https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|])"
12 | "$1 "]
13 |
14 | ; Emphasis
15 | [#"([ '\"\(\[\{])_(.+?)_([ '\".,;:\)\]\}]?)"
16 | "$1$2 $3"]
17 |
18 | ; Strong
19 | [#"([ '\"\(\[\{])\*\*(.+?)\*\*([ '\".,;:\)\]\}]?)"
20 | "$1$2 $3"]
21 | ])
22 |
23 | (defn apply-transform [text [regex replacement]]
24 | (str/replace text regex replacement))
25 |
26 | (defn simple-md [text]
27 | (when text
28 | (reduce apply-transform text transformations)))
29 |
--------------------------------------------------------------------------------
/src/enchilada/util/page.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.util.page
2 | (:require
3 | [clojure.string :as str]
4 | [hiccup.core :refer [html]]
5 | [hiccup.util :refer [to-uri]]))
6 |
7 | (defn include-async-js [& scripts]
8 | (for [script scripts]
9 | [:script {:type "text/javascript" :src (to-uri script) :async true}]))
10 |
11 |
--------------------------------------------------------------------------------
/src/enchilada/util/time_ago.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.util.time-ago
2 | (:require
3 | [clj-time.core :refer [interval now in-seconds in-minutes in-hours in-days in-months in-years]]
4 | [clj-time.coerce :refer [to-long from-long]]
5 | [clj-time.format :refer [parse]]))
6 |
7 | (defn elapsed-time [start]
8 | (let [interval (interval start (now))]
9 | (cond
10 | (<= (in-seconds interval) 60) "just now"
11 | (= (in-minutes interval) 1) "a minute ago"
12 | (<= (in-minutes interval) 60) (str (in-minutes interval) " minutes ago")
13 | (= (in-hours interval) 1) "an hour ago"
14 | (<= (in-hours interval) 24) (str (in-hours interval) " hours ago")
15 | (= (in-days interval) 1) "a day ago"
16 | (<= (in-days interval) 31) (str (in-days interval) " days ago")
17 | (= (in-months interval) 1) "a month ago"
18 | (<= (in-months interval) 12) (str (in-months interval) " months ago")
19 | (= (in-years interval) 1) "a year ago"
20 | :else (str (in-years interval) " years ago"))))
21 |
22 | (defn latest-commit-date
23 | "Looks at the history to determine the most recent commit date"
24 | [gist]
25 | (->>
26 | gist
27 | :history
28 | (map (comp to-long parse :committed_at))
29 | (reduce max 0)
30 | (from-long)))
31 |
--------------------------------------------------------------------------------
/src/enchilada/util/twitter.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.util.twitter
2 | (:require
3 | [hiccup.core :refer [html]]
4 | [enchilada.views.common :refer [limit first-filename]]))
5 |
6 | ;
7 | ;
8 | ;
9 | ;
10 | ;
11 | ;
12 | ;
13 |
14 | (defn share-button [site-url req]
15 | (let [url (str site-url (:uri req))]
16 | (html
17 | [:a.twitter-share-button {:href "https://twitter.com/share" :data-hashtags "clojurescript" :data-lang "en" :data-counturl url}]
18 | [:script "!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=\"https://platform.twitter.com/widgets.js\";fjs.parentNode.insertBefore(js,fjs);}}(document,\"script\",\"twitter-wjs\");"])))
19 |
20 | (defn generate-card-metadata [twitter-handle site-url gist]
21 | (let [owner (or (gist :owner) (gist :user))]
22 | (when-not (empty? twitter-handle)
23 | (html
24 | [:meta {:name "twitter:card" :content "summary_large_image"}]
25 | [:meta {:name "twitter:site" :content twitter-handle}]
26 | [:meta {:name "twitter:title" :content (str "Programming Enchiladas :: " (owner :login) " / " (first-filename gist))}]
27 | [:meta {:name "twitter:description" :content (-> :description gist (limit 200))}]
28 | [:meta {:name "twitter:image:src" :content (str site-url "/_images/" (gist :id))}]))))
29 |
--------------------------------------------------------------------------------
/src/enchilada/views/canvas.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.views.canvas
2 | (:require
3 | [compojure.core :refer [defroutes GET]]
4 | [hiccup.core :refer [html]]
5 | [hiccup.element :refer [image link-to]]
6 | [hiccup.page :refer [include-js]]
7 | [enchilada.util.page :refer [include-async-js]]
8 | [enchilada.util.markdown :refer [simple-md]]
9 | [enchilada.util.twitter :as twitter]
10 | [enchilada.util.time-ago :refer :all]
11 | [enchilada.views.not-found :as not-found]
12 | [enchilada.views.common :refer :all]
13 | ))
14 |
15 | (defn- meta-info [gist stats request]
16 | (let [last-updated (latest-commit-date gist)
17 | gist (fn [& props] (get-in gist props))
18 | owner (or (gist :owner) (gist :user))]
19 | [:section.container
20 | [:div.gist-header
21 | [:div.meta
22 | [:h1
23 | [:div.author
24 | (image { :width 26 :height 26 } (owner :avatar_url))
25 | [:span (link-to (owner :html_url) (owner :login))] " / "
26 | [:strong (link-to (gist :html_url) (first-filename gist))]
27 | (when stats
28 | [:div.stats
29 | [:span.share-buttons (twitter/share-button (System/getenv "FULL_SITE_URL") request)]
30 | [:span.views (get stats :visits 1) " views "]
31 | [:span.stars [:a {:href :# :title "Star this gist"} (get stats :stars 0) " \u2605"]]])
32 | [:div.gist-timestamp
33 | [:span.datetime "Last updated "
34 | [:time {:title last-updated :datetime last-updated} (elapsed-time last-updated)]]
35 | (fork-info gist)]]]]]
36 | [:div.gist-description
37 | [:p (-> :description gist simple-md)]]]))
38 |
39 | (defn- query-params [model]
40 | (let [params (select-keys model [:debug :optimization-level])]
41 | (when (seq params)
42 | (str "?" (clojure.string/join "&" (for [[k v] params] (str (name k) "=" v)))))))
43 |
44 | (defn render-page [{:keys [gist debug stats request] :as model}]
45 | (if (nil? gist)
46 | (not-found/page)
47 | (let [owner (or (gist :owner) (gist :user))]
48 | (layout
49 | :title (str "Programming Enchiladas :: " (owner :login) " / " (first-filename gist))
50 | :extra-metadata (twitter/generate-card-metadata
51 | (System/getenv "TWITTER_HANDLE")
52 | (System/getenv "FULL_SITE_URL")
53 | gist)
54 | :content
55 | [:div
56 | (spinner "container grey")
57 | (meta-info gist stats request)
58 | [:section.container
59 | [:div#error]]
60 | [:section#main-arena.container
61 | [:canvas#canvas-area { :width 800 :height 600 }]
62 | [:canvas#webgl-area { :width 800 :height 600 }]
63 | [:svg#svg-area]
64 | [:div#console]]
65 | (ribbon "Fork me on GitHub!" "https://github.com/rm-hull/programming-enchiladas")
66 | [:section.container
67 | (include-js (url gist ".js"))]
68 | (include-async-js (str "/_cljs/" (owner :login) "/" (:id gist) "/generated.js" (query-params model)))]))))
69 |
70 |
--------------------------------------------------------------------------------
/src/enchilada/views/common.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.views.common
2 | (:require
3 | [hiccup.core :refer :all]
4 | [hiccup.page :refer :all]
5 | [clj-stacktrace.core :refer :all]
6 | [clj-stacktrace.repl :refer :all]
7 | [clj-time.core :refer [year now]]
8 | [enchilada.util.page :refer [include-async-js]]
9 | [enchilada.util.gist :refer :all]
10 | [enchilada.util.google-analytics :as ga]))
11 |
12 | (defn url [gist & suffixes]
13 | (apply str "https://gist.github.com/" (login-id gist) suffixes))
14 |
15 | (defn spinner [css-class]
16 | (html
17 | [:div#spinner {:class css-class }
18 | [:div {:class "spinner"}
19 | (for [x (range 1 13)]
20 | (html
21 | [:div {:class (str "bar" x)}]))]]))
22 |
23 | (def home-url "/")
24 |
25 | (def keywords "clojure,clojurescript,gist,programming,lisp,github,html5,canvas,svg,webgl,3d,javascript,algorithms,computer,science,noir,compojure,ring,middleware,lein,leiningen")
26 |
27 | (def blurb "A ClojureScript-based HTML5 Canvas and SVG Graphics Playground, much like http://bl.ocks.org/ but specifically designed for showcasing small ClojuresScript code demos: The underlying agenda is to show how small simple functional programs can generate complex behaviour.")
28 |
29 | (defn link [url sort-param num-items]
30 | (str
31 | url "?sort=" sort-param
32 | (if num-items
33 | (str "&n=" num-items))))
34 |
35 | (def categories
36 | (array-map
37 | :random "Gists chosen at random"
38 | :latest "The most recently updated gists"
39 | :popular "The gists with most page views ☺"
40 | :favourites "Gists which have been ★'d the most"
41 | :unloved "Old, forgotten about, gists ☹" ))
42 |
43 | (defn header-bar [sort-param search-param num-items home-page?]
44 | [:div.header
45 | [:section.container
46 | [:a.header-logo {:href home-url :title "Explore other ClojureScript Gists"} [:i.fa.fa-cutlery] " Programming Enchiladas"]
47 | [:span.command-bar
48 | [:ul.top-nav
49 | (for [[s title] categories
50 | :let [s (name s)]]
51 | [:li
52 | (if (not= s sort-param)
53 | [:a {:href (link home-url s num-items) :title title} s]
54 | s)])]
55 | [:form#search-form {:method "GET" :action "/"}
56 | (when-not (= sort-param "search")
57 | [:input {:type "hidden" :name "sort" :value sort-param}])
58 | [:span.search
59 | [:div [:i.fa.fa-search]]
60 | [:input {:type "search" :placeholder "Search..." :name "search" :value search-param}]]
61 | (when home-page?
62 | [:span.num-items
63 | "Display"
64 | [:select#num-items {:name "n"}
65 | (for [[k v] (array-map 10 10, 20 20, 50 50, -1 "All")]
66 | [:option
67 | (if (= k num-items)
68 | {:selected true :value k}
69 | {:value k})
70 | v])]
71 | "Items"
72 | [:script {:type "text/javascript"}
73 | "document.getElementById('num-items').onchange = function(){ document.getElementById('search-form').submit();};"]])]]]])
74 |
75 | (defn footer-bar [home-page?]
76 | [:div.footer
77 | [:section.container
78 | [:p "Site: copyright © " (year (now)) " Richard Hull. License: "
79 | [:a {:href "http://creativecommons.org/licenses/by/3.0/legalcode"} "Creative Commons v3.0"]
80 | " Content: copyright as per respective owners else as otherwise specified."
81 | ]]])
82 |
83 | (defn layout [& {:keys [title content refresh sort-param search-param count-param home-page? extra-js extra-metadata]}]
84 | (html5
85 | [:head
86 | [:title title]
87 | [:meta {:name "keywords" :content keywords}]
88 | [:meta {:name "description" :content blurb}]
89 | extra-metadata
90 |
91 | (when refresh
92 | [:meta {:http-equiv "refresh" :content refresh}])
93 | [:link {:rel "icon" :type "image/png" :href "/_assets/images/favicon.png"}]
94 | (include-css
95 | "//netdna.bootstrapcdn.com/font-awesome/4.0.0/css/font-awesome.css"
96 | "/_assets/css/default.css")
97 | (apply include-js
98 | "https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"
99 | ;"/_assets/js/arbor.js"
100 | ;"/_assets/js/arbor-tween.js"
101 | extra-js)
102 | (include-async-js
103 | "//cdnjs.cloudflare.com/ajax/libs/react/0.8.0/react.min.js"
104 | "//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"
105 | "/_assets/js/doc.shim.js"
106 | ;"http://www.senchalabs.org/philogl/PhiloGL/build/PhiloGL.js")
107 | "/_assets/js/PhiloGL.js")
108 | [:script {:type "text/x-mathjax-config"}
109 | "MathJax.Hub.Config({tex2jax: {inlineMath: [['$','$']]}});" ]
110 | (ga/google-maps-jscript-api
111 | (System/getenv "GOOGLEMAPS_API_KEY")
112 | false)
113 | (ga/generate-tracking-script
114 | (System/getenv "GA_TRACKING_ID")
115 | (System/getenv "SITE_URL"))]
116 | [:body
117 | [:div.wrapper (header-bar sort-param search-param count-param home-page?)]
118 | [:div.wrapper.content content]]
119 | [:div.wrapper (footer-bar home-page?)]))
120 |
121 | (defn ribbon [text href]
122 | (html
123 | [:a.github-ribbon {:href href :title href :rel "me"} text]))
124 |
125 | (defn- elem-partial [elem]
126 | (if (:clojure elem)
127 | [:tr
128 | [:td.source (h (source-str elem))]
129 | [:td.method (h (clojure-method-str elem))]]
130 | [:tr
131 | [:td.source (h (source-str elem))]
132 | [:td.method (h (java-method-str elem))]]))
133 |
134 | (defn remove-newlines [s]
135 | (clojure.string/replace s "\n" " "))
136 |
137 | (defn replace-apostrophes [s]
138 | (clojure.string/replace s "'" "\\'"))
139 |
140 | (defn html-exception [ex]
141 | (let [ex-seq (iterate :cause (parse-exception ex))
142 | exception (first ex-seq)
143 | causes (rest ex-seq)]
144 | (html
145 | [:div#stacktrace
146 | [:div#exception
147 | [:h3.info (h (remove-newlines (str ex)))]
148 | [:table.trace
149 | :content
150 | [:tbody (map elem-partial (:trace-elems exception))]]]
151 | (for [cause causes :while cause]
152 | [:div#causes
153 | [:h3.info "Caused by: "
154 | (h (.getName (:class cause))) " "
155 | (h (remove-newlines (replace-apostrophes (:message cause))))]
156 | [:table.trace
157 | [:tbody (map elem-partial (:trimmed-elems cause))]]])])))
158 |
159 | (defn first-filename [gist]
160 | (->>
161 | :files
162 | gist
163 | vals
164 | (map :filename)
165 | (filter #(.endsWith % ".cljs"))
166 | first))
167 |
168 | (defn fork-info [gist]
169 | (when-let [fork (gist :fork_of)]
170 | (let [orig-owner (get-in fork [:owner :login])]
171 | [:span.text
172 | "— forked from "
173 | [:a {:href (str "/" orig-owner "/" (:id fork))} (str orig-owner "/" (first-filename gist))]])))
174 |
175 | (defn find-next-space [text max-length]
176 | (let [idx (.indexOf text " " max-length)]
177 | (if (neg? idx)
178 | (count text)
179 | idx)))
180 |
181 | (defn limit [text max-length]
182 | (if (> (count text) max-length)
183 | (str (subs text 0 (find-next-space text max-length)) "...")
184 | text))
185 |
--------------------------------------------------------------------------------
/src/enchilada/views/create.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.views.create
2 | (:require
3 | [clojure.string :as str]
4 | [clojure.java.io :as io]
5 | [me.raynes.fs :as fs]
6 | [compojure.core :refer [defroutes GET POST]]
7 | [ring.util.response :refer [response file-response content-type]]
8 | [hiccup.core :refer [html]]
9 | [enchilada.util.fs :refer [clean src-dir temp-dir cache-file output-dir output-file]]
10 | [enchilada.util.gist :refer [anonymous]]
11 | [enchilada.util.compiler :refer [generate-js]]
12 | [enchilada.views.common :refer [spinner layout ribbon]]))
13 |
14 | (defn create [req]
15 | (let []
16 | (layout
17 | :title "Programming Enchiladas :: Create"
18 | :home-page? false
19 | :extra-js ["//cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/ace.js"
20 | "//cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/theme-clouds.js"
21 | "//cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/mode-clojure.js"
22 | "//cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/snippets/clojure.js"
23 | "/_assets/js/create.js"
24 | ]
25 | :content
26 | [:div
27 | [:section.container
28 | [:h1 [:i.fa.fa-bug] " Create"]
29 | [:div.gist-description
30 | [:p "Code up some ClojureScript, press " [:i "this"] " compile button "
31 | [:i.fa.fa-hand-o-right] " "
32 | [:a#compile {:href "#" :title "Compile clojurescript"} [:i.fa.fa-play-circle.fa-lg]]
33 | " to test it, and then save it as a gist."]]
34 | [:div.create-parent
35 | (spinner "container grey")
36 | (ribbon "Fork me on GitHub!" "https://github.com/rm-hull/programming-enchiladas")
37 | [:div#editor]]]
38 | [:section.container
39 | [:div#error]]
40 | [:section#main-arena.container
41 | [:canvas#canvas-area { :width 800 :height 600 }]
42 | [:canvas#webgl-area { :width 800 :height 600 }]
43 | [:svg#svg-area]
44 | [:div#console]]])))
45 |
46 | (defn compile [req]
47 | (let [src (get-in req [:params :source])
48 | gist (anonymous src)]
49 | (generate-js gist {:debug true})
50 | (let [out (->
51 | (output-file gist)
52 | (file-response)
53 | (content-type "text/javascript"))]
54 | (doseq [path [src-dir temp-dir cache-file]]
55 | (clean path gist))
56 | out)))
57 |
58 | (defroutes routes
59 | (GET "/_create" [:as req] (create req))
60 | (POST "/_create/compile" [:as req] (compile req)))
61 |
--------------------------------------------------------------------------------
/src/enchilada/views/not_found.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.views.not-found
2 | (:require
3 | [ring.util.response :as response]
4 | [enchilada.views.common :refer [layout]]))
5 |
6 | (def messages [
7 | "You step in the stream, but the water has moved on."
8 | "Nothing to sea here. Keep clam."])
9 |
10 | (defn page [& [req]]
11 | (response/not-found
12 | (layout
13 | :title "404"
14 | :content [:div#not-found
15 | [:p (rand-nth messages)]
16 | [:p.err404 "404"]])))
17 |
--------------------------------------------------------------------------------
/src/enchilada/views/proxy.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.views.proxy
2 | (:use [compojure.core :only [defroutes GET]]
3 | [ring.util.response :only [header response]])
4 | (:require [clj-http.client :as http]
5 | [clojure.string :as str])
6 | (:import [java.io ByteArrayInputStream]))
7 |
8 | (defn- add-original-headers [response headers]
9 | (if (nil? headers)
10 | response
11 | (let [item (first headers)]
12 | (recur
13 | (header response (key item) (val item))
14 | (next headers)))))
15 |
16 | (defn- add-cors-headers [response request-headers]
17 | ; attribution: https://github.com/gr2m/CORS-Proxy/blob/master/src/corsproxy.coffee
18 | (let [x-headers (->>
19 | (keys request-headers)
20 | (filter #(.startsWith (str %) "x-")))
21 | headers (str
22 | "accept, accept-charset, accept-encoding, "
23 | "accept-language, authorization, content-length, "
24 | "content-type, host, origin, proxy-connection, "
25 | "referer, user-agent, x-requested-with, "
26 | (str/join ", " x-headers))]
27 | (->
28 | response
29 | (header "access-control-allow-methods" "HEAD, POST, GET, PUT, PATCH, DELETE")
30 | (header "access-control-max-age" "8640") ; 24 hours
31 | (header "access-control-allow-headers" headers)
32 | (header "access-control-allow-credentials" "true")
33 | (header "access-control-allow-origin" (get request-headers :origin "*")))))
34 |
35 | (defn proxy-request [req]
36 | (let [url (get-in req [:params :url])
37 | resp (http/get url {:as :byte-array})]
38 | (if (= (:status resp) 200)
39 | (->
40 | (response (ByteArrayInputStream. (:body resp)))
41 | (add-original-headers (:headers resp))
42 | (add-cors-headers (:headers req))
43 | (header "x-proxied-by" "Programming Enchiladas")))))
44 |
45 | (defroutes routes
46 | (GET "/proxy" [:as req] (proxy-request req)))
47 |
--------------------------------------------------------------------------------
/src/enchilada/views/sitemap.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.views.sitemap
2 | (:use [compojure.core :only [defroutes GET]]
3 | [ring.util.response :only [content-type response]]
4 | [clojure.data.xml :only [emit-str sexp-as-element]]
5 | [clj-time.coerce :only [from-long]]
6 | [enchilada.util.fs :only [is-filetype? work-files*]])
7 | (:require [clojure.java.io :as io]
8 | [me.raynes.fs :as fs])
9 | (:import [java.io File]))
10 |
11 | (defn parent-and-name [^File f]
12 | (str (fs/name (fs/parent f)) "/" (fs/name f)))
13 |
14 | (def is-json? (partial is-filetype? ".json"))
15 |
16 | (defn url-element [url chg-freq priority]
17 | (fn [^File f]
18 | [:url
19 | [:loc (str url "/" (parent-and-name f))]
20 | [:lastmod (str (from-long (fs/mod-time f)))]
21 | [:changefreq (name chg-freq)]
22 | [:priority priority]]))
23 |
24 | (defn generate-sitemap [base-url work-dir]
25 | (sexp-as-element
26 | [:urlset {:xmlns "http://www.sitemaps.org/schemas/sitemap/0.9"}
27 | (map (url-element base-url :weekly 0.5) (work-files* is-json? work-dir))]))
28 |
29 | (defroutes routes
30 | (GET "/sitemap.xml" [:as req]
31 | (let [base-url (str (name (:scheme req)) "://" (:server-name req) ":" (:server-port req))]
32 | (->
33 | (response (emit-str (generate-sitemap base-url (io/file "work/gists/cache"))))
34 | (content-type "text/xml")))))
35 |
--------------------------------------------------------------------------------
/src/enchilada/views/stats.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.views.stats
2 | (:use [compojure.core :only [defroutes GET]]
3 | [hiccup.core :only [html]]
4 | [hiccup.element :only [image link-to]]
5 | [hiccup.page :only [include-js]]))
6 |
7 | (defroutes routes
8 | (GET "/stats" [] "stats page - TODO"))
9 |
10 |
--------------------------------------------------------------------------------
/src/enchilada/views/welcome.clj:
--------------------------------------------------------------------------------
1 | (ns enchilada.views.welcome
2 | (:use
3 | [compojure.core :only [defroutes GET]]
4 | [ring.util.response :only [redirect file-response response header content-type]]
5 | [hiccup.core :only [html]]
6 | [enchilada.services.gamification :as gam]
7 | [enchilada.services.search :as search]
8 | [enchilada.util.time-ago]
9 | [enchilada.util.markdown :only [simple-md]]
10 | [enchilada.util.twitter :as twitter]
11 | [enchilada.util.gist :only [login-id filename->gist mongo->gist]]
12 | [enchilada.util.fs :only [is-json? work-files* fetch-gist png-img-file jpg-img-file]]
13 | [enchilada.views.common])
14 | (:require
15 | [clojure.string :as str]
16 | [clojure.java.io :as io]
17 | [me.raynes.fs :as fs]))
18 |
19 | (def +default-sort-order+ "latest")
20 |
21 | (defn gallery-panel [gist]
22 | (when gist
23 | (let [login-id (login-id gist)
24 | last-updated (latest-commit-date gist)
25 | gist (fn [& props] (get-in gist props))
26 | owner (or (gist :owner) (gist :user))
27 | filename (first-filename gist)]
28 | [:div.gallery-panel
29 | [:div.gist-header
30 | [:div.meta
31 | [:h1
32 | [:div.author
33 | [:img {:src (owner :avatar_url) :width 26 :height 26 }]
34 | [:span [:a {:href login-id} (owner :login)]] " / "
35 | [:strong [:a {:href login-id} filename]]
36 | [:div.gist-timestamp
37 | [:span.datetime "Last updated "
38 | [:time {:title last-updated :datetime last-updated} (elapsed-time last-updated)]]
39 | (fork-info gist)]]]]]
40 | [:div.gist-description
41 | [:p (-> :description gist (limit 200) simple-md)]]
42 | [:div.gallery-picture
43 | [:a {:href (str (owner :login) "/" (gist :id)) :title filename}
44 | [:img {:src (str "_images/" (gist :id)) :width 400 :height 300}]]]])))
45 |
46 | (defn take-1 [n coll]
47 | (if (neg? n)
48 | coll
49 | (take n coll)))
50 |
51 | (defn pick-choice [n sort-param]
52 | (map
53 | mongo->gist
54 | (case sort-param
55 | "popular" (gam/top-n :visits -1 n)
56 | "unloved" (gam/top-n :visits 1 n)
57 | "favourites" (gam/top-n :stars -1 n)
58 | (gam/top-n :last_updated -1 n))))
59 |
60 | (defn pick-search [n search-param]
61 | (take-1 n (map first (search/query search-param))))
62 |
63 | (defn pick-random [n]
64 | (->>
65 | (io/file "work/gists/cache")
66 | (work-files* is-json?)
67 | (map filename->gist)
68 | (shuffle)
69 | (take-1 n)))
70 |
71 | (defn to-int [s]
72 | (Integer/parseInt s))
73 |
74 | (defn welcome [req]
75 | (let [num-items (to-int (get-in req [:query-params "n"] "20"))
76 | search-param (get-in req [:query-params "search"])
77 | sort-param (if-not (empty? search-param)
78 | "search"
79 | (get-in req [:query-params "sort"] +default-sort-order+))
80 | picked-items (case sort-param
81 | "random" (pick-random num-items)
82 | "search" (pick-search num-items search-param)
83 | (pick-choice num-items sort-param))]
84 | (layout
85 | :title "Programming Enchiladas :: Gallery"
86 | :refresh (if (= sort-param "random") 3600)
87 | :sort-param sort-param
88 | :search-param search-param
89 | :count-param num-items
90 | :home-page? true
91 | :content
92 | [:section.container
93 | [:h1
94 | [:i.fa.fa-film.fa-x2]
95 | " Gallery"
96 | [:div.stats [:span.share-buttons (twitter/share-button (System/getenv "FULL_SITE_URL") req)]] ]
97 | [:div.gist-description
98 | [:p "A ClojureScript-based HTML5 Canvas and SVG Graphics Playground, much like "
99 | [:a {:href "http://bl.ocks.org/"} "http://bl.ocks.org/"]
100 | " but specifically designed for showcasing small ClojuresScript code demos: The underlying agenda is to show how small simple functional programs can generate complex behaviour."]
101 | [:p "This page shows some of the gists we know about. Why not add yours?"]]
102 |
103 | [:div.gallery-parent
104 | (ribbon "Fork me on GitHub!" "https://github.com/rm-hull/programming-enchiladas")
105 | (pmap (comp gallery-panel fetch-gist) picked-items)]])))
106 |
107 | (defn img-resource [[mime-type filename]]
108 | (if (fs/exists? (io/file filename))
109 | (->
110 | (file-response filename)
111 | (content-type mime-type))))
112 |
113 | (defn fetch-image [id]
114 | (first
115 | (for [f [["image/png" (png-img-file {:id id})]
116 | ["image/jpg" (jpg-img-file {:id id})]
117 | ["image/png" "resources/public/images/coming-soon.png"]]
118 | :let [resource (img-resource f)]
119 | :when resource]
120 | resource)))
121 |
122 | (defroutes routes
123 | (GET "/robots.txt" [] (file-response "resources/private/robots.txt"))
124 | (GET "/_images/:id" [id] (fetch-image id))
125 | (GET "/" [] welcome))
126 |
--------------------------------------------------------------------------------
/system.properties:
--------------------------------------------------------------------------------
1 | java.runtime.version=1.7
2 |
--------------------------------------------------------------------------------