├── .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 | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rm-hull/programming-enchiladas/trend.png)](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 |
17 |
18 | 19 | 20 | 21 |
22 |
23 |
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;u0&&(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 | --------------------------------------------------------------------------------