├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── boot.properties ├── build.boot ├── dev-resources └── cljs-api.edn ├── dev ├── lambdax │ └── boot │ │ └── addons.clj └── macro_debug.clj ├── env ├── dev │ └── cljs │ │ └── cljs_repl_web │ │ └── config.cljs └── prod │ └── cljs │ └── cljs_repl_web │ └── config.cljs ├── resources └── public │ ├── android-chrome-144x144.png │ ├── android-chrome-192x192.png │ ├── android-chrome-36x36.png │ ├── android-chrome-48x48.png │ ├── android-chrome-72x72.png │ ├── android-chrome-96x96.png │ ├── apple-touch-icon-114x114.png │ ├── apple-touch-icon-120x120.png │ ├── apple-touch-icon-144x144.png │ ├── apple-touch-icon-152x152.png │ ├── apple-touch-icon-180x180.png │ ├── apple-touch-icon-57x57.png │ ├── apple-touch-icon-60x60.png │ ├── apple-touch-icon-72x72.png │ ├── apple-touch-icon-76x76.png │ ├── apple-touch-icon-precomposed.png │ ├── apple-touch-icon.png │ ├── browserconfig.xml │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon-96x96.png │ ├── favicon.ico │ ├── index.html │ ├── js │ └── clojure-parinfer.js │ ├── manifest.json │ ├── mstile-144x144.png │ ├── mstile-150x150.png │ ├── mstile-310x150.png │ ├── mstile-310x310.png │ ├── mstile-70x70.png │ ├── safari-pinned-tab.svg │ └── styles │ ├── css │ ├── app.css │ ├── chosen-sprite.png │ ├── chosen-sprite@2x.png │ ├── console.css │ ├── material-design-iconic-font.min.css │ ├── re-com.css │ └── re-console-code-mirror.css │ ├── fonts │ ├── Material-Design-Iconic-Font.eot │ ├── Material-Design-Iconic-Font.svg │ ├── Material-Design-Iconic-Font.ttf │ ├── Material-Design-Iconic-Font.woff │ └── Material-Design-Iconic-Font.woff2 │ └── images │ ├── cljs.svg │ ├── header-bg.png │ ├── loading_cljs.png │ ├── logo.png │ └── replumb_logo_img.png ├── scripts ├── brepl ├── brepl.bat ├── brepl.clj ├── build ├── build.bat ├── build.clj ├── node_repl ├── node_repl.bat ├── node_repl.clj ├── release ├── release.bat ├── release.clj ├── repl ├── repl.bat ├── repl.clj ├── sftp-deploy-prod ├── sftp-deploy-test ├── watch ├── watch.bat └── watch.clj ├── src ├── clj │ └── cljs_api │ │ ├── generator.clj │ │ └── utils.cljc └── cljs │ └── cljs_repl_web │ ├── app.cljs │ ├── cljs_api.cljs │ ├── core.cljs │ ├── gist.cljs │ ├── handlers.cljs │ ├── io.cljs │ ├── localstorage.cljs │ ├── markdown.cljs │ ├── replumb_proxy.cljs │ ├── subs.cljs │ ├── views.cljs │ └── views │ └── utils.cljs ├── test └── cljs │ └── cljs_repl_web │ ├── console │ └── cljs_test.cljs │ ├── core_test.cljs │ └── suite.cljs └── version.properties /.gitignore: -------------------------------------------------------------------------------- 1 | pom.xml 2 | pom.xml.asc 3 | *jar 4 | *log 5 | lib/ 6 | classes/ 7 | target/ 8 | checkouts/ 9 | .lein-deps-sum 10 | .lein-repl-history 11 | .lein-plugins/ 12 | .lein-failures 13 | .nrepl-port 14 | .nrepl-history 15 | .repl/ 16 | figwheel_server.log 17 | out/ 18 | *.orig 19 | .projectile 20 | .dir-locals.el 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: clojure 2 | sudo: false 3 | jdk: 4 | - oraclejdk8 5 | install: 6 | - wget -O boot.jar https://github.com/boot-clj/boot/releases/download/2.6.0-SNAPSHOT/boot.jar 7 | - java -jar boot.jar -V 8 | script: java -jar boot.jar test 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 1.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 4 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 5 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a) in the case of the initial Contributor, the initial code and documentation 12 | distributed under this Agreement, and 13 | b) in the case of each subsequent Contributor: 14 | i) changes to the Program, and 15 | ii) additions to the Program; 16 | 17 | where such changes and/or additions to the Program originate from and are 18 | distributed by that particular Contributor. A Contribution 'originates' 19 | from a Contributor if it was added to the Program by such Contributor 20 | itself or anyone acting on such Contributor's behalf. Contributions do not 21 | include additions to the Program which: (i) are separate modules of 22 | software distributed in conjunction with the Program under their own 23 | license agreement, and (ii) are not derivative works of the Program. 24 | 25 | "Contributor" means any person or entity that distributes the Program. 26 | 27 | "Licensed Patents" mean patent claims licensable by a Contributor which are 28 | necessarily infringed by the use or sale of its Contribution alone or when 29 | combined with the Program. 30 | 31 | "Program" means the Contributions distributed in accordance with this 32 | Agreement. 33 | 34 | "Recipient" means anyone who receives the Program under this Agreement, 35 | including all Contributors. 36 | 37 | 2. GRANT OF RIGHTS 38 | a) Subject to the terms of this Agreement, each Contributor hereby grants 39 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 40 | reproduce, prepare derivative works of, publicly display, publicly 41 | perform, distribute and sublicense the Contribution of such Contributor, 42 | if any, and such derivative works, in source code and object code form. 43 | b) Subject to the terms of this Agreement, each Contributor hereby grants 44 | Recipient a non-exclusive, worldwide, royalty-free patent license under 45 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 46 | transfer the Contribution of such Contributor, if any, in source code and 47 | object code form. This patent license shall apply to the combination of 48 | the Contribution and the Program if, at the time the Contribution is 49 | added by the Contributor, such addition of the Contribution causes such 50 | combination to be covered by the Licensed Patents. The patent license 51 | shall not apply to any other combinations which include the Contribution. 52 | No hardware per se is licensed hereunder. 53 | c) Recipient understands that although each Contributor grants the licenses 54 | to its Contributions set forth herein, no assurances are provided by any 55 | Contributor that the Program does not infringe the patent or other 56 | intellectual property rights of any other entity. Each Contributor 57 | disclaims any liability to Recipient for claims brought by any other 58 | entity based on infringement of intellectual property rights or 59 | otherwise. As a condition to exercising the rights and licenses granted 60 | hereunder, each Recipient hereby assumes sole responsibility to secure 61 | any other intellectual property rights needed, if any. For example, if a 62 | third party patent license is required to allow Recipient to distribute 63 | the Program, it is Recipient's responsibility to acquire that license 64 | before distributing the Program. 65 | d) Each Contributor represents that to its knowledge it has sufficient 66 | copyright rights in its Contribution, if any, to grant the copyright 67 | license set forth in this Agreement. 68 | 69 | 3. REQUIREMENTS 70 | 71 | A Contributor may choose to distribute the Program in object code form under 72 | its own license agreement, provided that: 73 | 74 | a) it complies with the terms and conditions of this Agreement; and 75 | b) its license agreement: 76 | i) effectively disclaims on behalf of all Contributors all warranties 77 | and conditions, express and implied, including warranties or 78 | conditions of title and non-infringement, and implied warranties or 79 | conditions of merchantability and fitness for a particular purpose; 80 | ii) effectively excludes on behalf of all Contributors all liability for 81 | damages, including direct, indirect, special, incidental and 82 | consequential damages, such as lost profits; 83 | iii) states that any provisions which differ from this Agreement are 84 | offered by that Contributor alone and not by any other party; and 85 | iv) states that source code for the Program is available from such 86 | Contributor, and informs licensees how to obtain it in a reasonable 87 | manner on or through a medium customarily used for software exchange. 88 | 89 | When the Program is made available in source code form: 90 | 91 | a) it must be made available under this Agreement; and 92 | b) a copy of this Agreement must be included with each copy of the Program. 93 | Contributors may not remove or alter any copyright notices contained 94 | within the Program. 95 | 96 | Each Contributor must identify itself as the originator of its Contribution, 97 | if 98 | any, in a manner that reasonably allows subsequent Recipients to identify the 99 | originator of the Contribution. 100 | 101 | 4. COMMERCIAL DISTRIBUTION 102 | 103 | Commercial distributors of software may accept certain responsibilities with 104 | respect to end users, business partners and the like. While this license is 105 | intended to facilitate the commercial use of the Program, the Contributor who 106 | includes the Program in a commercial product offering should do so in a manner 107 | which does not create potential liability for other Contributors. Therefore, 108 | if a Contributor includes the Program in a commercial product offering, such 109 | Contributor ("Commercial Contributor") hereby agrees to defend and indemnify 110 | every other Contributor ("Indemnified Contributor") against any losses, 111 | damages and costs (collectively "Losses") arising from claims, lawsuits and 112 | other legal actions brought by a third party against the Indemnified 113 | Contributor to the extent caused by the acts or omissions of such Commercial 114 | Contributor in connection with its distribution of the Program in a commercial 115 | product offering. The obligations in this section do not apply to any claims 116 | or Losses relating to any actual or alleged intellectual property 117 | infringement. In order to qualify, an Indemnified Contributor must: 118 | a) promptly notify the Commercial Contributor in writing of such claim, and 119 | b) allow the Commercial Contributor to control, and cooperate with the 120 | Commercial Contributor in, the defense and any related settlement 121 | negotiations. The Indemnified Contributor may participate in any such claim at 122 | its own expense. 123 | 124 | For example, a Contributor might include the Program in a commercial product 125 | offering, Product X. That Contributor is then a Commercial Contributor. If 126 | that Commercial Contributor then makes performance claims, or offers 127 | warranties related to Product X, those performance claims and warranties are 128 | such Commercial Contributor's responsibility alone. Under this section, the 129 | Commercial Contributor would have to defend claims against the other 130 | Contributors related to those performance claims and warranties, and if a 131 | court requires any other Contributor to pay any damages as a result, the 132 | Commercial Contributor must pay those damages. 133 | 134 | 5. NO WARRANTY 135 | 136 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN 137 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 138 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each 140 | Recipient is solely responsible for determining the appropriateness of using 141 | and distributing the Program and assumes all risks associated with its 142 | exercise of rights under this Agreement , including but not limited to the 143 | risks and costs of program errors, compliance with applicable laws, damage to 144 | or loss of data, programs or equipment, and unavailability or interruption of 145 | operations. 146 | 147 | 6. DISCLAIMER OF LIABILITY 148 | 149 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 150 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 151 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 152 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 153 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 154 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 155 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 156 | OF SUCH DAMAGES. 157 | 158 | 7. GENERAL 159 | 160 | If any provision of this Agreement is invalid or unenforceable under 161 | applicable law, it shall not affect the validity or enforceability of the 162 | remainder of the terms of this Agreement, and without further action by the 163 | parties hereto, such provision shall be reformed to the minimum extent 164 | necessary to make such provision valid and enforceable. 165 | 166 | If Recipient institutes patent litigation against any entity (including a 167 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 168 | (excluding combinations of the Program with other software or hardware) 169 | infringes such Recipient's patent(s), then such Recipient's rights granted 170 | under Section 2(b) shall terminate as of the date such litigation is filed. 171 | 172 | All Recipient's rights under this Agreement shall terminate if it fails to 173 | comply with any of the material terms or conditions of this Agreement and does 174 | not cure such failure in a reasonable period of time after becoming aware of 175 | such noncompliance. If all Recipient's rights under this Agreement terminate, 176 | Recipient agrees to cease use and distribution of the Program as soon as 177 | reasonably practicable. However, Recipient's obligations under this Agreement 178 | and any licenses granted by Recipient relating to the Program shall continue 179 | and survive. 180 | 181 | Everyone is permitted to copy and distribute copies of this Agreement, but in 182 | order to avoid inconsistency the Agreement is copyrighted and may only be 183 | modified in the following manner. The Agreement Steward reserves the right to 184 | publish new versions (including revisions) of this Agreement from time to 185 | time. No one other than the Agreement Steward has the right to modify this 186 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 187 | Eclipse Foundation may assign the responsibility to serve as the Agreement 188 | Steward to a suitable separate entity. Each new version of the Agreement will 189 | be given a distinguishing version number. The Program (including 190 | Contributions) may always be distributed subject to the version of the 191 | Agreement under which it was received. In addition, after a new version of the 192 | Agreement is published, Contributor may elect to distribute the Program 193 | (including its Contributions) under the new version. Except as expressly 194 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 195 | licenses to the intellectual property of any Contributor under this Agreement, 196 | whether expressly, by implication, estoppel or otherwise. All rights in the 197 | Program not expressly granted under this Agreement are reserved. 198 | 199 | This Agreement is governed by the laws of the State of New York and the 200 | intellectual property laws of the United States of America. No party to this 201 | Agreement will bring a legal action under this Agreement more than one year 202 | after the cause of action arose. Each party waives its rights to a jury trial in 203 | any resulting litigation. 204 | 205 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Clojure Web Repl ([clojurescript.io](http://www.clojurescript.io)) 2 | 3 | A re-frame app designed to embed a pure ClojureScript REPL in a web 4 | page. 5 | Plumbing kindly provided by 6 | 7 | Replumb Logo 8 | . 9 | 10 | This project also employs the following libraries, whose authors we thank: 11 | 12 | * [reagent](https://github.com/reagent-project/reagent) 13 | * [re-frame](https://github.com/Day8/re-frame) 14 | * [re-com](https://github.com/Day8/re-com) 15 | * [markdown-clj](https://github.com/yogthos/markdown-clj) 16 | * [hickory](https://github.com/davidsantiago/hickory) 17 | * [CodeMirror](https://github.com/codemirror/CodeMirror) 18 | * [showdown](https://github.com/showdownjs/showdown) 19 | * [enquire.js](https://github.com/WickyNilliams/enquire.js) 20 | * ...and more 21 | 22 | Last but not least, many kudos go to [@jaredly](https://github.com/jaredly) and 23 | his [Reepl](https://github.com/jaredly/reepl) with which we share the 24 | CodeMirror implementation (and yes, we released a separate component 25 | [library](https://github.com/Lambda-X/re-console) with it). 26 | 27 | ## Tooling 28 | 29 | We recently switched this project to [boot](http://boot-clj.com/) 30 | Boot Logo 31 | and we could not be more happy. 32 | 33 | ### Interactive workflow 34 | 35 | Type `boot dev` and then browse to [http://localhost:3000](http://localhost:3000). 36 | 37 | ### Build 38 | 39 | The same easy command is used to build the content directly, which will be 40 | materialized in the `target` folder: 41 | 42 | `boot build -t prod|dev target` 43 | 44 | The `build` command defaults to `prod` when called with no arguments. 45 | 46 | ### Deploy 47 | 48 | In order to deploy just follow the `build` task with `deploy-s3`: 49 | 50 | ``` 51 | export ... 52 | export ... 53 | boot build -t prod|dev deploy-s3 54 | ``` 55 | 56 | The environment variables to set up are listed 57 | [here](https://github.com/Lambda-X/cljs-repl-web/blob/devel/build.boot#L73) 58 | 59 | 60 | #### Config 61 | 62 | The `cljs-repl-web.config/defaults` var contains the configuration map: 63 | 64 | ``` 65 | {:name "Clojurescript.io Website" 66 | :production? true|false 67 | :base-path "root/" 68 | :core-cache-url "/my-cache/core.cljs.cache.aot.json") 69 | :src-paths ["/some/path1" "/some/path2")] 70 | :verbose-repl? true|false} 71 | ``` 72 | 73 | #### Serve files 74 | 75 | Sometimes it is useful to serve files from `target`, for instance to check if 76 | everything works fine, kind of simulating the deployment. With `boot` you don't 77 | need no more `python -m SimpleHTTPServer`, only: 78 | 79 | `boot serve -d target wait` 80 | 81 | Check `boot serve -h` for the other options. 82 | 83 | ## Testing 84 | 85 | Tests at the moment use PhantomJS but there are just a few. 86 | 87 | For headless tests you need first of all 88 | [PhantomJS](https://github.com/ariya/phantomjs/). Then you can: 89 | 90 | `boot test -t prod|dev` 91 | 92 | The `auto-test` tasks also provides automatic test execution on file change. 93 | 94 | Featuring [doo](https://github.com/bensu/doo). 95 | 96 | ## Other Resources 97 | 98 | * [CLJSJS](https://github.com/cljsjs/packages) 99 | * [Boot built-in tasks](https://github.com/boot-clj/boot/wiki/Built-in-Tasks) 100 | 101 | ### License 102 | 103 | Distributed under the Eclipse Public License, the same as Clojure. 104 | 105 | Copyright (C) 2015-16 Scalac Sp. z o.o. 106 | 107 | Scalac [scalac.io](http://scalac.io/?utm_source=scalac_github&utm_campaign=scalac1&utm_medium=web) is a full-stack team of great functional developers focused around Scala/Clojure backed by great frontend and mobile developers. 108 | 109 | On our [blog](http://blog.scalac.io/?utm_source=scalac_github&utm_campaign=scalac1&utm_medium=web) we share our knowledge with community on how to write great, clean code, how to work remotely and about our culture. 110 | -------------------------------------------------------------------------------- /boot.properties: -------------------------------------------------------------------------------- 1 | #http://boot-clj.com 2 | #Mon Feb 15 13:35:26 PST 2016 3 | BOOT_CLOJURE_NAME=org.clojure/clojure 4 | BOOT_CLOJURE_VERSION=1.7.0 5 | BOOT_VERSION=2.6.0-SNAPSHOT 6 | BOOT_EMIT_TARGET=no 7 | -------------------------------------------------------------------------------- /build.boot: -------------------------------------------------------------------------------- 1 | (def clojure-dep '[org.clojure/clojure "1.8.0"]) 2 | (def clojurescript-dep '[org.clojure/clojurescript "1.8.40"]) 3 | 4 | (set-env! 5 | :source-paths #{"dev"} 6 | :dependencies (conj '[;; Boot deps 7 | [adzerk/boot-cljs "1.7.228-1" :scope "test"] 8 | [pandeiro/boot-http "0.7.2" :scope "test"] 9 | [adzerk/boot-reload "0.4.4" :scope "test"] 10 | [degree9/boot-semver "1.2.4" :scope "test"] 11 | [replumb/boot-pack-source "0.1.2-1" :scope "test"] 12 | [confetti/confetti "0.1.2-SNAPSHOT" :scope "test"] 13 | [adzerk/env "0.3.0" :scope "test"] 14 | 15 | ;; Repl 16 | [adzerk/boot-cljs-repl "0.3.0" :scope "test"] 17 | [com.cemerick/piggieback "0.2.1" :scope "test"] 18 | [weasel "0.7.0" :scope "test"] 19 | [org.clojure/tools.nrepl "0.2.12" :scope "test"] 20 | 21 | ;; Tests 22 | [crisptrutski/boot-cljs-test "0.2.2-SNAPSHOT" :scope "test"] 23 | 24 | ;; App deps 25 | [org.clojure/core.async "0.2.374"] 26 | [reagent "0.6.0-SNAPSHOT"] 27 | [re-frame "0.7.0"] 28 | [replumb/replumb "0.2.2-SNAPSHOT"] 29 | [cljsjs/highlight "8.4-0"] 30 | [re-console "0.1.4-SNAPSHOT"] 31 | [re-com "0.8.1"] 32 | [cljs-ajax "0.5.1"] 33 | [hickory "0.5.4"] 34 | [cljsjs/showdown "0.4.0-1"] 35 | [org.clojure/tools.reader "1.0.0-alpha3"] 36 | [cljsjs/enquire "2.1.2-0"] 37 | [com.cemerick/piggieback "0.2.1"] 38 | [org.clojars.stumitchell/clairvoyant "0.2.0"] 39 | [binaryage/devtools "0.6.0"] 40 | [day8/re-frame-tracer "0.1.0-SNAPSHOT"] 41 | [cljsjs/codemirror "5.10.0-0"] 42 | [adzerk/cljs-console "0.1.1"] 43 | [re-complete "0.1.4-1-SNAPSHOT"]] 44 | clojure-dep clojurescript-dep)) 45 | 46 | (def pack-source-deps (conj '[[replumb/replumb "0.2.2-SNAPSHOT"] 47 | [org.clojure/tools.reader "1.0.0-alpha3"]] 48 | clojurescript-dep)) 49 | 50 | (def cljs-api-deps (conj '[[org.clojure/tools.reader "1.0.0-alpha3"] 51 | [endophile "0.1.2"] 52 | [markdown-clj "0.9.78"]] 53 | clojure-dep)) 54 | 55 | (require '[adzerk.boot-cljs :refer [cljs]] 56 | '[adzerk.boot-reload :refer [reload]] 57 | '[pandeiro.boot-http :refer [serve]] 58 | '[crisptrutski.boot-cljs-test :refer [test-cljs exit!]] 59 | '[adzerk.boot-cljs-repl :refer [cljs-repl start-repl]] 60 | '[boot-semver.core :refer :all] 61 | '[boot.pod :as pod] 62 | '[clojure.pprint :refer [pprint]] 63 | '[replumb.boot-pack-source :refer [pack-source]] 64 | '[confetti.boot-confetti :refer [create-site sync-bucket]] 65 | '[adzerk.env :as env] 66 | '[lambdax.boot.addons :as addons]) 67 | 68 | (def +version+ (get-version)) 69 | 70 | ;;;;;;;;;;;;;;;;;;;;;;; 71 | ;;; Env Variables ;;; 72 | ;;;;;;;;;;;;;;;;;;;;;;; 73 | 74 | (env/def 75 | AWS_BUCKET nil 76 | AWS_ACCESS_KEY nil 77 | AWS_SECRET_KEY nil 78 | AWS_CLOUDFRONT_ID nil) 79 | 80 | ;;;;;;;;;;;;;;;;;;;;;; 81 | ;;; Options ;;; 82 | ;;;;;;;;;;;;;;;;;;;;;; 83 | 84 | (task-options! pom {:project "cljs-repl-web" 85 | :version +version+} 86 | test-cljs {:js-env :phantom 87 | :out-file "phantom-tests.js"}) 88 | 89 | (def foreign-libs 90 | [{:file "resources/public/js/clojure-parinfer.js" 91 | :provides ["parinfer.codemirror.mode.clojure.clojure-parinfer"]}]) 92 | 93 | (def prod-compiler-options 94 | {:closure-defines {"goog.DEBUG" false 95 | "clairvoyant.core.devmode" false} 96 | :optimize-constants true 97 | :static-fns true 98 | :elide-asserts true 99 | :pretty-print false 100 | :source-map-timestamp true 101 | :dump-core false 102 | :parallel-build true 103 | :foreign-libs foreign-libs}) 104 | 105 | (def dev-compiler-options 106 | (merge prod-compiler-options 107 | {:closure-defines {"goog.DEBUG" true 108 | "clairvoyant.core.devmode" true}})) 109 | 110 | (defmulti options 111 | "Return the correct option map for the build, dispatching on identity" 112 | identity) 113 | 114 | (defmethod options :generators 115 | [selection] 116 | {:type :generator 117 | :env {:source-paths #{"dev" "src/clj"} 118 | :resource-paths #{"dev-resources"}}}) 119 | 120 | (defmethod options :dev 121 | [selection] 122 | {:type :dev 123 | :props {"CLJS_LOG_LEVEL" "DEBUG"} 124 | :env {:source-paths #{"src/clj" "src/cljs" "env/dev/cljs" "dev"} 125 | :resource-paths #{"resources/public/"}} 126 | :cljs {:source-map true 127 | :optimizations :simple 128 | :compiler-options dev-compiler-options} 129 | :test-cljs {:optimizations :simple 130 | :cljs-opts dev-compiler-options 131 | :suite-ns 'cljs-repl-web.suite}}) 132 | 133 | (defmethod options :prod 134 | [selection] 135 | {:type :prod 136 | :props {"CLJS_LOG_LEVEL" "INFO"} 137 | :env {:source-paths #{"src/clj" "src/cljs" "env/prod/cljs"} 138 | :resource-paths #{"resources/public/"}} 139 | :cljs {:optimizations :simple 140 | :compiler-options prod-compiler-options} 141 | :test-cljs {:optimizations :simple 142 | :cljs-opts prod-compiler-options 143 | :suite-ns 'cljs-repl-web.suite}}) 144 | 145 | (defn set-system-properties! 146 | "Set a system property for each entry in the map m." 147 | [m] 148 | (doseq [kv m] 149 | (System/setProperty (key kv) (val kv)))) 150 | 151 | (deftask version-file 152 | "A task that includes the version.properties file in the fileset." 153 | [] 154 | (with-pre-wrap [fileset] 155 | (boot.util/info "Add version.properties...\n") 156 | (-> fileset 157 | (add-resource (java.io.File. ".") :include #{#"^version\.properties$"}) 158 | commit!))) 159 | 160 | (declare add-cache add-cljs-source) 161 | 162 | ;;;;;;;;;;;;;;;;;; 163 | ;; MAIN TASKS ;; 164 | ;;;;;;;;;;;;;;;;;; 165 | 166 | (deftask build 167 | "Build the final artifact, if no type is passed in, it builds production." 168 | [t type VAL kw "The build type, either prod or dev"] 169 | (let [options (options (or type :prod))] 170 | (boot.util/info "Building %s profile...\n" (:type options)) 171 | (apply set-env! (reduce #(into %2 %1) [] (:env options))) 172 | (set-system-properties! (:props options)) 173 | (comp (version-file) 174 | (apply cljs (reduce #(into %2 %1) [] (:cljs options))) 175 | (sift :include #{#"main.out"} 176 | :invert true) 177 | (add-cljs-source) 178 | (add-cache :dir "js-cache")))) 179 | 180 | (deftask dev 181 | "Start the dev interactive environment." 182 | [] 183 | (boot.util/info "Starting interactive dev...\n") 184 | (let [options (options :dev)] 185 | (apply set-env! (reduce #(into %2 %1) [] (:env options))) 186 | (set-system-properties! (:props options)) 187 | (comp (version-file) 188 | (watch) 189 | (cljs-repl) 190 | (reload :on-jsload 'cljs-repl-web.core/main) 191 | (apply cljs (reduce #(into %2 %1) [] (:cljs options))) 192 | (add-cljs-source) 193 | (add-cache :dir "js-cache") 194 | (serve)))) 195 | 196 | ;;;;;;;;;;;;;;;;;;;;; 197 | ;; TEST (please) ;; 198 | ;;;;;;;;;;;;;;;;;;;;; 199 | 200 | ;; This prevents a name collision WARNING between the test task and 201 | ;; clojure.core/test, a function that nobody really uses or cares 202 | ;; about. 203 | (ns-unmap 'boot.user 'test) 204 | 205 | (defn test-cljs-opts 206 | [options namespaces exit?] 207 | (cond-> options 208 | namespaces (-> (update-in [:test-cljs :suite-ns] (fn [_] nil)) 209 | (assoc-in [:test-cljs :namespaces] namespaces)) 210 | exit? (assoc-in [:test-cljs :exit?] exit?))) 211 | 212 | (defn set-test-env! 213 | [options] 214 | (apply set-env! (reduce #(into %2 %1) [] (update-in (:env options) [:source-paths] conj "test/cljs")))) 215 | 216 | (deftask test 217 | "Run tests once. 218 | 219 | If no type is passed in, it tests against the production build. It 220 | optionally accepts (a set of) regular expressions that are used for testing 221 | only some namespaces." 222 | [t type VAL kw "The build type, either prod or dev" 223 | n namespace NAMESPACE #{regex} "Namespace regex to test against"] 224 | (let [options (-> (options (or type :prod)) 225 | (test-cljs-opts namespace true))] 226 | (boot.util/info "Testing options %s\n" (with-out-str (pprint options))) 227 | (set-test-env! options) 228 | (apply test-cljs (reduce #(into %2 %1) [] (:test-cljs options))))) 229 | 230 | (deftask auto-test 231 | "Run tests watching for file changes. 232 | 233 | If no type is passed in, it tests against the production build. It optionally 234 | accepts (a set of) regular expressions that are used for testing only some 235 | namespaces." 236 | [t type VAL kw "The build type, either prod or dev" 237 | n namespace NAMESPACE #{regex} "Namespace regex to test against"] 238 | (let [options (-> (options (or type :prod)) 239 | (test-cljs-opts namespace false))] 240 | (set-test-env! options) 241 | (comp (watch) 242 | (apply test-cljs (reduce #(into %2 %1) [] (:test-cljs options)))))) 243 | 244 | ;;;;;;;;;;;;;;;;;;; 245 | ;; OTHER TASKS ;; 246 | ;;;;;;;;;;;;;;;;;;; 247 | 248 | (deftask add-cljs-source 249 | [] 250 | (comp (with-pre-wrap [fs] 251 | (boot.util/info "Pack source files...\n") 252 | fs) 253 | (pack-source :to-dir "cljs-src" 254 | :deps (into #{} pack-source-deps) 255 | :exclusions '#{org.clojure/clojure 256 | org.mozilla/rhino}))) 257 | 258 | (deftask deploy-s3 259 | [y dry-run bool "Run dryly :)" 260 | p prune bool "Delete files from S3 bucket not in the current fileset"] 261 | (let [bucket (get (env/env) "AWS_BUCKET")] 262 | (boot.util/info "Deploying on bucket %s...\n" bucket) 263 | (sync-bucket :dry-run dry-run 264 | :prune prune 265 | :bucket bucket 266 | :access-key (get (env/env) "AWS_ACCESS_KEY") 267 | :secret-key (get (env/env) "AWS_SECRET_KEY") 268 | :cloudfront-id (get (env/env) "AWS_CLOUDFRONT_ID")))) 269 | 270 | (deftask cljs-api 271 | "The task generates the Clojurescript API and the cljs-repl-web.cljs-api 272 | namespace. 273 | 274 | It does NOT add it to the fileset, but calls cljs-api.generator/-main and 275 | dumps in src/cljs (hard coded). It should not be part of the build pipeline 276 | unless there is a ClojureScript version change, in which case it should be 277 | executed once: 278 | 279 | # boot cljs-api" 280 | [] 281 | (let [custom-env (:env (options :generators)) 282 | pod-env (-> (get-env) 283 | (assoc :dependencies cljs-api-deps) 284 | (update :directories concat 285 | (:source-paths custom-env) 286 | (:resource-paths custom-env))) 287 | pod (future (pod/make-pod pod-env))] 288 | (with-pass-thru fs 289 | (boot.util/info "Generating cljs-api...\n") 290 | (pod/with-eval-in @pod 291 | (require 'cljs-api.generator) 292 | (cljs-api.generator/-main))))) 293 | 294 | (def dump-cache-deps '[[boot/core "2.6.0-SNAPSHOT"] 295 | [com.cognitect/transit-clj "0.8.285"]]) 296 | 297 | (deftask transit-jsonify 298 | "Materializes the transit+json file resulting from the input transit file path. 299 | 300 | The new file will be added to the fileset root and it is up to the next tasks 301 | to move it to the proper place (use sift --move for this)." 302 | [p transit-path PATH str "The fileset path to the cache file in transit format" 303 | n json-name NAME str "The name of the transit+json file"] 304 | (let [custom-env (:env (options :generators)) 305 | pod-env (-> (get-env) 306 | (assoc :dependencies dump-cache-deps) 307 | (update :directories concat 308 | (:source-paths custom-env) 309 | (:resource-paths custom-env))) 310 | pod (future (pod/make-pod pod-env))] 311 | (dbug "transit-path %s - json-name %s\n" transit-path json-name) 312 | (with-pre-wrap fs 313 | (commit! 314 | (let [tmp-dir (tmp-dir!) 315 | input-path (->> transit-path (tmp-get fs) (tmp-file) (.getPath)) 316 | out-path (str (addons/normalize-path (.getPath tmp-dir)) json-name)] 317 | (let [new-fs (if-let [transit-json (pod/with-eval-in @pod 318 | (require '[lambdax.boot.addons :as addons]) 319 | (.getPath (addons/transit-json ~input-path ~out-path)))] 320 | (do (dbug "Conversion produced %s\n" transit-json) 321 | (add-resource fs tmp-dir)) 322 | (do (warn "Could not perform Transit/Json conversion, skipping...\n") 323 | fs))] 324 | (pod/destroy-pod @pod) ;; AR - ugly, I need to find a better way 325 | new-fs)))))) 326 | 327 | (deftask add-cache 328 | "The task fetches the core.cljs.cache.aot file from your .m2, and materializes it on the classpath. 329 | 330 | It is added to the filese so it should be part of the build pipeline: 331 | 332 | $ boot build add-cache target" 333 | [d dir PATH str "The dir path where to dump the cljs.core cache file"] 334 | (assert dir "The dir param cannot be nil") 335 | (let [dir (addons/normalize-path dir) 336 | cache-json-name "core.cljs.cache.aot.json" 337 | cache-fs-path "cljs/core.cljs.cache.aot.edn" 338 | cache-fs-path-regex (re-pattern cache-fs-path)] 339 | (comp (with-pass-thru fs 340 | (info "Adding cljs.core cache to %s...\n" dir)) 341 | (sift :add-jar {(first clojurescript-dep) cache-fs-path-regex}) 342 | (transit-jsonify :transit-path cache-fs-path :json-name cache-json-name) 343 | (sift :include #{cache-fs-path-regex} :invert true) 344 | (sift :move {(re-pattern cache-json-name) (str dir cache-json-name)})))) 345 | -------------------------------------------------------------------------------- /dev/lambdax/boot/addons.clj: -------------------------------------------------------------------------------- 1 | (ns lambdax.boot.addons 2 | {:boot/export-tasks true} 3 | (:require [boot.core :as core :refer [boot]] 4 | [boot.pod :as pod] 5 | [boot.util :as util] 6 | [clojure.java.io :as io] 7 | [cognitect.transit :as transit]) 8 | (:import [java.io ByteArrayOutputStream])) 9 | 10 | (defn transit-json 11 | "Convert a transit file to a transit json file. 12 | Returns the new java.io.File on completion or nil if it fails." 13 | [src-path out-path] 14 | (try 15 | (let [cache (read-string (slurp (io/file src-path))) 16 | out (ByteArrayOutputStream. 1000000)] 17 | (transit/write (transit/writer out :json) cache) 18 | (doto (io/file out-path) 19 | (spit (.toString out "UTF-8")))) 20 | (catch Exception e 21 | (.printStackTrace e *err*) 22 | nil))) 23 | 24 | (defn normalize-path 25 | "Adds a / if missing at the end of the path." 26 | [path] 27 | (str path (when-not (= "/" (last path)) "/"))) 28 | -------------------------------------------------------------------------------- /dev/macro_debug.clj: -------------------------------------------------------------------------------- 1 | (ns ^{:doc 2 | "Thanks to Michael Bradley's following file we are able to debug macros in Clojurescript 3 | https://gist.github.com/michaelsbradleyjr/7509505"} 4 | macro-debug 5 | (:require [cljs.analyzer :as cljs] 6 | clojure.walk)) 7 | 8 | (declare ap 9 | cljs-macroexpand* 10 | cljs-macroexpand-1* 11 | cljs-macroexpand-all* 12 | 13 | cljs-macroexpand 14 | cljs-macroexpand-1 15 | cljs-macroexpand-all 16 | debug-* 17 | 18 | ->compiler-console 19 | ->js-log 20 | 21 | debug-*->* 22 | debug->cons 23 | debug-1->cons 24 | debug-all->cons 25 | debug->js 26 | debug-1->js 27 | debug-all->js 28 | defalias 29 | 30 | debug 31 | debug-1 32 | debug-all 33 | expand-symbol 34 | ppstring 35 | prccon) 36 | 37 | (def ^:dynamic *out-fn* nil) 38 | 39 | ;; macros and supporting funcs for macro dev and debug in ClojureScript projects 40 | ;; ----------------------------------------------------------------------------- 41 | 42 | (defmacro ^{:private true} ap 43 | [f] 44 | `(apply ~f (conj 'args '&env))) 45 | 46 | (defn cljs-macroexpand* 47 | [env form] 48 | (let [ex (cljs-macroexpand-1* env form)] 49 | (if (identical? ex form) 50 | form 51 | (recur env ex)))) 52 | 53 | (defn cljs-macroexpand-1* 54 | [env form] 55 | (cljs/macroexpand-1 env form)) 56 | 57 | (defn cljs-macroexpand-all* 58 | [env form] 59 | (clojure.walk/prewalk (fn [x] (if (seq? x) (cljs-macroexpand* env x) x)) form)) 60 | 61 | (defmacro cljs-macroexpand 62 | [& args] 63 | (ap cljs-macroexpand*)) 64 | 65 | (defmacro cljs-macroexpand-1 66 | [& args] 67 | (ap cljs-macroexpand-1*)) 68 | 69 | (defmacro cljs-macroexpand-all 70 | [& args] 71 | (ap cljs-macroexpand-all*)) 72 | 73 | (defn- debug-* 74 | [env which expander args] 75 | (let [args (if (< (count args) 2) (conj args nil) args) 76 | [descrip form] args 77 | expanded (expander env form) 78 | expstrng (str "\n" 79 | (str "DESCRIPTION: " (or descrip "(no description supplied)")) 80 | "\n" 81 | "\n" 82 | (str "BEFORE " which 83 | "\n" 84 | "\n" 85 | (ppstring form)) 86 | "\n" 87 | (str "AFTER " which 88 | "\n" 89 | "\n" 90 | (ppstring expanded)) 91 | "\n")] 92 | (*out-fn* expstrng))) 93 | 94 | (defn- ->compiler-console 95 | [val] 96 | `(do ~(prccon val))) 97 | 98 | (defn- ->js-log 99 | [val] 100 | `(do (.log js/console ~val))) 101 | 102 | (defmacro ^{:private true} debug-*->* 103 | [which out-fn] 104 | (list 'binding ['*out-fn* out-fn] 105 | (list 'debug-* '&env which (symbol (str "cljs-" which "*")) 'args))) 106 | 107 | (defmacro debug->cons 108 | [& args] 109 | (debug-*->* "macroexpand" ->compiler-console)) 110 | 111 | (defmacro debug-1->cons 112 | [& args] 113 | (debug-*->* "macroexpand-1" ->compiler-console)) 114 | 115 | (defmacro debug-all->cons 116 | [& args] 117 | (debug-*->* "macroexpand-all" ->compiler-console)) 118 | 119 | (defmacro debug->js 120 | [& args] 121 | (debug-*->* "macroexpand" ->js-log)) 122 | 123 | (defmacro debug-1->js 124 | [& args] 125 | (debug-*->* "macroexpand-1" ->js-log)) 126 | 127 | (defmacro debug-all->js 128 | [& args] 129 | (debug-*->* "macroexpand-all" ->js-log)) 130 | 131 | (defmacro ^{:private true} defalias 132 | ([name orig] 133 | `(do 134 | (alter-meta! 135 | (if (.hasRoot (var ~orig)) 136 | (def ~name (.getRawRoot (var ~orig))) 137 | (def ~name)) 138 | #(conj (dissoc % :macro) 139 | (apply dissoc (meta (var ~orig)) (remove #{:macro} (keys %))))) 140 | (var ~name))) 141 | ([name orig doc] 142 | (list `defalias (with-meta name (assoc (meta name) :doc doc)) orig))) 143 | 144 | (defalias debug debug->cons) 145 | 146 | (defalias debug-1 debug-1->cons) 147 | 148 | (defalias debug-all debug-all->cons) 149 | 150 | (defn expand-symbol 151 | "Resolves a symbol to a namespaced symbol, relative to some environment as 152 | obtained with &env within a macro definition." 153 | [env sym] 154 | (:name (cljs/resolve-var env sym))) 155 | 156 | (defn ppstring 157 | [v] 158 | (with-out-str (clojure.pprint/pprint v))) 159 | 160 | (defn prccon 161 | "Prints to the compiler's console at compile time. Sprinkle as needed within 162 | macros and their supporting functions to facilitate debugging during 163 | development." 164 | [& args] 165 | (binding [*out* *err*] 166 | (apply println args))) 167 | -------------------------------------------------------------------------------- /env/dev/cljs/cljs_repl_web/config.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-repl-web.config) 2 | 3 | (def defaults 4 | (let [base-path ""] 5 | {:name "Clojurescript.io Website (DEV)" 6 | :production? false 7 | :base-path base-path 8 | :core-cache-url (str base-path "/js-cache/core.cljs.cache.aot.json") 9 | :src-paths [(str base-path "/cljs-src")] 10 | :version-path (str base-path "/version.properties") 11 | :verbose-repl? true})) 12 | -------------------------------------------------------------------------------- /env/prod/cljs/cljs_repl_web/config.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-repl-web.config) 2 | 3 | (def defaults 4 | (let [base-path ""] 5 | {:name "Clojurescript.io Website" 6 | :production? true 7 | :base-path base-path 8 | :core-cache-url (str base-path "/js-cache/core.cljs.cache.aot.json") 9 | :src-paths [(str base-path "/cljs-src")] 10 | :version-path (str base-path "/version.properties") 11 | :verbose-repl? true})) 12 | -------------------------------------------------------------------------------- /resources/public/android-chrome-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/android-chrome-144x144.png -------------------------------------------------------------------------------- /resources/public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /resources/public/android-chrome-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/android-chrome-36x36.png -------------------------------------------------------------------------------- /resources/public/android-chrome-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/android-chrome-48x48.png -------------------------------------------------------------------------------- /resources/public/android-chrome-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/android-chrome-72x72.png -------------------------------------------------------------------------------- /resources/public/android-chrome-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/android-chrome-96x96.png -------------------------------------------------------------------------------- /resources/public/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /resources/public/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /resources/public/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /resources/public/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /resources/public/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /resources/public/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /resources/public/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /resources/public/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /resources/public/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /resources/public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /resources/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/apple-touch-icon.png -------------------------------------------------------------------------------- /resources/public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | #2b5797 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /resources/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/favicon-16x16.png -------------------------------------------------------------------------------- /resources/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/favicon-32x32.png -------------------------------------------------------------------------------- /resources/public/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/favicon-96x96.png -------------------------------------------------------------------------------- /resources/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/favicon.ico -------------------------------------------------------------------------------- /resources/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Replumb REPL 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 51 | 52 | 53 |
54 | Fork me on GitHub 55 |
56 | 62 | 63 |
64 |
65 |

Loading parenthesis...

66 | ClojureScript 67 |
68 |
69 |
70 |
71 | 76 | 77 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /resources/public/js/clojure-parinfer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * To Parinfer developers, 3 | * 4 | * This is a syntax-highlighting mode for Clojure, copied from CodeMirror. 5 | * We modify it for Parinfer so that it dims the inferred parens at the end of a line. 6 | * (Search for Parinfer below for the relevant edit) 7 | * 8 | * For the purpose of extra-highlighting, we also modify it by tracking a previousToken 9 | * so we can highlight def'd symbols and symbols that are called to. Example: 10 | * 11 | * (def foo 123) (bar 123) 12 | * ^^^ ^^^ 13 | * |------------------|------------- highlighted as 'def' token type 14 | * 15 | * This Clojure mode also has logic for where to indent the cursor when pressing enter. 16 | * We do not modify this. 17 | * 18 | */ 19 | 20 | 21 | 22 | 23 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 24 | // Distributed under an MIT license: http://codemirror.net/LICENSE 25 | 26 | /** 27 | * Author: Hans Engel 28 | * Branched from CodeMirror's Scheme mode (by Koh Zi Han, based on implementation by Koh Zi Chun) 29 | */ 30 | 31 | (function(mod) { 32 | if (typeof exports == "object" && typeof module == "object") // CommonJS 33 | mod(require("../../lib/codemirror")); 34 | else if (typeof define == "function" && define.amd) // AMD 35 | define(["../../lib/codemirror"], mod); 36 | else // Plain browser env 37 | mod(CodeMirror); 38 | })(function(CodeMirror) { 39 | "use strict"; 40 | 41 | CodeMirror.defineMode("clojure-parinfer", function (options) { 42 | var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", CHARACTER = "string-2", 43 | ATOM = "atom", NUMBER = "number", BRACKET = "bracket", KEYWORD = "keyword", VAR = "variable", 44 | SOL = "sol", EOL = "eol", MOL = "mol", // close-paren styles 45 | DEF = "def"; 46 | var INDENT_WORD_SKIP = options.indentUnit || 2; 47 | var NORMAL_INDENT_UNIT = options.indentUnit || 2; 48 | 49 | function makeKeywords(str) { 50 | var obj = {}, words = str.split(" "); 51 | for (var i = 0; i < words.length; ++i) obj[words[i]] = true; 52 | return obj; 53 | } 54 | 55 | var atoms = makeKeywords("true false nil"); 56 | 57 | var defs = makeKeywords( 58 | "defn defn- def defonce defmulti defmethod defmacro defstruct deftype ns"); 59 | 60 | var keywords = makeKeywords( 61 | "defn defn- def def- defonce defmulti defmethod defmacro defstruct deftype defprotocol defrecord defproject deftest slice defalias defhinted defmacro- defn-memo defnk defnk defonce- defunbound defunbound- defvar defvar- let letfn do case cond condp for loop recur when when-not when-let when-first if if-let if-not . .. -> ->> doto and or dosync doseq dotimes dorun doall load import unimport ns in-ns refer try catch finally throw with-open with-local-vars binding gen-class gen-and-load-class gen-and-save-class handler-case handle"); 62 | 63 | var builtins = makeKeywords( 64 | "* *' *1 *2 *3 *agent* *allow-unresolved-vars* *assert* *clojure-version* *command-line-args* *compile-files* *compile-path* *compiler-options* *data-readers* *e *err* *file* *flush-on-newline* *fn-loader* *in* *math-context* *ns* *out* *print-dup* *print-length* *print-level* *print-meta* *print-readably* *read-eval* *source-path* *unchecked-math* *use-context-classloader* *verbose-defrecords* *warn-on-reflection* + +' - -' -> ->> ->ArrayChunk ->Vec ->VecNode ->VecSeq -cache-protocol-fn -reset-methods .. / < <= = == > >= EMPTY-NODE accessor aclone add-classpath add-watch agent agent-error agent-errors aget alength alias all-ns alter alter-meta! alter-var-root amap ancestors and apply areduce array-map aset aset-boolean aset-byte aset-char aset-double aset-float aset-int aset-long aset-short assert assoc assoc! assoc-in associative? atom await await-for await1 bases bean bigdec bigint biginteger binding bit-and bit-and-not bit-clear bit-flip bit-not bit-or bit-set bit-shift-left bit-shift-right bit-test bit-xor boolean boolean-array booleans bound-fn bound-fn* bound? butlast byte byte-array bytes case cast char char-array char-escape-string char-name-string char? chars chunk chunk-append chunk-buffer chunk-cons chunk-first chunk-next chunk-rest chunked-seq? class class? clear-agent-errors clojure-version coll? comment commute comp comparator compare compare-and-set! compile complement concat cond condp conj conj! cons constantly construct-proxy contains? count counted? create-ns create-struct cycle dec dec' decimal? declare default-data-readers definline definterface defmacro defmethod defmulti defn defn- defonce defprotocol defrecord defstruct deftype delay delay? deliver denominator deref derive descendants destructure disj disj! dissoc dissoc! distinct distinct? doall dorun doseq dosync dotimes doto double double-array doubles drop drop-last drop-while empty empty? ensure enumeration-seq error-handler error-mode eval even? every-pred every? ex-data ex-info extend extend-protocol extend-type extenders extends? false? ffirst file-seq filter filterv find find-keyword find-ns find-protocol-impl find-protocol-method find-var first flatten float float-array float? floats flush fn fn? fnext fnil for force format frequencies future future-call future-cancel future-cancelled? future-done? future? gen-class gen-interface gensym get get-in get-method get-proxy-class get-thread-bindings get-validator group-by hash hash-combine hash-map hash-set identical? identity if-let if-not ifn? import in-ns inc inc' init-proxy instance? int int-array integer? interleave intern interpose into into-array ints io! isa? iterate iterator-seq juxt keep keep-indexed key keys keyword keyword? last lazy-cat lazy-seq let letfn line-seq list list* list? load load-file load-reader load-string loaded-libs locking long long-array longs loop macroexpand macroexpand-1 make-array make-hierarchy map map-indexed map? mapcat mapv max max-key memfn memoize merge merge-with meta method-sig methods min min-key mod munge name namespace namespace-munge neg? newline next nfirst nil? nnext not not-any? not-empty not-every? not= ns ns-aliases ns-imports ns-interns ns-map ns-name ns-publics ns-refers ns-resolve ns-unalias ns-unmap nth nthnext nthrest num number? numerator object-array odd? or parents partial partition partition-all partition-by pcalls peek persistent! pmap pop pop! pop-thread-bindings pos? pr pr-str prefer-method prefers primitives-classnames print print-ctor print-dup print-method print-simple print-str printf println println-str prn prn-str promise proxy proxy-call-with-super proxy-mappings proxy-name proxy-super push-thread-bindings pvalues quot rand rand-int rand-nth range ratio? rational? rationalize re-find re-groups re-matcher re-matches re-pattern re-seq read read-line read-string realized? reduce reduce-kv reductions ref ref-history-count ref-max-history ref-min-history ref-set refer refer-clojure reify release-pending-sends rem remove remove-all-methods remove-method remove-ns remove-watch repeat repeatedly replace replicate require reset! reset-meta! resolve rest restart-agent resultset-seq reverse reversible? rseq rsubseq satisfies? second select-keys send send-off seq seq? seque sequence sequential? set set-error-handler! set-error-mode! set-validator! set? short short-array shorts shuffle shutdown-agents slurp some some-fn sort sort-by sorted-map sorted-map-by sorted-set sorted-set-by sorted? special-symbol? spit split-at split-with str string? struct struct-map subs subseq subvec supers swap! symbol symbol? sync take take-last take-nth take-while test the-ns thread-bound? time to-array to-array-2d trampoline transient tree-seq true? type unchecked-add unchecked-add-int unchecked-byte unchecked-char unchecked-dec unchecked-dec-int unchecked-divide-int unchecked-double unchecked-float unchecked-inc unchecked-inc-int unchecked-int unchecked-long unchecked-multiply unchecked-multiply-int unchecked-negate unchecked-negate-int unchecked-remainder-int unchecked-short unchecked-subtract unchecked-subtract-int underive unquote unquote-splicing update-in update-proxy use val vals var-get var-set var? vary-meta vec vector vector-of vector? when when-first when-let when-not while with-bindings with-bindings* with-in-str with-loading-context with-local-vars with-meta with-open with-out-str with-precision with-redefs with-redefs-fn xml-seq zero? zipmap *default-data-reader-fn* as-> cond-> cond->> reduced reduced? send-via set-agent-send-executor! set-agent-send-off-executor! some-> some->>"); 65 | 66 | var indentKeys = makeKeywords( 67 | // Built-ins 68 | "ns fn def defn defmethod bound-fn if if-not case condp when while when-not when-first do future comment doto locking proxy with-open with-precision reify deftype defrecord defprotocol extend extend-protocol extend-type try catch " + 69 | 70 | // Binding forms 71 | "let letfn binding loop for doseq dotimes when-let if-let " + 72 | 73 | // Data structures 74 | "defstruct struct-map assoc " + 75 | 76 | // clojure.test 77 | "testing deftest " + 78 | 79 | // contrib 80 | "handler-case handle dotrace deftrace"); 81 | 82 | var tests = { 83 | digit: /\d/, 84 | digit_or_colon: /[\d:]/, 85 | hex: /[0-9a-f]/i, 86 | sign: /[+-]/, 87 | exponent: /e/i, 88 | keyword_char: /[^\s\(\[\;\)\]]/, 89 | symbol: /[\w*+!\-\._?:<>\/\xa1-\uffff]/ 90 | }; 91 | 92 | function stateStack(indent, type, prev) { // represents a state stack object 93 | this.indent = indent; 94 | this.type = type; 95 | this.prev = prev; 96 | } 97 | 98 | function pushStack(state, indent, type) { 99 | state.indentStack = new stateStack(indent, type, state.indentStack); 100 | } 101 | 102 | function popStack(state) { 103 | state.indentStack = state.indentStack.prev; 104 | } 105 | 106 | function isNumber(ch, stream){ 107 | // hex 108 | if ( ch === '0' && stream.eat(/x/i) ) { 109 | stream.eatWhile(tests.hex); 110 | return true; 111 | } 112 | 113 | // leading sign 114 | if ( ( ch == '+' || ch == '-' ) && ( tests.digit.test(stream.peek()) ) ) { 115 | stream.eat(tests.sign); 116 | ch = stream.next(); 117 | } 118 | 119 | if ( tests.digit.test(ch) ) { 120 | stream.eat(ch); 121 | stream.eatWhile(tests.digit); 122 | 123 | if ( '.' == stream.peek() ) { 124 | stream.eat('.'); 125 | stream.eatWhile(tests.digit); 126 | } 127 | 128 | if ( stream.eat(tests.exponent) ) { 129 | stream.eat(tests.sign); 130 | stream.eatWhile(tests.digit); 131 | } 132 | 133 | return true; 134 | } 135 | 136 | return false; 137 | } 138 | 139 | // Eat character that starts after backslash \ 140 | function eatCharacter(stream) { 141 | var first = stream.next(); 142 | // Read special literals: backspace, newline, space, return. 143 | // Just read all lowercase letters. 144 | if (first && first.match(/[a-z]/) && stream.match(/[a-z]+/, true)) { 145 | return; 146 | } 147 | // Read unicode character: \u1000 \uA0a1 148 | if (first === "u") { 149 | stream.match(/[0-9a-z]{4}/i, true); 150 | } 151 | } 152 | 153 | return { 154 | startState: function () { 155 | return { 156 | previousToken: null, 157 | indentStack: null, 158 | indentation: 0, 159 | mode: false, 160 | atStart: false 161 | }; 162 | }, 163 | 164 | token: function (stream, state) { 165 | 166 | if (stream.sol()) { 167 | state.atStart = (state.mode != "string"); 168 | } 169 | 170 | if (state.indentStack == null && stream.sol()) { 171 | // update indentation, but only if indentStack is empty 172 | state.indentation = stream.indentation(); 173 | } 174 | 175 | // skip spaces 176 | if (stream.eatSpace()) { 177 | return null; 178 | } 179 | var returnType = null; 180 | var previousToken = null; 181 | 182 | switch(state.mode){ 183 | case "string": // multi-line string parsing mode 184 | var next, escaped = false; 185 | while ((next = stream.next()) != null) { 186 | if (next == "\"" && !escaped) { 187 | 188 | state.mode = false; 189 | break; 190 | } 191 | escaped = !escaped && next == "\\"; 192 | } 193 | returnType = STRING; // continue on in string mode 194 | break; 195 | default: // default parsing mode 196 | var ch = stream.next(); 197 | 198 | if (ch == "\"") { 199 | state.mode = "string"; 200 | returnType = STRING; 201 | } else if (ch == "\\") { 202 | eatCharacter(stream); 203 | returnType = CHARACTER; 204 | } else if (ch == "'" && !( tests.digit_or_colon.test(stream.peek()) )) { 205 | returnType = ATOM; 206 | } else if (ch == ";") { // comment 207 | stream.skipToEnd(); // rest of the line is a comment 208 | returnType = COMMENT; 209 | } else if (isNumber(ch,stream)){ 210 | returnType = NUMBER; 211 | } else if (ch == "(" || ch == "[" || ch == "{" ) { 212 | if (ch == "(") { 213 | previousToken = "("; 214 | } 215 | 216 | var keyWord = '', indentTemp = stream.column(), letter; 217 | /** 218 | Either 219 | (indent-word .. 220 | (non-indent-word .. 221 | (;something else, bracket, etc. 222 | */ 223 | 224 | if (ch == "(") while ((letter = stream.eat(tests.keyword_char)) != null) { 225 | keyWord += letter; 226 | } 227 | 228 | if (keyWord.length > 0 && (indentKeys.propertyIsEnumerable(keyWord) || 229 | /^(?:def|with)/.test(keyWord))) { // indent-word 230 | pushStack(state, indentTemp + INDENT_WORD_SKIP, ch); 231 | } else { // non-indent word 232 | // we continue eating the spaces 233 | stream.eatSpace(); 234 | if (stream.eol() || stream.peek() == ";") { 235 | // nothing significant after 236 | // we restart indentation the user defined spaces after 237 | pushStack(state, indentTemp + NORMAL_INDENT_UNIT, ch); 238 | } else { 239 | pushStack(state, indentTemp + stream.current().length, ch); // else we match 240 | } 241 | } 242 | stream.backUp(stream.current().length - 1); // undo all the eating 243 | 244 | returnType = BRACKET; 245 | } else if (ch == ")" || ch == "]" || ch == "}") { 246 | returnType = BRACKET; 247 | 248 | // Parinfer: (style trailing delimiters) 249 | stream.eatWhile(/[\s,\]})]/); 250 | if (stream.eol() || stream.peek() == ";") { 251 | returnType += " " + EOL; 252 | } else if (state.atStart) { 253 | returnType += " " + SOL; 254 | } else { 255 | returnType += " " + MOL; 256 | } 257 | stream.backUp(stream.current().length - 1); 258 | 259 | if (state.indentStack != null && state.indentStack.type == (ch == ")" ? "(" : (ch == "]" ? "[" :"{"))) { 260 | popStack(state); 261 | } 262 | } else if ( ch == ":" ) { 263 | stream.eatWhile(tests.symbol); 264 | return ATOM; 265 | } else { 266 | stream.eatWhile(tests.symbol); 267 | 268 | if (keywords && keywords.propertyIsEnumerable(stream.current())) { 269 | returnType = KEYWORD; 270 | } else if (builtins && builtins.propertyIsEnumerable(stream.current())) { 271 | returnType = BUILTIN; 272 | } else if (atoms && atoms.propertyIsEnumerable(stream.current())) { 273 | returnType = ATOM; 274 | } else if (state.previousToken == "def" || state.previousToken == "(") { 275 | returnType = DEF; 276 | } else { 277 | returnType = VAR; 278 | } 279 | 280 | if (state.previousToken == "(" && defs && defs.propertyIsEnumerable(stream.current())) { 281 | previousToken = "def"; 282 | } 283 | } 284 | 285 | if (!(ch == ")" || ch == "]" || ch == "}")) { 286 | state.atStart = false; 287 | } 288 | } 289 | 290 | state.previousToken = previousToken; 291 | 292 | return returnType; 293 | }, 294 | 295 | indent: function (state) { 296 | if (state.indentStack == null) return state.indentation; 297 | return state.indentStack.indent; 298 | }, 299 | 300 | closeBrackets: {pairs: "()[]{}\"\""}, 301 | lineComment: ";;" 302 | }; 303 | }); 304 | 305 | CodeMirror.defineMIME("text/x-clojure", "clojure"); 306 | 307 | }); 308 | -------------------------------------------------------------------------------- /resources/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "replumb", 3 | "icons": [ 4 | { 5 | "src": "\/android-chrome-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image\/png", 8 | "density": "0.75" 9 | }, 10 | { 11 | "src": "\/android-chrome-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image\/png", 14 | "density": "1.0" 15 | }, 16 | { 17 | "src": "\/android-chrome-72x72.png", 18 | "sizes": "72x72", 19 | "type": "image\/png", 20 | "density": "1.5" 21 | }, 22 | { 23 | "src": "\/android-chrome-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image\/png", 26 | "density": "2.0" 27 | }, 28 | { 29 | "src": "\/android-chrome-144x144.png", 30 | "sizes": "144x144", 31 | "type": "image\/png", 32 | "density": "3.0" 33 | }, 34 | { 35 | "src": "\/android-chrome-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image\/png", 38 | "density": "4.0" 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /resources/public/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/mstile-144x144.png -------------------------------------------------------------------------------- /resources/public/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/mstile-150x150.png -------------------------------------------------------------------------------- /resources/public/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/mstile-310x150.png -------------------------------------------------------------------------------- /resources/public/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/mstile-310x310.png -------------------------------------------------------------------------------- /resources/public/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/mstile-70x70.png -------------------------------------------------------------------------------- /resources/public/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /resources/public/styles/css/app.css: -------------------------------------------------------------------------------- 1 | body,html { 2 | font-family: "Roboto"; 3 | background: #87D7AE; 4 | } 5 | 6 | .app-main { 7 | padding: 0 20px 0 20px; 8 | margin: 0; 9 | } 10 | 11 | /* :narrow */ 12 | @media only screen and (max-width: 480px) { 13 | .app-main { 14 | padding: 0 2px 0 2px; 15 | } 16 | } 17 | 18 | .page-width { 19 | margin: auto auto; 20 | } 21 | 22 | #app-center { 23 | position: absolute; 24 | top: 180px; 25 | width: 100%; 26 | min-height: 170px; 27 | height: 170px; 28 | 29 | } 30 | 31 | #app-bottom { 32 | margin-top: 18px; 33 | margin-bottom: 30px; 34 | padding-top: 160px; 35 | } 36 | 37 | /* :medium */ 38 | @media only screen and (min-width: 481px) and (max-width: 960px) { 39 | #app-center { 40 | height: 130px; 41 | min-height: 130px; 42 | } 43 | } 44 | 45 | /* :narrow */ 46 | @media only screen and (max-width: 480px) { 47 | #app-center { 48 | height: 85px; 49 | min-height: 85px; 50 | top: 100px; 51 | } 52 | #app-bottom { 53 | padding-top: 140px; 54 | } 55 | } 56 | 57 | /*************** loading *****************/ 58 | 59 | #loading-div { 60 | color: #FFFFFF; 61 | position: relative; 62 | width: 100%; 63 | height: 100%; 64 | text-align: center; 65 | } 66 | 67 | .loading-h2 { 68 | position: absolute; 69 | margin: 0 auto; 70 | left: 0; 71 | right: 0; 72 | bottom: 70%; 73 | margin: 0; /* or bootstrap will add it */ 74 | color: #EEE; 75 | } 76 | 77 | #loading-div img { 78 | position: absolute; 79 | bottom: -1px; 80 | border: 0; 81 | margin: 0 auto; 82 | left: 0; 83 | right: 0; 84 | } 85 | 86 | /* :narrow */ 87 | @media only screen and (max-width: 480px) { 88 | .loading-h2 { 89 | font-size: 0.8em; 90 | bottom:90%; 91 | } 92 | 93 | #loading-div img { 94 | width: 48px; 95 | height: 48px; 96 | } 97 | } 98 | 99 | /* :medium */ 100 | @media only screen and (min-width: 481px) and (max-width: 960px) { 101 | .loading-h2 { 102 | font-size: 1.6em; 103 | bottom: 90%; 104 | } 105 | } 106 | 107 | /* 108 | https://css-tricks.com/snippets/css/keyframe-animation-syntax/ 109 | */ 110 | 111 | @-moz-keyframes spin { 112 | 25% { -moz-transform: translate(150px,0px) rotate(360deg); } 113 | 50% { -moz-transform: translate(0px,0px) rotate(0deg); } 114 | 75% { -moz-transform: translate(-150px,0px) rotate(-360deg); } 115 | 100% { -moz-transform: translate(0px,0px) rotate(0deg); } 116 | } 117 | 118 | @-webkit-keyframes spin { 119 | 25% { -webkit-transform: translate(150px,0px) rotate(360deg); } 120 | 50% { -webkit-transform: translate(0px,0px) rotate(0deg); } 121 | 75% { -webkit-transform: translate(-150px,0px) rotate(-360deg); } 122 | 100% { -webkit-transform: translate(0px,0px) rotate(0deg); } 123 | } 124 | 125 | @keyframes spin { 126 | 25% { transform: translate(150px,0px) rotate(360deg); } 127 | 50% { transform: translate(0px,0px) rotate(0deg); } 128 | 75% { transform: translate(-150px,0px) rotate(-360deg); } 129 | 100% { transform: translate(0px,0px) rotate(0deg); } 130 | } 131 | 132 | #loading-img { 133 | -webkit-animation: spin 16s linear infinite; 134 | -moz-animation: spin 16s linear infinite; 135 | animation: spin 16s linear infinite; 136 | } 137 | 138 | /*************** header *****************/ 139 | 140 | .page-header { 141 | margin: 0; 142 | padding: 20px 0px 10px 0px; 143 | background: #363636; 144 | text-align: center; 145 | border-bottom: 0; /* or bootstrap adds it */ 146 | border-bottom: 10px solid #FFFFFF; 147 | padding-bottom: 200px; 148 | background: #363636 url(../images/header-bg.png) no-repeat center bottom; 149 | } 150 | 151 | .page-header a { 152 | display: block; 153 | margin-top: 20px; 154 | } 155 | 156 | .page-header h1, .page-header h1 small { 157 | color: #FFFFFF; 158 | font-weight: 300; 159 | } 160 | 161 | .logo { 162 | height: 40px; 163 | background: url("../images/logo.png"); 164 | margin: auto auto; 165 | background-position: center; 166 | background-repeat: no-repeat; 167 | } 168 | 169 | /* :narrow */ 170 | @media only screen and (max-width: 480px) { 171 | .page-header { 172 | padding: 4px 0px 120px 0px; 173 | } 174 | 175 | .page-header a { 176 | display: block; 177 | margin-top: 2px; 178 | } 179 | 180 | .page-header h1 { 181 | font-size: 1.25em; 182 | margin: 0; 183 | } 184 | 185 | .page-header h1 small { 186 | display: none; 187 | visibility: hidden; 188 | } 189 | 190 | .logo { 191 | height: 40px; 192 | background: url("../images/replumb_logo_img.png"); 193 | margin: auto auto; 194 | background-position: center; 195 | background-repeat: no-repeat; 196 | } 197 | } 198 | 199 | @media only screen and (min-width: 481px) and (max-width: 960px) { 200 | .page-header { 201 | padding-bottom: 160px; 202 | } 203 | } 204 | 205 | /* This overrides hljs's style, therefore it must go after */ 206 | .hljs { 207 | display: inline; 208 | overflow-x: auto; 209 | padding: 0; 210 | border: 0; 211 | background: #FFFFFF; 212 | } 213 | 214 | .cljs-btn > i { 215 | color: #FFFFFF; 216 | } 217 | 218 | .rc-icon-disabled { 219 | opacity: 0.4; 220 | } 221 | 222 | /*************** footer *****************/ 223 | 224 | #app-footer-container { 225 | color: #FFFFFF; 226 | margin-top: 10px; 227 | background: #475b51; 228 | } 229 | 230 | .page-footer { 231 | padding: 15px; 232 | } 233 | 234 | 235 | 236 | .app-footer-btn { 237 | color: #FFFFFF; 238 | background-color: transparent; 239 | border-color: #FFFFFF; 240 | margin: 2px; 241 | padding: 4px 8px; 242 | } 243 | 244 | .app-footer-btn:hover { 245 | color: #65B58C; /* Needed for plain text like Blog */ 246 | background-color: #FFFFFF; 247 | -webkit-transition: all 500ms ease; 248 | -moz-transition: all 500ms ease; 249 | -ms-transition: all 500ms ease; 250 | transition: all 500ms ease; 251 | } 252 | 253 | .app-footer-btn:hover > div > i { 254 | color: #65B58C; 255 | background-color: #FFFFFF; 256 | -webkit-transition: all 500ms ease; 257 | -moz-transition: all 500ms ease; 258 | -ms-transition: all 500ms ease; 259 | transition: all 500ms ease; 260 | } 261 | 262 | /*********************/ 263 | /*** API Panel ***/ 264 | /*********************/ 265 | 266 | .rc-line { display: none } 267 | 268 | .api-panel-section { 269 | background-color: #65B58C; 270 | padding: 10px 10px 10px 15px; 271 | margin-bottom: 10px; 272 | border-radius: 10px; 273 | } 274 | 275 | .api-panel-popup-section-title { 276 | font-family: "Roboto"; 277 | text-align: center; 278 | text-transform: uppercase; 279 | } 280 | 281 | /* :wide */ 282 | .api-panel-section-title { 283 | font-family: "Roboto"; 284 | font-size: 1.5em; 285 | } 286 | 287 | /* :narrow */ 288 | @media only screen and (max-width: 480px) { 289 | /* Center the section title only when we display 1 column */ 290 | /* AR - changed to follow the other media queries */ 291 | .api-panel-section-title { 292 | text-align: center; 293 | display:block; 294 | } 295 | .api-panel-section { 296 | padding: 10px; 297 | } 298 | } 299 | 300 | /* :medium */ 301 | @media only screen and (min-width: 481px) and (max-width: 960px) { 302 | /* Center the section title only when we display 1 column */ 303 | /* AR - changed to follow the other media queries */ 304 | .api-panel-section-title { 305 | text-align: center; 306 | display:block; 307 | } 308 | } 309 | 310 | .api-panel-topic { 311 | text-transform: uppercase; 312 | font-family: "Roboto"; 313 | } 314 | 315 | /* problem here, see https://github.com/Day8/re-com/issues/76 */ 316 | .api-panel-topic-box > div { 317 | -webkit-flex: 0 1 auto !important; 318 | flex: 0 1 auto !important; 319 | } 320 | 321 | /* problem here, see https://github.com/Day8/re-com/issues/76 */ 322 | .api-panel-symbol-label-box > div { 323 | -webkit-flex: 0 1 auto !important; 324 | flex: 0 1 auto !important; 325 | } 326 | 327 | .api-panel-symbol-button { 328 | margin: 2px 2px; 329 | min-width: 50px; 330 | padding:5px; 331 | cursor:pointer; 332 | font-family: monospace; 333 | font-size:0.8125em; 334 | } 335 | 336 | .api-panel-symbol-label { 337 | font-weigth: bold; 338 | font-family: monospace; 339 | color: #FFFFFF; 340 | } 341 | 342 | .api-panel-signature { 343 | font-family: monospace; 344 | } 345 | 346 | /* problem here, see https://github.com/Day8/re-com/issues/76 */ 347 | .api-panel-button-send-repl-box > div { 348 | -webkit-flex: 1 0 auto !important; 349 | flex: 1 0 auto !important; 350 | margin: 0; 351 | } 352 | 353 | .api-panel-number-icon { 354 | border: 0; 355 | margin: 0; 356 | padding: 0; 357 | } 358 | 359 | .api-panel-send-repl-img { 360 | margin: 4px; 361 | -moz-margin-start: 6px; 362 | -webkit-margin-start: 6px; 363 | width: 28px; 364 | height: 28px; 365 | } 366 | 367 | .api-panel-button-send-repl { 368 | align-items: stretch !important; 369 | border: 0; 370 | } 371 | 372 | .api-panel-button-send-repl:hover { 373 | fill: #ACE63C; 374 | } 375 | 376 | .popover-content code { 377 | color: inherit; 378 | background-color: inherit; 379 | border: none; 380 | } 381 | 382 | /*********************/ 383 | /*** Tour ***/ 384 | /*********************/ 385 | 386 | 387 | .rc-v-box .rc-popover-anchor-wrapper .rc-point-wrapper { 388 | align-items: stretch !important; 389 | /*flex: 0 1 auto !important;*/ 390 | /*flex-flow: row !important;*/ 391 | } 392 | 393 | .rc-v-box .rc-popover-anchor-wrapper .rc-point-wrapper .rc-popover-point { 394 | align-self: center !important; 395 | } 396 | 397 | h1.tour-title { 398 | margin-top: 5px; 399 | color: rgba(68, 68, 68, 0.6); 400 | font-weight: bold; 401 | font-size: 17px; 402 | } 403 | 404 | ul.tour { 405 | -webkit-padding-start: 20px; 406 | padding-start: 20px; 407 | } 408 | 409 | .symbol { 410 | font-weight: bold; 411 | color: #a50; 412 | font-size: 110%; 413 | } 414 | 415 | .mode { 416 | display:inline-block; 417 | color: #a50; 418 | padding: 4px; 419 | font-size: 95%; 420 | border: 1px solid rgba(128, 128, 128, 0.15); 421 | border-radius: 5px; 422 | background-color: #eee; 423 | } 424 | 425 | a.tour { 426 | color:#337ab7; 427 | text-decoration: none; 428 | } 429 | 430 | /* Some examples contains tables in their markdown. 431 | * These tables have a class of "code-tbl-9bef6" 432 | * see: http://stackoverflow.com/questions/5110249/wildcard-in-css-for-classes 433 | */ 434 | 435 | table[class^="code-tbl-"], table[class*=" code-tbl-"] { 436 | width: 100%; 437 | } 438 | 439 | table[class^="code-tbl-"] td, table[class*=" code-tbl-"] td { 440 | padding-right: 5px; 441 | vertical-align: top; 442 | } 443 | 444 | .ribbon { 445 | background-color: #87D7AE; 446 | overflow: hidden; 447 | white-space: nowrap; 448 | /* top left corner */ 449 | position: absolute; 450 | width: 220px; 451 | left: -50px; 452 | top: 40px; 453 | /* 45 deg ccw rotation */ 454 | -webkit-transform: rotate(-45deg); 455 | -moz-transform: rotate(-45deg); 456 | -ms-transform: rotate(-45deg); 457 | -o-transform: rotate(-45deg); 458 | transform: rotate(-45deg); 459 | /* shadow */ 460 | -webkit-box-shadow: 0 0 10px #888; 461 | -moz-box-shadow: 0 0 10px #888; 462 | box-shadow: 0 0 10px #888; 463 | } 464 | 465 | .ribbon a { 466 | border: 1px solid #CCD7D1; 467 | color: #FFFFFF; 468 | display: block; 469 | font: bold 81.25% 'Roboto'; 470 | margin: 1px 0; 471 | padding: 10px 50px; 472 | text-align: center; 473 | text-decoration: none; 474 | /* shadow */ 475 | text-shadow: 0 0 5px #444; 476 | } 477 | 478 | /* :narrow */ 479 | @media only screen and (max-width: 480px) { 480 | .ribbon { 481 | width: 140px; 482 | left: -40px; 483 | top: 16px; 484 | } 485 | 486 | .ribbon a { 487 | padding: 6px 36px; 488 | font: bold 50% 'Roboto'; 489 | } 490 | } 491 | 492 | /* :medium */ 493 | @media only screen and (min-width: 481px) and (max-width: 960px) { 494 | .ribbon { 495 | width: 180px; 496 | left: -46px; 497 | top: 28px; 498 | } 499 | 500 | .ribbon a { 501 | padding: 6px 36px; 502 | } 503 | } 504 | 505 | /* override re-console style */ 506 | 507 | /* :wide */ 508 | .re-console-container { 509 | /* Note that this element must be explicity sized and positioned absolute or relative. */ 510 | position: relative; 511 | width: 660px; 512 | height: 300px; 513 | min-width: 300px; 514 | min-height: 186px; 515 | } 516 | 517 | .re-console-mode-line { 518 | font-size: 12px; 519 | width: 660px; 520 | min-width: 300px; 521 | } 522 | 523 | .popup-tour-console-anchor { 524 | width: 660px; 525 | min-width: 300px; 526 | height: 0; 527 | } 528 | 529 | /* :narrow */ 530 | @media only screen and (max-width: 480px) { 531 | .re-console-container { 532 | /* Note that this element must be explicity sized and positioned absolute or relative. */ 533 | position: relative; 534 | width: 300px; 535 | height: 180px; 536 | min-width: 120px; 537 | min-height: 80px; 538 | } 539 | 540 | .re-console-mode-line { 541 | font-size: 10px; 542 | width: 300px; 543 | min-width: 120px; 544 | } 545 | 546 | .popup-tour-console-anchor { 547 | width: 300px; 548 | min-width: 120px; 549 | } 550 | 551 | #app-bottom { 552 | margin-top: 28px; 553 | } 554 | 555 | .modal-tour-popup { 556 | width: 250px; 557 | } 558 | 559 | } 560 | 561 | /* :medium */ 562 | @media only screen and (min-width: 481px) and (max-width: 960px) { 563 | .re-console-container { 564 | /* Note that this element must be explicity sized and positioned absolute or relative. */ 565 | position: relative; 566 | width: 460px; 567 | height: 280px; 568 | min-width: 220px; 569 | min-height: 75px; 570 | } 571 | 572 | .re-console-mode-line { 573 | width: 460px; 574 | min-width: 220px; 575 | } 576 | 577 | .popup-tour-console-anchor { 578 | width: 460px; 579 | min-width: 220px; 580 | } 581 | } 582 | 583 | /* Styles for auto-complete */ 584 | 585 | ul.re-completion-list { 586 | position: absolute; 587 | margin: 0; 588 | padding: 0; 589 | /* left: 364px; */ 590 | /* top: 26px; */ 591 | min-height: 24px; 592 | max-height: 123px; 593 | width: 220px; 594 | overflow-y: scroll; 595 | border-radius: 5px; 596 | background-color: white; 597 | border: 1px solid #65B58C; 598 | } 599 | 600 | ul.re-completion-list li { 601 | font-family: Consolas,monospace; 602 | font-size: inherit; 603 | margin: 0; 604 | padding: 0 4px; 605 | list-style: none; 606 | white-space: pre; 607 | word-wrap: normal; 608 | line-height: inherit; 609 | color: inherit; 610 | cursor: pointer; 611 | } 612 | 613 | ul.re-completion-list li:first-of-type { 614 | padding-top: 2px; 615 | } 616 | 617 | ul.re-completion-list li:last-of-type { 618 | padding-bottom: 2px; 619 | } 620 | 621 | li.re-completion-selected { 622 | background-color: #EEE; 623 | } 624 | 625 | -------------------------------------------------------------------------------- /resources/public/styles/css/chosen-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/styles/css/chosen-sprite.png -------------------------------------------------------------------------------- /resources/public/styles/css/chosen-sprite@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/styles/css/chosen-sprite@2x.png -------------------------------------------------------------------------------- /resources/public/styles/css/console.css: -------------------------------------------------------------------------------- 1 | .jqconsole { 2 | padding: 5px; 3 | } 4 | 5 | .jqconsole-prompt { 6 | color: black; 7 | } 8 | 9 | .jqconsole-old-prompt { 10 | opacity: 0.4; 11 | font-weight: normal; 12 | } 13 | 14 | .jqconsole-output { 15 | color: dark-grey; 16 | } 17 | 18 | .jqconsole-return { 19 | color: #2c4010; 20 | } 21 | 22 | .jqconsole-error { 23 | color: #CB2531; 24 | } 25 | 26 | /* The cursor. */ 27 | .jqconsole-cursor { 28 | font-weight: normal; 29 | font-family: monospace; 30 | background-color: #96ca4b; 31 | } 32 | 33 | /* The cursor color when the console looses focus. */ 34 | .jqconsole-blurred .jqconsole-cursor { 35 | font-weight: normal; 36 | font-family: monospace; 37 | background-color: #b2ca8f; 38 | } 39 | 40 | .jqconsole-prompt-text > code { 41 | margin-right: 0px; 42 | background-color: transparent; 43 | } 44 | 45 | /* Additional elements */ 46 | .cljs-buttons-container { 47 | margin-top: 5px; 48 | text-align: center; 49 | } 50 | 51 | .jqconsole-button { 52 | margin: 0px 2px; 53 | } 54 | 55 | /* Character matchings */ 56 | 57 | /* ( ) */ 58 | .match-round-brackets { 59 | color: red; 60 | } 61 | 62 | /* [ ] */ 63 | .match-square-brackets { 64 | color: green; 65 | } 66 | 67 | /* { } */ 68 | .match-curly-brackets { 69 | color: blue; 70 | } 71 | 72 | /* " " */ 73 | .match-string { 74 | color: yellow; 75 | } 76 | 77 | .jqconsole-ansi-bold { 78 | font-weight: bold!important; 79 | } 80 | 81 | .jqconsole-ansi-lighter { 82 | font-weight: lighter!important; 83 | } 84 | 85 | .jqconsole-ansi-italic { 86 | font-style: italic!important; 87 | } 88 | 89 | .jqconsole-ansi-underline { 90 | text-decoration: underline!important; 91 | } 92 | 93 | @-webkit-keyframes blinker { 94 | from { opacity: 1.0; } 95 | to { opacity: 0.0; } 96 | } 97 | 98 | @-moz-keyframes blinker { 99 | from { opacity: 1.0; } 100 | to { opacity: 0.0; } 101 | } 102 | 103 | @-ms-keyframes blinker { 104 | from { opacity: 1.0; } 105 | to { opacity: 0.0; } 106 | } 107 | 108 | @-o-keyframes blinker { 109 | from { opacity: 1.0; } 110 | to { opacity: 0.0; } 111 | } 112 | 113 | .jqconsole-ansi-blink { 114 | -webkit-animation-name: blinker; 115 | -moz-animation-name: blinker; 116 | -ms-animation-name: blinker; 117 | -o-animation-name: blinker; 118 | -webkit-animation-iteration-count: infinite; 119 | -moz-animation-iteration-count: infinite; 120 | -ms-animation-iteration-count: infinite; 121 | -o-animation-iteration-count: infinite; 122 | -webkit-animation-timing-function: cubic-bezier(1.0,0,0,1.0); 123 | -ms-animation-timing-function: cubic-bezier(1.0,0,0,1.0); 124 | -o-animation-timing-function: cubic-bezier(1.0,0,0,1.0); 125 | -moz-animation-timing-function: cubic-bezier(1.0,0,0,1.0); 126 | -webkit-animation-duration: 1s; 127 | -moz-animation-duration: 1s; 128 | -o-animation-duration: 1s; 129 | -ms-animation-duration: 1s; 130 | } 131 | 132 | .jqconsole-ansi-blink-rapid { 133 | -webkit-animation-name: blinker; 134 | -moz-animation-name: blinker; 135 | -ms-animation-name: blinker; 136 | -o-animation-name: blinker; 137 | -webkit-animation-iteration-count: infinite; 138 | -moz-animation-iteration-count: infinite; 139 | -ms-animation-iteration-count: infinite; 140 | -o-animation-iteration-count: infinite; 141 | -webkit-animation-timing-function: cubic-bezier(1.0,0,0,1.0); 142 | -ms-animation-timing-function: cubic-bezier(1.0,0,0,1.0); 143 | -o-animation-timing-function: cubic-bezier(1.0,0,0,1.0); 144 | -moz-animation-timing-function: cubic-bezier(1.0,0,0,1.0); 145 | -webkit-animation-duration: 0.5s; 146 | -moz-animation-duration: 0.5s; 147 | -o-animation-duration: 0.5s; 148 | -ms-animation-duration: 0.5s; 149 | } 150 | 151 | 152 | .jqconsole-ansi-hidden { 153 | visibility:hidden!important; 154 | } 155 | 156 | .jqconsole-ansi-line-through { 157 | text-decoration: line-through; 158 | } 159 | 160 | .jqconsole-ansi-fonts-1 { 161 | 162 | } 163 | .jqconsole-ansi-fonts-2 { 164 | 165 | } 166 | .jqconsole-ansi-fonts-3 { 167 | 168 | } 169 | .jqconsole-ansi-fonts-4 { 170 | 171 | } 172 | .jqconsole-ansi-fonts-5 { 173 | 174 | } 175 | .jqconsole-ansi-fonts-6 { 176 | 177 | } 178 | .jqconsole-ansi-fonts-7 { 179 | 180 | } 181 | .jqconsole-ansi-fonts-8 { 182 | 183 | } 184 | .jqconsole-ansi-fonts-9 { 185 | 186 | } 187 | 188 | .jqconsole-ansi-fraktur { 189 | 190 | } 191 | 192 | .jqconsole-ansi-color-black { 193 | color: black!important; 194 | } 195 | .jqconsole-ansi-color-red { 196 | color: red!important; 197 | } 198 | .jqconsole-ansi-color-green { 199 | color: green!important; 200 | } 201 | .jqconsole-ansi-color-yellow { 202 | color: yellow!important; 203 | } 204 | .jqconsole-ansi-color-blue { 205 | color: blue!important; 206 | } 207 | .jqconsole-ansi-color-magenta { 208 | color: magenta!important; 209 | } 210 | .jqconsole-ansi-color-cyan { 211 | color: cyan!important; 212 | } 213 | .jqconsole-ansi-color-white { 214 | color: white!important; 215 | } 216 | 217 | .jqconsole-ansi-background-color-black { 218 | background-color: black!important; 219 | } 220 | .jqconsole-ansi-background-color-red { 221 | background-color: red!important; 222 | } 223 | .jqconsole-ansi-background-color-green { 224 | background-color: green!important; 225 | } 226 | .jqconsole-ansi-background-color-yellow { 227 | background-color: yellow!important; 228 | } 229 | .jqconsole-ansi-background-color-blue { 230 | background-color: blue!important; 231 | } 232 | .jqconsole-ansi-background-color-magenta { 233 | background-color: magenta!important; 234 | } 235 | .jqconsole-ansi-background-color-cyan { 236 | background-color: cyan!important; 237 | } 238 | .jqconsole-ansi-background-color-white { 239 | background-color: white!important; 240 | } 241 | 242 | .jqconsole-ansi-framed { 243 | border: 1px solid!important; 244 | } 245 | .jqconsole-ansi-overline { 246 | text-decoration: overline!important; 247 | } 248 | -------------------------------------------------------------------------------- /resources/public/styles/css/re-com.css: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------------------- 2 | re-com.css 3 | 4 | This file is required for styling the re-com UI library components. 5 | To use the library, bootstrap.css is also required, which is best obtained from a CDN. 6 | So, place the following lines in the section of the html file: 7 | 8 | 9 | 10 | 11 | The following is required for constraining an application to the 12 | height of the browser window and setting some global defaults like font... 13 | ----------------------------------------------------------------------------------------*/ 14 | 15 | /* The following style addresses: http://stackoverflow.com/questions/28636832/firefox-overflow-y-not-working-with-nested-flexbox */ 16 | * { 17 | min-height: 0px; 18 | min-width: 0px; 19 | } 20 | 21 | html, body { 22 | font-family: Segoe UI, Roboto, sans-serif; 23 | font-size: 14px; 24 | font-weight: 400; 25 | color: rgba(68, 68, 68, 1.0); 26 | letter-spacing: 0.002em; 27 | height: 100%; 28 | margin: 0px; 29 | padding: 0px; 30 | } 31 | 32 | /*---------------------------------------------------------------------------------------- 33 | Bootstrap overrides after upgrading to 3.3.5 34 | ----------------------------------------------------------------------------------------*/ 35 | 36 | .popover, .tooltip { 37 | font-family: Segoe UI, Roboto, sans-serif; 38 | } 39 | 40 | .popover-title { 41 | font-weight: normal; 42 | } 43 | 44 | .form-control-feedback { 45 | pointer-events: initial; 46 | } 47 | 48 | 49 | /*---------------------------------------------------------------------------------------- 50 | The section immediately below is required for the drop-down components and comes from 51 | the bootstrap-chosen library: 52 | 53 | https://github.com/alxlit/bootstrap-chosen 54 | License: MIT. 55 | 56 | That library provides it's css as a .less file. 57 | To compile the less file into the css below, use the following steps from 58 | a command prompt (you'll need git and the lessc compiler (http://lesscss.org): 59 | 60 | git clone https://github.com/alxlit/bootstrap-chosen 61 | 62 | cd bootstrap-chosen 63 | 64 | git clone --depth=1 https://github.com/twbs/bootstrap 65 | 66 | Now edit bootstrap-chosen.less and add the following two lines before the first @import: 67 | @import "bootstrap/less/variables.less"; 68 | @import "bootstrap/less/mixins.less"; 69 | 70 | lessc bootstrap-chosen.less > bootstrap-chosen.css 71 | 72 | Finally, replace the section below with the contents of bootstrap-chosen.css. 73 | 74 | START OF BOOTSTRAP-CHOSEN SECTION... 75 | ----------------------------------------------------------------------------------------*/ 76 | 77 | .chosen-select { 78 | width: 100%; 79 | } 80 | .chosen-select-deselect { 81 | width: 100%; 82 | } 83 | .chosen-container { 84 | display: inline-block; 85 | font-size: 14px; 86 | position: relative; 87 | vertical-align: middle; 88 | } 89 | .chosen-container .chosen-drop { 90 | background: #ffffff; 91 | border: 1px solid #cccccc; 92 | border-bottom-right-radius: 4px; 93 | border-bottom-left-radius: 4px; 94 | -webkit-box-shadow: 0 8px 8px rgba(0, 0, 0, .25); 95 | box-shadow: 0 8px 8px rgba(0, 0, 0, .25); 96 | margin-top: -1px; 97 | position: absolute; 98 | top: 100%; 99 | left: -9000px; 100 | z-index: 1060; 101 | } 102 | .chosen-container.chosen-with-drop .chosen-drop { 103 | left: 0; 104 | right: 0; 105 | } 106 | .chosen-container .chosen-results { 107 | color: #555555; 108 | margin: 0 4px 4px 0; 109 | max-height: 240px; 110 | padding: 0 0 0 4px; 111 | position: relative; 112 | overflow-x: hidden; 113 | overflow-y: auto; 114 | -webkit-overflow-scrolling: touch; 115 | } 116 | .chosen-container .chosen-results li { 117 | display: none; 118 | line-height: 1.42857143; 119 | list-style: none; 120 | margin: 0; 121 | padding: 5px 6px; 122 | } 123 | .chosen-container .chosen-results li em { 124 | background: #feffde; 125 | font-style: normal; 126 | } 127 | .chosen-container .chosen-results li.group-result { 128 | display: list-item; 129 | cursor: default; 130 | color: #999; 131 | font-weight: bold; 132 | } 133 | .chosen-container .chosen-results li.group-option { 134 | padding-left: 15px; 135 | } 136 | .chosen-container .chosen-results li.active-result { 137 | cursor: pointer; 138 | display: list-item; 139 | } 140 | .chosen-container .chosen-results li.highlighted { 141 | background-color: #428bca; 142 | background-image: none; 143 | color: white; 144 | } 145 | .chosen-container .chosen-results li.highlighted em { 146 | background: transparent; 147 | } 148 | .chosen-container .chosen-results li.disabled-result { 149 | display: list-item; 150 | color: #777777; 151 | } 152 | .chosen-container .chosen-results .no-results { 153 | background: #eeeeee; 154 | display: list-item; 155 | } 156 | .chosen-container .chosen-results-scroll { 157 | background: white; 158 | margin: 0 4px; 159 | position: absolute; 160 | text-align: center; 161 | width: 321px; 162 | z-index: 1; 163 | } 164 | .chosen-container .chosen-results-scroll span { 165 | display: inline-block; 166 | height: 1.42857143; 167 | text-indent: -5000px; 168 | width: 9px; 169 | } 170 | .chosen-container .chosen-results-scroll-down { 171 | bottom: 0; 172 | } 173 | .chosen-container .chosen-results-scroll-down span { 174 | background: url("chosen-sprite.png") no-repeat -4px -3px; 175 | } 176 | .chosen-container .chosen-results-scroll-up span { 177 | background: url("chosen-sprite.png") no-repeat -22px -3px; 178 | } 179 | .chosen-container-single .chosen-single { 180 | background-color: #ffffff; 181 | -webkit-background-clip: padding-box; 182 | -moz-background-clip: padding; 183 | background-clip: padding-box; 184 | border: 1px solid #cccccc; 185 | border-top-right-radius: 4px; 186 | border-top-left-radius: 4px; 187 | border-bottom-right-radius: 4px; 188 | border-bottom-left-radius: 4px; 189 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); 190 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); 191 | color: #555555; 192 | display: block; 193 | height: 34px; 194 | overflow: hidden; 195 | line-height: 34px; 196 | padding: 0 0 0 8px; 197 | position: relative; 198 | text-decoration: none; 199 | white-space: nowrap; 200 | } 201 | .chosen-container-single .chosen-single span { 202 | display: block; 203 | margin-right: 26px; 204 | overflow: hidden; 205 | text-overflow: ellipsis; 206 | white-space: nowrap; 207 | } 208 | .chosen-container-single .chosen-single abbr { 209 | background: url("chosen-sprite.png") right top no-repeat; 210 | display: block; 211 | font-size: 1px; 212 | height: 10px; 213 | position: absolute; 214 | right: 26px; 215 | top: 12px; 216 | width: 12px; 217 | } 218 | .chosen-container-single .chosen-single abbr:hover { 219 | background-position: right -11px; 220 | } 221 | .chosen-container-single .chosen-single.chosen-disabled .chosen-single abbr:hover { 222 | background-position: right 2px; 223 | } 224 | .chosen-container-single .chosen-single div { 225 | display: block; 226 | height: 100%; 227 | position: absolute; 228 | top: 0; 229 | right: 0; 230 | width: 18px; 231 | } 232 | .chosen-container-single .chosen-single div b { 233 | background: url("chosen-sprite.png") no-repeat 0 7px; 234 | display: block; 235 | height: 100%; 236 | width: 100%; 237 | } 238 | .chosen-container-single .chosen-default { 239 | color: #777777; 240 | } 241 | .chosen-container-single .chosen-search { 242 | margin: 0; 243 | padding: 3px 4px; 244 | position: relative; 245 | white-space: nowrap; 246 | z-index: 1000; 247 | } 248 | .chosen-container-single .chosen-search input[type="text"] { 249 | background: url("chosen-sprite.png") no-repeat 100% -20px, #ffffff; 250 | border: 1px solid #cccccc; 251 | border-top-right-radius: 4px; 252 | border-top-left-radius: 4px; 253 | border-bottom-right-radius: 4px; 254 | border-bottom-left-radius: 4px; 255 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); 256 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); 257 | margin: 1px 0; 258 | padding: 4px 20px 4px 4px; 259 | width: 100%; 260 | } 261 | .chosen-container-single .chosen-drop { 262 | margin-top: -1px; 263 | border-bottom-right-radius: 4px; 264 | border-bottom-left-radius: 4px; 265 | -webkit-background-clip: padding-box; 266 | -moz-background-clip: padding; 267 | background-clip: padding-box; 268 | } 269 | .chosen-container-single-nosearch .chosen-search input { 270 | position: absolute; 271 | left: -9000px; 272 | } 273 | .chosen-container-multi .chosen-choices { 274 | background-color: #ffffff; 275 | border: 1px solid #cccccc; 276 | border-top-right-radius: 4px; 277 | border-top-left-radius: 4px; 278 | border-bottom-right-radius: 4px; 279 | border-bottom-left-radius: 4px; 280 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); 281 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); 282 | cursor: text; 283 | height: auto !important; 284 | height: 1%; 285 | margin: 0; 286 | overflow: hidden; 287 | padding: 0; 288 | position: relative; 289 | } 290 | .chosen-container-multi .chosen-choices li { 291 | float: left; 292 | list-style: none; 293 | } 294 | .chosen-container-multi .chosen-choices .search-field { 295 | margin: 0; 296 | padding: 0; 297 | white-space: nowrap; 298 | } 299 | .chosen-container-multi .chosen-choices .search-field input[type="text"] { 300 | background: transparent !important; 301 | border: 0 !important; 302 | -webkit-box-shadow: none; 303 | box-shadow: none; 304 | color: #555555; 305 | height: 32px; 306 | margin: 0; 307 | padding: 4px; 308 | outline: 0; 309 | } 310 | .chosen-container-multi .chosen-choices .search-field .default { 311 | color: #999; 312 | } 313 | .chosen-container-multi .chosen-choices .search-choice { 314 | -webkit-background-clip: padding-box; 315 | -moz-background-clip: padding; 316 | background-clip: padding-box; 317 | background-color: #eeeeee; 318 | border: 1px solid #cccccc; 319 | border-top-right-radius: 4px; 320 | border-top-left-radius: 4px; 321 | border-bottom-right-radius: 4px; 322 | border-bottom-left-radius: 4px; 323 | background-image: -webkit-linear-gradient(top, #ffffff 0%, #eeeeee 100%); 324 | background-image: -o-linear-gradient(top, #ffffff 0%, #eeeeee 100%); 325 | background-image: linear-gradient(to bottom, #ffffff 0%, #eeeeee 100%); 326 | background-repeat: repeat-x; 327 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffeeeeee', GradientType=0); 328 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); 329 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); 330 | color: #333333; 331 | cursor: default; 332 | line-height: 13px; 333 | margin: 6px 0 3px 5px; 334 | padding: 3px 20px 3px 5px; 335 | position: relative; 336 | } 337 | .chosen-container-multi .chosen-choices .search-choice .search-choice-close { 338 | background: url("chosen-sprite.png") right top no-repeat; 339 | display: block; 340 | font-size: 1px; 341 | height: 10px; 342 | position: absolute; 343 | right: 4px; 344 | top: 5px; 345 | width: 12px; 346 | cursor: pointer; 347 | } 348 | .chosen-container-multi .chosen-choices .search-choice .search-choice-close:hover { 349 | background-position: right -11px; 350 | } 351 | .chosen-container-multi .chosen-choices .search-choice-focus { 352 | background: #d4d4d4; 353 | } 354 | .chosen-container-multi .chosen-choices .search-choice-focus .search-choice-close { 355 | background-position: right -11px; 356 | } 357 | .chosen-container-multi .chosen-results { 358 | margin: 0 0 0 0; 359 | padding: 0; 360 | } 361 | .chosen-container-multi .chosen-drop .result-selected { 362 | display: none; 363 | } 364 | .chosen-container-active .chosen-single { 365 | border: 1px solid #66afe9; 366 | -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .075) inset, 0 0 8px rgba(82, 168, 236, .6); 367 | box-shadow: 0 1px 1px rgba(0, 0, 0, .075) inset, 0 0 8px rgba(82, 168, 236, .6); 368 | -webkit-transition: border linear .2s, box-shadow linear .2s; 369 | -o-transition: border linear .2s, box-shadow linear .2s; 370 | transition: border linear .2s, box-shadow linear .2s; 371 | } 372 | .chosen-container-active.chosen-with-drop .chosen-single { 373 | background-color: #ffffff; 374 | border: 1px solid #66afe9; 375 | border-bottom-right-radius: 0; 376 | border-bottom-left-radius: 0; 377 | -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .075) inset, 0 0 8px rgba(82, 168, 236, .6); 378 | box-shadow: 0 1px 1px rgba(0, 0, 0, .075) inset, 0 0 8px rgba(82, 168, 236, .6); 379 | -webkit-transition: border linear .2s, box-shadow linear .2s; 380 | -o-transition: border linear .2s, box-shadow linear .2s; 381 | transition: border linear .2s, box-shadow linear .2s; 382 | } 383 | .chosen-container-active.chosen-with-drop .chosen-single div { 384 | background: transparent; 385 | border-left: none; 386 | } 387 | .chosen-container-active.chosen-with-drop .chosen-single div b { 388 | background-position: -18px 7px; 389 | } 390 | .chosen-container-active .chosen-choices { 391 | border: 1px solid #66afe9; 392 | border-bottom-right-radius: 0; 393 | border-bottom-left-radius: 0; 394 | -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .075) inset, 0 0 8px rgba(82, 168, 236, .6); 395 | box-shadow: 0 1px 1px rgba(0, 0, 0, .075) inset, 0 0 8px rgba(82, 168, 236, .6); 396 | -webkit-transition: border linear .2s, box-shadow linear .2s; 397 | -o-transition: border linear .2s, box-shadow linear .2s; 398 | transition: border linear .2s, box-shadow linear .2s; 399 | } 400 | .chosen-container-active .chosen-choices .search-field input[type="text"] { 401 | color: #111 !important; 402 | } 403 | .chosen-container-active.chosen-with-drop .chosen-choices { 404 | border-bottom-right-radius: 0; 405 | border-bottom-left-radius: 0; 406 | } 407 | .chosen-disabled { 408 | cursor: default; 409 | opacity: 0.5 !important; 410 | } 411 | .chosen-disabled .chosen-single { 412 | cursor: default; 413 | } 414 | .chosen-disabled .chosen-choices .search-choice .search-choice-close { 415 | cursor: default; 416 | } 417 | .chosen-rtl { 418 | text-align: right; 419 | } 420 | .chosen-rtl .chosen-single { 421 | padding: 0 8px 0 0; 422 | overflow: visible; 423 | } 424 | .chosen-rtl .chosen-single span { 425 | margin-left: 26px; 426 | margin-right: 0; 427 | direction: rtl; 428 | } 429 | .chosen-rtl .chosen-single div { 430 | left: 7px; 431 | right: auto; 432 | } 433 | .chosen-rtl .chosen-single abbr { 434 | left: 26px; 435 | right: auto; 436 | } 437 | .chosen-rtl .chosen-choices .search-field input[type="text"] { 438 | direction: rtl; 439 | } 440 | .chosen-rtl .chosen-choices li { 441 | float: right; 442 | } 443 | .chosen-rtl .chosen-choices .search-choice { 444 | margin: 6px 5px 3px 0; 445 | padding: 3px 5px 3px 19px; 446 | } 447 | .chosen-rtl .chosen-choices .search-choice .search-choice-close { 448 | background-position: right top; 449 | left: 4px; 450 | right: auto; 451 | } 452 | .chosen-rtl.chosen-container-single .chosen-results { 453 | margin: 0 0 4px 4px; 454 | padding: 0 4px 0 0; 455 | } 456 | .chosen-rtl .chosen-results .group-option { 457 | padding-left: 0; 458 | padding-right: 15px; 459 | } 460 | .chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div { 461 | border-right: none; 462 | } 463 | .chosen-rtl .chosen-search input[type="text"] { 464 | background: url("chosen-sprite.png") no-repeat -28px -20px, #ffffff; 465 | direction: rtl; 466 | padding: 4px 5px 4px 20px; 467 | } 468 | @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-resolution: 144dpi) { 469 | .chosen-rtl .chosen-search input[type="text"], 470 | .chosen-container-single .chosen-single abbr, 471 | .chosen-container-single .chosen-single div b, 472 | .chosen-container-single .chosen-search input[type="text"], 473 | .chosen-container-multi .chosen-choices .search-choice .search-choice-close, 474 | .chosen-container .chosen-results-scroll-down span, 475 | .chosen-container .chosen-results-scroll-up span { 476 | background-image: url("chosen-sprite@2x.png") !important; 477 | background-size: 52px 37px !important; 478 | background-repeat: no-repeat !important; 479 | } 480 | } 481 | 482 | /*---------------------------------------------------------------------------------------- 483 | ...END OF BOOTSTRAP-CHOSEN SECTION 484 | 485 | bootstrap-chosen additions 486 | ----------------------------------------------------------------------------------------*/ 487 | 488 | /* Mouse hover color in drop-downs */ 489 | .chosen-container .chosen-results li.mouseover { 490 | background-color: #eeeeee; 491 | background-image: none; 492 | } 493 | 494 | /*---------------------------------------------------------------------------------------- 495 | Stylesheet for re-com.date Date Picker variants inline-picker & dropdown-picker 496 | Day8 variation loosely based on: 497 | Copyright 2013 Dan Grossman ( http://www.dangrossman.info ) 498 | Licensed under the Apache License v2.0 499 | http://www.apache.org/licenses/LICENSE-2.0 500 | Built for http://www.improvely.com 501 | http://eternicode.github.io/bootstrap-datepicker 502 | 503 | START OF DATE PICKER SECTION... 504 | ----------------------------------------------------------------------------------------*/ 505 | 506 | .datepicker.single .calendar { 507 | float: none; 508 | } 509 | 510 | .datepicker .calendar { 511 | display: none; 512 | max-width: 200px; 513 | } 514 | 515 | .datepicker .calendar.single .calendar-date { 516 | border: none; 517 | } 518 | 519 | .datepicker .calendar th, .datepicker .calendar td { 520 | white-space: nowrap; 521 | text-align: center; 522 | min-width: 32px; 523 | } 524 | 525 | .datepicker .calendar-date { 526 | border: 1px solid #ddd; 527 | padding: 4px; 528 | border-radius: 4px; 529 | background: #fff; 530 | } 531 | 532 | .datepicker .calendar-time { 533 | text-align: center; 534 | margin: 8px auto 0 auto; 535 | line-height: 30px; 536 | } 537 | 538 | .datepicker { 539 | position: absolute; 540 | background: #fff; 541 | top: 100px; 542 | left: 20px; 543 | padding: 10px 10px 5px; 544 | margin-top: 1px; 545 | -webkit-border-radius: 4px; 546 | -moz-border-radius: 4px; 547 | line-height: 16px; 548 | border-radius: 4px; 549 | } 550 | 551 | .datepicker table { 552 | width: 100%; 553 | margin: 0; 554 | border-collapse: separate; 555 | } 556 | 557 | .datepicker td, .datepicker th { 558 | text-align: center; 559 | width: 27px; 560 | height: 26px; 561 | max-width: 27px; 562 | max-height: 26px; 563 | min-width: 27px; 564 | min-height: 26px; 565 | -webkit-border-radius: 4px; 566 | -moz-border-radius: 4px; 567 | border-radius: 4px; 568 | padding: 4px; 569 | cursor: default; 570 | white-space: nowrap; 571 | font-weight: normal; 572 | } 573 | 574 | .datepicker td.off { 575 | padding: 4px; 576 | color: #999; 577 | } 578 | 579 | .datepicker td.disabled { 580 | color: #999; 581 | } 582 | 583 | .datepicker th.disabled { 584 | color: #999; 585 | } 586 | 587 | .datepicker td.available:hover, .datepicker th.available:hover { 588 | background: #eee; 589 | cursor: pointer; 590 | } 591 | 592 | .datepicker td.in-range { 593 | background: #ebf4f8; 594 | -webkit-border-radius: 0; 595 | -moz-border-radius: 0; 596 | border-radius: 0; 597 | } 598 | 599 | .datepicker td.start-date { 600 | -webkit-border-radius: 4px 0 0 4px; 601 | -moz-border-radius: 4px 0 0 4px; 602 | border-radius: 4px 0 0 4px; 603 | } 604 | 605 | .datepicker td.end-date { 606 | -webkit-border-radius: 0 4px 4px 0; 607 | -moz-border-radius: 0 4px 4px 0; 608 | border-radius: 0 4px 4px 0; 609 | } 610 | 611 | .datepicker td.start-date.end-date { 612 | -webkit-border-radius: 4px; 613 | -moz-border-radius: 4px; 614 | border-radius: 4px; 615 | } 616 | 617 | .datepicker td.active, .datepicker td.active:hover { 618 | background-color: #357ebd; 619 | border-color: #3071a9; 620 | color: #fff; 621 | } 622 | 623 | /* Introduced by Day8 from http://eternicode.github.io/bootstrap-datepicker */ 624 | .datepicker td.today, .datepicker td.today:hover { 625 | background-color: #ffcd70; 626 | border-color: #f59e00; 627 | border-radius: 18px; 628 | color: #fff; 629 | } 630 | 631 | .datepicker th.day-enabled, label.day-enabled { 632 | font-weight: normal; 633 | font-size: 10px; 634 | color: #333; 635 | } 636 | 637 | .datepicker th.selectable { 638 | font-weight: normal; 639 | color: #357ebd; 640 | } 641 | 642 | .datepicker th.day-disabled { 643 | font-weight: normal; 644 | font-size: 10px; 645 | color: #999; 646 | } 647 | 648 | .datepicker td.week, .datepicker th.week { 649 | font-size: 80%; 650 | color: #ccc; 651 | } 652 | 653 | 654 | .datepicker th.prev { 655 | border: 1px solid #ccc; 656 | border-right-style: none; 657 | border-top-right-radius: 0; 658 | border-bottom-right-radius: 0; 659 | background-color: #F7F7F7; 660 | } 661 | 662 | .datepicker th.next { 663 | border: 1px solid #ccc; 664 | border-left-style: none; 665 | border-top-left-radius: 0; 666 | border-bottom-left-radius: 0; 667 | background-color: #F7F7F7; 668 | } 669 | 670 | .datepicker th.month { 671 | width: auto; 672 | font-size: 14px; 673 | color: #777; 674 | border-radius: 0; 675 | border: 1px solid #CCC; 676 | border-right-style: none; 677 | border-left-style: none; 678 | background-color: #F7F7F7; 679 | } 680 | 681 | .dropdown-button { 682 | cursor: pointer; 683 | height: 32px; 684 | font-size: 13px; 685 | font-weight: normal; 686 | } 687 | 688 | .dropdown-button.activator { 689 | width: 40px; 690 | color: #777; 691 | background-color: #F7F7F7 692 | } 693 | 694 | /*---------------------------------------------------------------------------------------- 695 | END OF DATE PICKER SECTION... 696 | 697 | 698 | CSS THROBBER 699 | ----------------------------------------------------------------------------------------*/ 700 | @keyframes blink { 701 | 0% { 702 | transform: scale(1); 703 | opacity: 1; 704 | } 705 | 100% { 706 | transform: scale(.5); 707 | opacity: 0; 708 | } 709 | } 710 | @-moz-keyframes blink { 711 | 0% { 712 | -moz-transform: scale(1); 713 | opacity: 1; 714 | } 715 | 100% { 716 | -moz-transform: scale(.5); 717 | opacity: 0; 718 | } 719 | } 720 | @-webkit-keyframes blink { 721 | 0% { 722 | -webkit-transform: scale(1); 723 | opacity: 1; 724 | } 725 | 100% { 726 | -webkit-transform: scale(.5); 727 | opacity: 0; 728 | } 729 | } 730 | 731 | .loader { 732 | position: relative; 733 | display: inline-block; 734 | vertical-align: middle; 735 | width: 20px; 736 | height: 20px; 737 | margin: 20px; 738 | padding: 0px; 739 | } 740 | 741 | .loader li { 742 | position: absolute; 743 | display: block; 744 | border-radius: 4px; 745 | background-color: #999; 746 | width: 6px; 747 | height: 6px; 748 | margin-top: -3px; 749 | margin-left: -3px; 750 | opacity: 0; 751 | -webkit-animation: blink .8s ease infinite; 752 | -moz-animation: blink .8s ease infinite; 753 | animation: blink .8s ease infinite; 754 | } 755 | 756 | .loader li:nth-child(1) { 757 | top: 0%; 758 | left: 50%; 759 | -webkit-animation-delay: -.7s; 760 | -moz-animation-delay: -.7s; 761 | animation-delay: -.7s; 762 | } 763 | 764 | .loader li:nth-child(2) { 765 | top: 15%; 766 | left: 85%; 767 | -webkit-animation-delay: -.6s; 768 | -moz-animation-delay: -.6s; 769 | animation-delay: -.6s; 770 | } 771 | 772 | .loader li:nth-child(3) { 773 | top: 50%; 774 | left: 100%; 775 | -webkit-animation-delay: -.5s; 776 | -moz-animation-delay: -.5s; 777 | animation-delay: -.5s; 778 | } 779 | 780 | .loader li:nth-child(4) { 781 | top: 85%; 782 | left: 85%; 783 | -webkit-animation-delay: -.4s; 784 | -moz-animation-delay: -.4s; 785 | animation-delay: -.4s; 786 | } 787 | 788 | .loader li:nth-child(5) { 789 | top: 100%; 790 | left: 50%; 791 | -webkit-animation-delay: -.3s; 792 | -moz-animation-delay: -.3s; 793 | animation-delay: -.3s; 794 | } 795 | 796 | .loader li:nth-child(6) { 797 | top: 85%; 798 | left: 15%; 799 | -webkit-animation-delay: -.2s; 800 | -moz-animation-delay: -.2s; 801 | animation-delay: -.2s; 802 | } 803 | 804 | .loader li:nth-child(7) { 805 | top: 50%; 806 | left: 0%; 807 | -webkit-animation-delay: -.1s; 808 | -moz-animation-delay: -.1s; 809 | animation-delay: -.1s; 810 | 811 | } 812 | 813 | .loader li:nth-child(8) { 814 | top: 15%; 815 | left: 15%; 816 | } 817 | 818 | 819 | .loader.small { 820 | width: 12px; 821 | height: 12px; 822 | margin: 10px; 823 | } 824 | 825 | .loader.small li { 826 | width: 4px; 827 | height: 4px; 828 | margin-top: -2px; 829 | margin-left: -2px; 830 | } 831 | 832 | .loader.large { 833 | width: 48px; 834 | height: 48px; 835 | margin: 20px; 836 | } 837 | 838 | .loader.large li { 839 | border-radius: 14px; 840 | width: 14px; 841 | height: 14px; 842 | margin-top: -7px; 843 | margin-left: -7px; 844 | } 845 | 846 | /*---------------------------------------------------------------------------------------- 847 | END OF CSS THROBBER SECTION... 848 | 849 | 850 | Bootstrap list-group-item variation (only used in selection-list component) 851 | ----------------------------------------------------------------------------------------*/ 852 | /* TODO: re-demo CSS */ 853 | .compact { 854 | margin: 0; 855 | height: 19px; 856 | white-space: nowrap; 857 | border: none; 858 | padding: 0; 859 | overflow: hidden; 860 | cursor: default; 861 | font-size: small; 862 | text-overflow: ellipsis; 863 | } 864 | /* TODO: re-demo CSS */ 865 | .list-group-item:hover { 866 | background-color: #eeeeee; 867 | } 868 | 869 | /*---------------------------------------------------------------------------------------- 870 | Custom scrollbars 871 | - http://css-tricks.com/custom-scrollbars-in-webkit 872 | - http://codemug.com/html/custom-scrollbars-using-css 873 | ----------------------------------------------------------------------------------------*/ 874 | 875 | ::-webkit-scrollbar { 876 | width: 10px; 877 | border-radius: 5px; 878 | background-color: rgba(0,0,0,0.05); 879 | } 880 | ::-webkit-scrollbar:horizontal { 881 | height:10px; 882 | } 883 | ::-webkit-scrollbar:hover { 884 | background-color:rgba(0,0,0,0.20); 885 | } 886 | ::-webkit-scrollbar-thumb:horizontal, ::-webkit-scrollbar-thumb:vertical { 887 | background:rgba(0,0,0,0.25); 888 | border-radius: 5px; 889 | } 890 | ::-webkit-scrollbar-thumb:horizontal:active, ::-webkit-scrollbar-thumb:vertical:active { 891 | background:rgba(0,0,0,0.45); 892 | } 893 | 894 | /* Color of selected text*/ 895 | /* 896 | ::-moz-selection { color: gold; background: red; } 897 | ::selection { color: gold; background: red; } 898 | */ 899 | 900 | 901 | /*---------------------------------------------------------------------------------------- 902 | Override bootstrap input text placeholder color (it's too dark) 903 | ----------------------------------------------------------------------------------------*/ 904 | 905 | .form-control::-webkit-input-placeholder { 906 | color: #bbb; 907 | } 908 | 909 | /*---------------------------------------------------------------------------------------- 910 | Override bootstrap warning color (from horrible brown/orange to orange) 911 | ----------------------------------------------------------------------------------------*/ 912 | 913 | .has-warning .form-control { 914 | border-color: #f57c00; 915 | } 916 | 917 | .has-warning .form-control:focus { 918 | border-color: #f57c00; 919 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px rgba(245, 124, 0, 0.6); 920 | } 921 | 922 | /* TODO: REMOVE */ 923 | .has-warning .form-control-feedback { 924 | color: #f57c00; 925 | } 926 | 927 | .rc-input-text .zmdi-alert-triangle { 928 | color: #f57c00; 929 | } 930 | 931 | .has-error .form-control { 932 | border-color: #d50000; 933 | } 934 | 935 | .has-error .form-control:focus { 936 | border-color: #d50000; 937 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px rgba(213, 0, 0, 0.6); 938 | } 939 | 940 | /* TODO: REMOVE */ 941 | .has-error .form-control-feedback { 942 | color: #d50000; 943 | } 944 | 945 | .rc-input-text .zmdi-alert-circle { 946 | color: #d50000; 947 | } 948 | 949 | .alert-warning { 950 | color: #ea6b00; 951 | background-color: #ffecdc; 952 | border-color: #faebcc; 953 | } 954 | 955 | code { 956 | margin-right: 0.2em; 957 | border-radius: 4px; 958 | color: #333; 959 | background: #eee; 960 | border: 1px solid #ddd; 961 | padding: 0.1em 0.3em; 962 | } 963 | 964 | 965 | /*---------------------------------------------------------------------------------------- 966 | Time component 967 | ----------------------------------------------------------------------------------------*/ 968 | 969 | .time-entry { 970 | flex: none; 971 | padding-left: 0.3em; 972 | padding-top: 2px; 973 | border: 1px solid #ccc; 974 | border-radius: 4px 0 0 4px; 975 | width: 3.3em; 976 | } 977 | .time-entry:last-child { 978 | border-radius: 4px; 979 | } 980 | .time-icon { 981 | display: flex; 982 | padding: 0 0.3em; 983 | border: 1px solid #ccc; 984 | border-left: none; 985 | border-radius: 0 4px 4px 0; 986 | background-color: #ddd; 987 | } 988 | 989 | /*---------------------------------------------------------------------------------------- 990 | md-circle-icon-button 991 | ----------------------------------------------------------------------------------------*/ 992 | 993 | .rc-md-circle-icon-button { 994 | width: 40px; /* material design dimensions */ 995 | height: 40px; 996 | text-align: center; /* to horizontally center the icon */ 997 | border: 1px solid lightgrey; 998 | border-radius: 50%; 999 | background-color: inherit; 1000 | opacity: 0.9; 1001 | } 1002 | 1003 | .rc-md-circle-icon-button > i { 1004 | font-size : 24px; 1005 | line-height: 40px; /* to vertically center in surrounding :div */ 1006 | color: inherit; 1007 | } 1008 | 1009 | .rc-md-circle-icon-button:hover { 1010 | border: 1px solid #428bca; 1011 | color: #428bca; 1012 | } 1013 | 1014 | .rc-circle-smaller { 1015 | width: 26px; 1016 | height: 26px; 1017 | } 1018 | 1019 | .rc-circle-smaller > i { 1020 | font-size: 16px; 1021 | line-height: 24px; 1022 | } 1023 | 1024 | .rc-circle-larger { 1025 | width: 56px; 1026 | height: 56px; 1027 | } 1028 | 1029 | .rc-circle-larger > i { 1030 | font-size: 24px; 1031 | line-height: 56px; 1032 | } 1033 | 1034 | .rc-circle-emphasis { 1035 | border: 1px solid #428bca; 1036 | background-color: #428bca; 1037 | color: white; 1038 | } 1039 | 1040 | .rc-circle-emphasis:hover { 1041 | border: 1px solid #2196f3; 1042 | background-color: #2196f3; 1043 | color: white; 1044 | } 1045 | 1046 | .rc-circle-disabled { 1047 | border: none; 1048 | } 1049 | 1050 | .rc-circle-disabled:hover { 1051 | border: none; 1052 | } 1053 | 1054 | .rc-circle-disabled > i { 1055 | color: lightgrey; 1056 | } 1057 | 1058 | /*---------------------------------------------------------------------------------------- 1059 | md-icon-button 1060 | ----------------------------------------------------------------------------------------*/ 1061 | 1062 | .rc-md-icon-button { 1063 | height: 24px; 1064 | font-size: 24px; 1065 | line-height: 24px; 1066 | text-align: center; /* to horizontally center the icon */ 1067 | background-color: inherit; 1068 | border-radius: 3px; 1069 | } 1070 | 1071 | .rc-md-icon-button > i { 1072 | color: inherit; 1073 | } 1074 | 1075 | .rc-md-icon-button:hover { 1076 | color: #2196f3; 1077 | } 1078 | 1079 | .rc-icon-smaller { 1080 | height: 16px; 1081 | font-size: 16px; 1082 | line-height: 16px; 1083 | } 1084 | 1085 | .rc-icon-larger { 1086 | height: 32px; 1087 | font-size: 32px; 1088 | line-height: 32px; 1089 | } 1090 | 1091 | .rc-icon-emphasis { 1092 | background-color: #428bca; 1093 | color: white; 1094 | } 1095 | 1096 | .rc-icon-emphasis:hover { 1097 | background-color: #2196f3; 1098 | color: white; 1099 | } 1100 | 1101 | .rc-icon-disabled > i { 1102 | color: lightgrey; 1103 | } 1104 | 1105 | /*---------------------------------------------------------------------------------------- 1106 | info-button 1107 | 1108 | CSS styles below are taken from https://github.com/oakmac/cljs.info... 1109 | 1110 | The MIT License (MIT) 1111 | Copyright (c) 2014 Chris Oakman and contributors 1112 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 1113 | to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 1114 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 1115 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 1116 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1117 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1118 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 1119 | IN THE SOFTWARE. 1120 | ----------------------------------------------------------------------------------------*/ 1121 | 1122 | .rc-info-button { 1123 | padding-left: 3px; 1124 | padding-right: 3px; 1125 | fill: #bdbdbd; 1126 | } 1127 | 1128 | .rc-info-button:hover { 1129 | fill: #2962ff; 1130 | } 1131 | 1132 | .popover-content .info-heading { 1133 | font-size: 22px; 1134 | } 1135 | 1136 | .popover-content .info-subheading { 1137 | font-size: 18px; 1138 | margin-top: 8px; 1139 | } 1140 | 1141 | .popover-content .info-bold { 1142 | background-color: #555; 1143 | padding-left: 3px; 1144 | padding-right: 3px; 1145 | border-radius: 3px; 1146 | font-weight: bold; 1147 | } 1148 | 1149 | .popover-content a { 1150 | color: #ccf; 1151 | } 1152 | 1153 | .popover-content code { 1154 | color: white; 1155 | background-color: #555; 1156 | border: none; 1157 | } 1158 | 1159 | /*---------------------------------------------------------------------------------------- 1160 | row-button 1161 | ----------------------------------------------------------------------------------------*/ 1162 | 1163 | .rc-row-button { 1164 | text-align: center; /* to horizontally center the icon */ 1165 | color: #2167f2; /2196f3; 1166 | background-color: inherit; 1167 | opacity: 0; 1168 | cursor: pointer; 1169 | } 1170 | 1171 | .rc-row-button > i { 1172 | color: inherit; 1173 | } 1174 | 1175 | .rc-row-mouse-over-row { 1176 | opacity: 0.4; 1177 | } 1178 | 1179 | .rc-row-button:hover, rc-row-mouse-over-row:hover { 1180 | opacity: 1; 1181 | } 1182 | 1183 | .rc-row-disabled { 1184 | opacity: 1; 1185 | color: lightgrey; 1186 | cursor: default; 1187 | } 1188 | 1189 | /*---------------------------------------------------------------------------------------- 1190 | div-table 1191 | ----------------------------------------------------------------------------------------*/ 1192 | 1193 | .rc-div-table { 1194 | flex: none; 1195 | width: auto; 1196 | border: 2px solid lightgrey; 1197 | cursor: default; 1198 | } 1199 | 1200 | .rc-div-table-header > div { 1201 | background-color: #e8e8e8; 1202 | font-weight: bold; 1203 | padding: 5px; 1204 | } 1205 | 1206 | .rc-div-table-row { 1207 | border-top: 1px solid lightgrey; 1208 | } 1209 | 1210 | .rc-div-table-row:hover { 1211 | background-color: #f2f2f2; 1212 | } 1213 | 1214 | .rc-div-table-row > div { 1215 | padding: 5px; 1216 | } 1217 | 1218 | /*---------------------------------------------------------------------------------------- 1219 | General Typography 1220 | ----------------------------------------------------------------------------------------*/ 1221 | 1222 | .semibold { 1223 | font-weight: 500; 1224 | } 1225 | .bold { 1226 | font-weight: 700; 1227 | } 1228 | .italic { 1229 | font-style: italic; 1230 | } 1231 | .uppercase { 1232 | text-transform: uppercase; 1233 | } 1234 | .all-small-caps { 1235 | font-variant: small-caps; 1236 | } 1237 | 1238 | .level1 { 1239 | font-family: Segoe UI Semilight, Roboto, sans-serif; 1240 | font-weight: 200; 1241 | font-size: 40px; 1242 | color: rgba(68, 68, 68, 1.0); 1243 | letter-spacing: normal; 1244 | /*-ms-font-feature-settings: 'ss20' 1;*/ 1245 | } 1246 | .level2 { 1247 | font-family: Segoe UI Semilight, Roboto, sans-serif; 1248 | font-weight: 200; 1249 | font-size: 26px; 1250 | color: rgba(0, 0, 0, 1.0); 1251 | letter-spacing: 0.001em; 1252 | } 1253 | .level3 { 1254 | font-family: Segoe UI Semilight, Roboto, sans-serif; 1255 | font-weight: 500; 1256 | font-size: 15px; 1257 | color: rgba(68, 68, 68, 1.0); 1258 | letter-spacing: 0.002em; 1259 | } 1260 | .level4 { 1261 | font-family: Segoe UI Semilight, Roboto, sans-serif; 1262 | font-weight: 500; 1263 | font-size: 15px; 1264 | color: rgba(68, 68, 68, 0.6); 1265 | letter-spacing: 0.002em; 1266 | } 1267 | .field-label { 1268 | font-family: Segoe UI Semilight, Roboto, sans-serif; 1269 | font-weight: 400; 1270 | font-size: 14px; 1271 | color: rgba(68, 68, 68, 0.6); 1272 | letter-spacing: 0.002em; 1273 | font-variant: small-caps; 1274 | flex: none; 1275 | } 1276 | 1277 | .noselect { 1278 | -webkit-user-select: none; 1279 | -moz-user-select: none; 1280 | -ms-user-select: none; 1281 | user-select: none; 1282 | } 1283 | /*---------------------------------------------------------------------------------------- 1284 | Flexbox "display" styles 1285 | Requires 2 display values which we can't represent in a Clojure map 1286 | ----------------------------------------------------------------------------------------*/ 1287 | 1288 | .display-flex { 1289 | display: -webkit-flex; 1290 | display: flex; 1291 | } 1292 | .display-inline-flex { 1293 | display: -webkit-inline-flex; 1294 | display: inline-flex; 1295 | } 1296 | 1297 | .zmdi-hc-fw-rc { 1298 | width: 1em; 1299 | text-align: center; 1300 | } 1301 | -------------------------------------------------------------------------------- /resources/public/styles/css/re-console-code-mirror.css: -------------------------------------------------------------------------------- 1 | .re-console-container { 2 | background-color: #FFF; 3 | overflow-y: scroll; 4 | padding: 10px; 5 | margin: 0 0; 6 | border-top-left-radius: 10px; 7 | border-top-right-radius: 10px; 8 | -webkit-transform: translateZ(0); 9 | -webkit-mask-image: -webkit-radial-gradient(circle, white 100%, black 100%); 10 | } 11 | 12 | .re-console .CodeMirror { 13 | height: auto; 14 | font-size: 13px; 15 | font-family: Menlo,Monaco,Consolas,"Courier New",monospace; 16 | margin-bottom: 20px; 17 | } 18 | 19 | .re-console .CodeMirror-code { 20 | background-color: #FFF; 21 | } 22 | 23 | .re-console .CodeMirror-lines, .re-console .CodeMirror pre { 24 | padding: 0; 25 | } 26 | 27 | .re-console-item, .re-console-item pre, .re-console-item-error pre { 28 | font-family: Menlo,Monaco,Consolas,"Courier New",monospace; 29 | font-size: 13px; 30 | opacity: 0.7; 31 | background: transparent; 32 | border: none; 33 | margin: 0; 34 | padding: 0px; 35 | overflow: visible; 36 | } 37 | 38 | .re-console-item-error { 39 | color: #e60000; 40 | } 41 | 42 | .re-console-mode-line { 43 | font-family: Menlo,Monaco,Consolas,"Courier New",monospace; 44 | /* font-size: 10px; AR - handled in app.css */ 45 | background-color: #414141; 46 | text-align:center; 47 | color: white; 48 | padding: 5px; 49 | margin: 0px; 50 | border-bottom-left-radius: 10px; 51 | border-bottom-right-radius: 10px; 52 | -webkit-transform: translateZ(0); 53 | -webkit-mask-image: -webkit-radial-gradient(circle, white 100%, black 100%); 54 | white-space: pre-wrap; /* CSS 3 */ 55 | white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ 56 | white-space: -pre-wrap; /* Opera 4-6 */ 57 | white-space: -o-pre-wrap; /* Opera 7 */ 58 | word-wrap: break-word; 59 | } 60 | 61 | .re-console-container::-webkit-scrollbar { 62 | width: 6px; 63 | height: 6px; 64 | } 65 | -------------------------------------------------------------------------------- /resources/public/styles/fonts/Material-Design-Iconic-Font.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/styles/fonts/Material-Design-Iconic-Font.eot -------------------------------------------------------------------------------- /resources/public/styles/fonts/Material-Design-Iconic-Font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/styles/fonts/Material-Design-Iconic-Font.ttf -------------------------------------------------------------------------------- /resources/public/styles/fonts/Material-Design-Iconic-Font.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/styles/fonts/Material-Design-Iconic-Font.woff -------------------------------------------------------------------------------- /resources/public/styles/fonts/Material-Design-Iconic-Font.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/styles/fonts/Material-Design-Iconic-Font.woff2 -------------------------------------------------------------------------------- /resources/public/styles/images/cljs.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 31 | 32 | 33 | 34 | 36 | 37 | 41 | 42 | 46 | 51 | 52 | 54 | 56 | 57 | -------------------------------------------------------------------------------- /resources/public/styles/images/header-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/styles/images/header-bg.png -------------------------------------------------------------------------------- /resources/public/styles/images/loading_cljs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/styles/images/loading_cljs.png -------------------------------------------------------------------------------- /resources/public/styles/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/styles/images/logo.png -------------------------------------------------------------------------------- /resources/public/styles/images/replumb_logo_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lambda-X/cljs-repl-web/0dabe8ff18d8c0f4ae6c9f052dc23eb1f5ad262c/resources/public/styles/images/replumb_logo_img.png -------------------------------------------------------------------------------- /scripts/brepl: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | rlwrap lein trampoline run -m clojure.main scripts/brepl.clj 3 | -------------------------------------------------------------------------------- /scripts/brepl.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | lein trampoline run -m clojure.main scripts\brepl.clj 3 | -------------------------------------------------------------------------------- /scripts/brepl.clj: -------------------------------------------------------------------------------- 1 | (require 2 | '[cljs.build.api :as b] 3 | '[cljs.repl :as repl] 4 | '[cljs.repl.browser :as browser]) 5 | 6 | (b/build "src" 7 | {:main 'cljs-browser-repl.core 8 | :output-to "target/cljs-browser-repl.js" 9 | :output-dir "target" 10 | :verbose true}) 11 | 12 | (repl/repl (browser/repl-env) 13 | :output-dir "target") 14 | -------------------------------------------------------------------------------- /scripts/build: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | rlwrap lein trampoline run -m clojure.main scripts/build.clj 3 | -------------------------------------------------------------------------------- /scripts/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | lein trampoline run -m clojure.main scripts\build.clj 3 | -------------------------------------------------------------------------------- /scripts/build.clj: -------------------------------------------------------------------------------- 1 | (require '[cljs.build.api :as b]) 2 | 3 | (println "Building ...") 4 | 5 | (let [start (System/nanoTime)] 6 | (b/build "src" 7 | {:main 'hello-world.core 8 | :output-to "target/cljs-browser-repl.js" 9 | :output-dir "target" 10 | :verbose true}) 11 | (println "... done. Elapsed" (/ (- (System/nanoTime) start) 1e9) "seconds")) 12 | -------------------------------------------------------------------------------- /scripts/node_repl: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | rlwrap lein trampoline run -m clojure.main scripts/node_repl.clj 3 | -------------------------------------------------------------------------------- /scripts/node_repl.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | lein trampoline run -m clojure.main scripts\node_repl.clj 3 | -------------------------------------------------------------------------------- /scripts/node_repl.clj: -------------------------------------------------------------------------------- 1 | (require 'cljs.repl) 2 | (require 'cljs.build.api) 3 | (require 'cljs.repl.node) 4 | 5 | (cljs.build.api/build "src" 6 | {:main 'cljs-browser-repl.core 7 | :output-to "target/cljs-browser-repl.js" 8 | :verbose true}) 9 | 10 | ;; (println (cljs.build.api/ns->location 'hello-world.core)) 11 | 12 | (cljs.repl/repl (cljs.repl.node/repl-env) 13 | :watch "src" 14 | :output-dir "target") 15 | -------------------------------------------------------------------------------- /scripts/release: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | rlwrap lein trampoline run -m clojure.main scripts/release.clj 3 | -------------------------------------------------------------------------------- /scripts/release.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | lein trampoline run -m clojure.main scripts\release.clj 3 | -------------------------------------------------------------------------------- /scripts/release.clj: -------------------------------------------------------------------------------- 1 | (require '[cljs.build.api :as b]) 2 | 3 | (println "Building ...") 4 | 5 | (let [start (System/nanoTime)] 6 | (b/build "src" 7 | {:output-to "release/cljs-browser-repl.js" 8 | :output-dir "release" 9 | :optimizations :advanced 10 | :verbose true}) 11 | (println "... done. Elapsed" (/ (- (System/nanoTime) start) 1e9) "seconds")) 12 | -------------------------------------------------------------------------------- /scripts/repl: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | rlwrap lein trampoline run -m clojure.main scripts/repl.clj 3 | -------------------------------------------------------------------------------- /scripts/repl.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | lein trampoline run -m clojure.main scripts\repl.clj 3 | -------------------------------------------------------------------------------- /scripts/repl.clj: -------------------------------------------------------------------------------- 1 | (require 2 | '[cljs.repl :as repl] 3 | '[cljs.repl.node :as node]) 4 | 5 | (repl/repl (node/repl-env)) 6 | -------------------------------------------------------------------------------- /scripts/sftp-deploy-prod: -------------------------------------------------------------------------------- 1 | # AR - We need out now that replumb supports require 2 | # rm -RI resources/public/js/compiled/out 3 | rsync -rlDvz resources/public/ arichiardi@clojurescript.io:/var/www/clojurescript.io/ 4 | -------------------------------------------------------------------------------- /scripts/sftp-deploy-test: -------------------------------------------------------------------------------- 1 | # AR - We need out now that replumb supports require 2 | # rm -RI resources/public/js/compiled/out 3 | rsync -rlDvz resources/public/ arichiardi@clojurescript.io:/var/www/clojurescript.io/test/ 4 | -------------------------------------------------------------------------------- /scripts/watch: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | rlwrap lein trampoline run -m clojure.main scripts/watch.clj 3 | -------------------------------------------------------------------------------- /scripts/watch.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | lein trampoline run -m clojure.main scripts\watch.clj 3 | -------------------------------------------------------------------------------- /scripts/watch.clj: -------------------------------------------------------------------------------- 1 | (require '[cljs.build.api :as b]) 2 | 3 | (b/watch "src" 4 | {:main 'cljs-browser-repl.core 5 | :output-to "target/cljs-browser-repl.js" 6 | :output-dir "target"}) 7 | -------------------------------------------------------------------------------- /src/clj/cljs_api/generator.clj: -------------------------------------------------------------------------------- 1 | (ns cljs-api.generator 2 | (:require [clojure.tools.reader.edn :as tr-edn] 3 | [clojure.tools.reader.reader-types :as tr-types] 4 | [clojure.pprint :as pprint :refer [pprint]] 5 | [clojure.string :as s] 6 | [clojure.java.io :as io] 7 | [endophile.core :as e] 8 | [markdown.core :as md] 9 | [markdown.transformers :as mdt] 10 | [cljs-api.utils :as api-utils])) 11 | 12 | (def api-edn-keyseqs #{[:release :cljs-version] 13 | [:release :cljs-date] 14 | [:release :clj-version] 15 | [:release :gclosure-lib] 16 | [:release :treader-version] 17 | [:symbols]}) 18 | 19 | (def api-symbol-keywords #{:full-name :ns :name :type :docstring 20 | :signature :description :examples :related}) 21 | 22 | (def selected-namespaces #{"cljs.core" "special"}) 23 | 24 | (defn filter-kv 25 | "Given an associative collection, return a map that contains the key 26 | sequences in input, filtering out the rest" 27 | [keyseqs coll] 28 | (reduce (fn [m kv] (assoc-in m kv (get-in coll kv))) {} keyseqs)) 29 | 30 | (defn api-symbol->new-value 31 | [api-symbol] 32 | (select-keys (second api-symbol) api-symbol-keywords)) 33 | 34 | (defn api-symbol->new-key 35 | [api-symbol] 36 | (:name (second api-symbol))) 37 | 38 | (defn api-symbols->name-map 39 | [api-symbols] 40 | (into {} (map (juxt api-symbol->new-key api-symbol->new-value) api-symbols))) 41 | 42 | ;;;;;;;;;;;;;;;; 43 | ;; :examples ;;; 44 | ;;;;;;;;;;;;;;;; 45 | 46 | (defn assoc-example-htmls 47 | "Note, needs the value of the symbol map, aka the map containing 48 | {:examples ...}. It adds a seq of examples in (escaped) html format 49 | under :examples-htmls." 50 | [api-symbol-content] 51 | (let [examples (->> api-symbol-content :examples (map :content)) 52 | html (mapv #(md/md-to-html-string %) examples)] 53 | (assoc api-symbol-content :examples-htmls html))) 54 | 55 | (defn top-level-sexps 56 | "Given a string, splits it in top level sexp. Returns a list of 57 | strings." 58 | [edn-string] 59 | (with-open [r (tr-types/string-push-back-reader edn-string)] 60 | (loop [paren-count 0 61 | top-level "" 62 | top-levels []] 63 | (if-let [c (tr-types/read-char r)] 64 | (cond 65 | (= \( c) (recur (inc paren-count) 66 | (if (= 0 paren-count) 67 | (str c) 68 | (str top-level c)) 69 | (if (= 0 paren-count) 70 | (conj top-levels top-level) 71 | top-levels)) 72 | (= \) c) (recur (dec paren-count) 73 | (if (= 0 (dec paren-count)) 74 | "" 75 | (str top-level c)) 76 | (if (= 0 (dec paren-count)) 77 | (conj top-levels (str top-level c)) 78 | top-levels)) 79 | :else (recur paren-count (str top-level c) top-levels)) 80 | (conj top-levels top-level))))) 81 | 82 | (defn lift-up-comments 83 | "Given lines of code, merges each commented line (;) with the 84 | previous. Tipically [\"(pop [1 2 3])\" \";;=> [1 2]\" ...] becomes 85 | [\"(pop [1 2 3]) ;;=> [1 2]\" ...]." 86 | [lines] 87 | ;; AR - Like this trick? 88 | ;; (doall (map pprint (reductions (fn [merged-lines line] 89 | ;; (if (re-find #"^;" line) 90 | ;; (cons (str (first merged-lines) " " line) 91 | ;; (drop 1 merged-lines)) 92 | ;; (cons line merged-lines))) [] lines))) 93 | 94 | ;; AR - conj does not fit because drop returns lazy seqs, 95 | ;; reverse trasducer would be nice 96 | (vec (reverse (reduce (fn [merged-lines line] 97 | (if (re-find #"^;" line) 98 | (cons (str (first merged-lines) " " line) 99 | (drop 1 merged-lines)) 100 | (cons line merged-lines))) 101 | () lines)))) 102 | 103 | (defn remove-comments 104 | "Remove the comments. Takes a string as input and returns a string 105 | as output, without comments." 106 | [edn-str] 107 | (let [lines (s/split-lines edn-str) 108 | no-comments (filterv #(re-find #"^[^;]" %) lines)] 109 | (s/join \newline no-comments))) 110 | 111 | (defn remove-js-examples 112 | "Removes JavaScript code, which in this specific case are lines between 113 | \"// JavaScript\" (included) and \";; ClojureScript\". Takes a string as 114 | input and returns a string as output." 115 | [edn-str] 116 | (loop [lines (s/split-lines edn-str) 117 | new-lines []] 118 | (if (empty? lines) 119 | (s/join \newline new-lines) 120 | (let [first (first lines)] 121 | (if (.startsWith first "// JavaScript") 122 | (recur (drop-while (complement #(.startsWith % ";; ClojureScript")) lines) new-lines) 123 | (recur (rest lines) (conj new-lines first))))))) 124 | 125 | (defn clj-node-seq->strings 126 | "Nodes as used in clojure.xml and in the enlive HTML library. 127 | Nodes can be lists of nodes." 128 | [clj-node-seq] 129 | (let [code-content #(-> % :content first :content first) 130 | code-nodes (filter #(and (= :pre (:tag %)) (= :code (-> % :content first :tag))) clj-node-seq)] 131 | ;; AR - Thanks Tomasz for spotting the nested vector problem! 132 | 133 | (into [] (flatten 134 | (mapv (comp (partial filterv seq) ;; filter out empty lines 135 | (partial map #(s/trim %)) ;; trim spaces 136 | top-level-sexps ;; split in top level forms 137 | remove-comments ;; remove the comments 138 | remove-js-examples ;; remove javascript examples 139 | code-content) ;; fetch content 140 | code-nodes))))) 141 | 142 | (defn assoc-example-strings 143 | "Note, needs the value of the symbol map, aka the map containing 144 | {:examples ...}. It adds a seq of examples in (escaped) html format 145 | under :examples-strings" 146 | [api-symbol-content] 147 | (let [examples (->> api-symbol-content :examples (map :content)) 148 | seq-of-clj-node-seq (map #(e/to-clj (e/mp %)) examples) ;; this is a seq of {:tag .. :content ...} 149 | examples-strings (mapv clj-node-seq->strings seq-of-clj-node-seq)] 150 | (assoc api-symbol-content :examples-strings examples-strings))) 151 | 152 | (comment 153 | (def ss (:symbols (filter-kv api-edn-keyseqs (load-cljs-api-edn)))) 154 | (def c (val (first (filter #(and (= "cond" (-> % second :name)) (= "cljs.core" (-> % second :ns))) ss)))) 155 | (def c1 (assoc-example-strings c))) 156 | 157 | ;;;;;;;;;;;;;;;;;;; 158 | ;; :description ;;; 159 | ;;;;;;;;;;;;;;;;;;; 160 | 161 | (defn assoc-description-html 162 | "Note, needs the value of the symbol map, aka the map containing 163 | {:description ...}. It adds a seq of examples in (escaped) html format 164 | under :description-html." 165 | [api-symbol-content] 166 | (let [description (->> api-symbol-content :description) 167 | html (md/md-to-html-string description)] 168 | (assoc api-symbol-content :description-html html))) 169 | 170 | ;;;;;;;;;;;;;;;;;;; 171 | ;;; API parsing ;;; 172 | ;;;;;;;;;;;;;;;;;;; 173 | 174 | (defn load-cljs-api-edn 175 | [] 176 | (tr-edn/read-string (slurp (io/file (io/resource "cljs-api.edn"))))) 177 | 178 | (defn api-edn->api-map 179 | "Given the cljs edn map and selected symbols, generate a new map for those 180 | symbols with only interesting keys (in the form of a req of keyseq)." 181 | [cljs-api-edn symbols] 182 | (let [filtered-map (filter-kv api-edn-keyseqs cljs-api-edn) 183 | selected-symbols (->> (:symbols filtered-map) 184 | (filter #(get selected-namespaces (-> % second :ns))) 185 | (filter #(get symbols (-> % second :name symbol)))) 186 | symbol-name-map (into {} (api-symbols->name-map selected-symbols))] 187 | (assoc filtered-map 188 | :symbols (reduce (fn [symbol-map [symbol-k symbol-v]] 189 | (assoc symbol-map symbol-k (-> symbol-v 190 | ;; assoc-description-html ;; AR - not used anymore 191 | assoc-example-strings 192 | assoc-example-htmls))) 193 | {} symbol-name-map)))) 194 | 195 | (defn exit 196 | [code] 197 | (flush) 198 | (shutdown-agents) 199 | (System/exit code)) 200 | 201 | (defn -main 202 | [& args] 203 | (when (seq args) 204 | (do (.println *err* "This script does not want params.\n\nUsage: lein run -m cljs-api.generator/-main.") 205 | (exit 255))) 206 | (let [file-name "src/cljs/cljs_repl_web/cljs_api.cljs" 207 | api-edn (load-cljs-api-edn) 208 | sections (:sections api-utils/custom-api-map) 209 | sections-with-symbols (filter #(= :symbols (-> % :additional-info :type)) sections) 210 | selected-symbols (->> sections-with-symbols (mapcat :topics) (mapcat :symbols) set) 211 | new-edn-map (api-edn->api-map api-edn selected-symbols) 212 | file-content (str "(ns cljs-repl-web.cljs-api)\n\n" 213 | "(def cljs-api-edn " (with-out-str (pprint new-edn-map)) ")")] 214 | (try 215 | (spit file-name file-content :encoding "UTF-8") 216 | (catch Exception e 217 | (.printStackTrace e *err*) 218 | (exit 254))) 219 | (println "Cljs api dumped in" file-name) 220 | (println "Generated docs for" (count (:symbols new-edn-map)) "symbols")) 221 | (exit 0)) 222 | -------------------------------------------------------------------------------- /src/clj/cljs_api/utils.cljc: -------------------------------------------------------------------------------- 1 | (ns cljs-api.utils) 2 | 3 | (defrecord Section [title additional-info topics]) 4 | (defrecord Topic [title symbols]) 5 | 6 | ;;; A map of sections and topics we want to display in our API panel. 7 | ;;; We can easily add or remove sections/topics and they will be automatically 8 | ;;; displayed in the API panel. 9 | (def custom-api-map 10 | {:sections [(Section. "Useful Functions" {:type :symbols} 11 | [(Topic. "math" 12 | '(+ - * / quot rem mod inc dec max min rand)) 13 | (Topic. "comparison" 14 | '(= == not= < > <= >=)) 15 | (Topic. "predicates" 16 | '(nil? identical? zero? pos? neg? even? odd? true? false? 17 | distinct? empty? every? not-every? some not-any?)) 18 | (Topic. "higher-order functions" 19 | '(map map-indexed mapcat reduce filter partition-by 20 | take-while keep remove merge-with iterate repeatedly)) 21 | (Topic. "functions as data" 22 | '(apply partial comp complement juxt))]) 23 | (Section. "Datatypes" {:type :texts} 24 | [(Topic. "maps " '({:key1 val1 :key2 val2})) 25 | (Topic. "vectors" '([1 2 3] [:a :b :c])) 26 | (Topic. "sets" '(#{:a :b :c 1 2 3})) 27 | (Topic. "scalars" '(a-symbol :a-keyword "\"a-string\"")) 28 | (Topic. "arrays" '((array 1 2 3)))]) 29 | (Section. "Functions" {:type :texts} 30 | [(Topic. "calling" 31 | '("( )")) 32 | (Topic. "defining named functions" 33 | '("(defn [] |CONSTRAINTS| )")) 34 | (Topic. "anonymous function" 35 | '("(fn |NAME| [] |CONSTRAINTS| )")) 36 | (Topic. "anonymous inline funcion" 37 | '("#( |% %2 %3 OR %&|)"))]) 38 | (Section. "Sequences" {:type :symbols} 39 | [(Topic. "creation" 40 | '(vec hash-map set for list list* sorted-map 41 | repeat range cycle seq rseq)) 42 | (Topic. "inspection" 43 | '(first last rest next get get-in count keys 44 | vals nth contains? find)) 45 | (Topic. "manipulation" 46 | '(into conj cons assoc flatten merge assoc-in 47 | dissoc zipmap partition update-in reverse 48 | take drop distinct))]) 49 | (Section. "Useful Macros" {:type :symbols} 50 | [(Topic. "conditionals" 51 | '(if if-not if-let cond condp and or not when when-let 52 | when-not case)) 53 | (Topic. "nesting, chaining, and interop" 54 | '(-> ->> doto .. .)) 55 | (Topic. "defining things" 56 | '(def defn fn let letfn defmulti defmethod 57 | deftype defrecord reify this-as declare ns))]) 58 | (Section. "JavaScript Interop" {:type :texts} 59 | [(Topic. "method call" '("(.the-method target-object args...)")) 60 | (Topic. "property access" '("(.-property target-object)")) 61 | (Topic. "property setting" '("(set! (.-title js/document) \"Hi!\")")) 62 | (Topic. "direct javascript" '("(js/alert \"Hello world!\")")) 63 | (Topic. "external library use" '("(.text (js/jQuery \"#title\") \"ClojureScript Rocks!\")"))])]}) 64 | -------------------------------------------------------------------------------- /src/cljs/cljs_repl_web/app.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-repl-web.app 2 | (:require [reagent.core :as reagent] 3 | cljsjs.enquire 4 | [re-frame.core :refer [dispatch subscribe]] 5 | [re-complete.app :as complete-app] 6 | [replumb.ast :as ast] 7 | [clojure.string :as str])) 8 | 9 | (defn gist-showing? 10 | "Given a db, indicates if the gist login dialog is shown. 11 | It is not bound to any specific console." 12 | [db] 13 | (get-in db [:gist-data :gist-showing?])) 14 | 15 | (defn gist-auth-data 16 | "Given a db, returns the authorization data for gist. 17 | It is not bound to any specific console. It is used to 18 | retrieve the data from the form." 19 | [db] 20 | (get-in db [:gist-data :auth-data])) 21 | 22 | (defn gist-saved-username 23 | "Given a db, returns the saved username (gist login). It is not bound 24 | to any specific console. It is used to keep info for the next login." 25 | [db] 26 | (get-in db [:gist-data :saved-username])) 27 | 28 | (defn gist-error-msg 29 | "Given a db, returns the error after an unsuccessful attempt 30 | to create a gist. It is not bound to any specific console." 31 | [db] 32 | (get-in db [:gist-data :error-msg])) 33 | 34 | 35 | ;;;;;;;;;;;;;;;;;;;;; 36 | ;; Media Queries ;; 37 | ;;;;;;;;;;;;;;;;;;;;; 38 | 39 | (def mq-string-narrow "only screen and (max-width: 480px)") 40 | (def mq-string-medium "only screen and (min-width: 481px) and (max-width: 960px)") 41 | (def mq-string-wide "only screen and (min-width: 961px)") 42 | 43 | (defn initial-media-query! 44 | "Returns the initial media query state among :narrow, :medium 45 | and :wide." 46 | [] 47 | (let [match-media! #( %)] 48 | (cond 49 | (.-matches (js/window.matchMedia mq-string-narrow)) :narrow 50 | (.-matches (js/window.matchMedia mq-string-medium )) :medium 51 | (.-matches (js/window.matchMedia mq-string-wide)) :wide 52 | :else :wide))) 53 | 54 | (defn register-media-queries! 55 | [] 56 | (js/enquire.register mq-string-narrow 57 | (clj->js {:match #(dispatch [:media-match :narrow])})) 58 | (js/enquire.register mq-string-medium 59 | (clj->js {:match #(dispatch [:media-match :medium])})) 60 | (js/enquire.register mq-string-wide 61 | (clj->js {:match #(dispatch [:media-match :wide])}) 62 | true 63 | ;; AR - degrade gracefully to this if browser does 64 | ;; not support CSS3 Media queries 65 | )) 66 | 67 | (defn media-query-size 68 | "Given a db, returns one in :wide :medium or :narrow, defaults 69 | to :wide." 70 | [db] 71 | (or (get db :media-query-size) :wide)) 72 | 73 | ;;;;;;;;;;;;;;;;;;;;; 74 | ;; Initial State ;; 75 | ;;;;;;;;;;;;;;;;;;;;; 76 | 77 | (def initial-state 78 | {:consoles {} 79 | :gist-data {:gist-showing? false 80 | :auth-data {:username "" :password ""}} 81 | :media-query-size :wide}) 82 | 83 | (defn make-init-state! 84 | "Create the initial state, can be side effecting." 85 | [config {:keys [username]}] 86 | (-> (merge initial-state {:media-query-size (initial-media-query!)}) 87 | (assoc-in [:gist-data :auth-data :username] username))) 88 | 89 | (defn reset-state! 90 | "Reset the app state. Use this do" 91 | [] 92 | (dispatch [:reset-db])) 93 | 94 | ;;;;;;;;;;;;;;;;;;;;; 95 | ;;; Autocomplete ;;; 96 | ;;;;;;;;;;;;;;;;;;;;; 97 | 98 | (defn opening-excluded-chars [word excluded-chars] 99 | (let [starts-with-excluded-char? ((set excluded-chars) (first word))] 100 | (if starts-with-excluded-char? 101 | (opening-excluded-chars (apply str (rest word)) excluded-chars) 102 | word))) 103 | 104 | (defn closing-excluded-chars [word excluded-chars] 105 | (let [ends-with-excluded-char? ((set excluded-chars) (last word))] 106 | (if ends-with-excluded-char? 107 | (closing-excluded-chars (apply str (butlast word)) excluded-chars) 108 | word))) 109 | 110 | (defn current-word [previous-input input] 111 | (->> input 112 | (complete-app/index previous-input) 113 | (complete-app/current-word input))) 114 | 115 | (defn sort-case-insensitive-way [data] 116 | (sort #(compare (str/upper-case %1) (str/upper-case %2)) data)) 117 | 118 | (defn get-functions-from-ns [ns] 119 | (sort-case-insensitive-way (map str (keys (ast/ns-publics @replumb.repl.st ns))))) 120 | 121 | (defn lower-case-first [items-to-sort] 122 | (let [lower-case-items (sort (filter #((set (map char (range 97 122))) (first %)) 123 | items-to-sort)) 124 | other-items (sort (vec (clojure.set/difference (set items-to-sort) (set lower-case-items))))] 125 | (apply conj other-items (reverse lower-case-items)))) 126 | 127 | (defn sort-dictionary [dictionary] 128 | (->> dictionary 129 | sort-case-insensitive-way 130 | lower-case-first)) 131 | 132 | (defn create-dictionary [event console-key] 133 | (let [previous-input (subscribe [:get-previous-input console-key]) 134 | options (subscribe [:get-options console-key]) 135 | trim-chars (:trim-chars @options) 136 | new-text (-> @previous-input 137 | (current-word event) 138 | (opening-excluded-chars trim-chars) 139 | (closing-excluded-chars trim-chars)) 140 | user-ns (keys (replumb.ast/ns-publics @replumb.repl.st 'cljs.user)) 141 | requires (ast/requires @replumb.repl.st 'cljs.user) 142 | cljs-user (get-functions-from-ns 'cljs.user) 143 | cljs-core (get-functions-from-ns 'cljs.core) 144 | base-libs (concat cljs-user cljs-core) 145 | list-of-requires-vars (mapcat (fn [[k v]] 146 | (map #(str k "/" %) (keys (ast/ns-publics @replumb.repl.st v)))) 147 | requires) 148 | created-dictionary (concat base-libs list-of-requires-vars)] 149 | (if (= new-text "") 150 | "" 151 | (dispatch [:dictionary console-key (sort-dictionary created-dictionary)])))) 152 | -------------------------------------------------------------------------------- /src/cljs/cljs_repl_web/core.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-repl-web.core 2 | (:require [reagent.core :as reagent] 3 | [re-frame.core :refer [dispatch dispatch-sync]] 4 | [re-com.core :refer [p h-box v-box box gap line]] 5 | [devtools.core :as devtools] 6 | [cljs-repl-web.handlers] 7 | [cljs-repl-web.subs] 8 | [cljs-repl-web.views :as views] 9 | [cljs-repl-web.views.utils :as utils] 10 | [cljs-repl-web.replumb-proxy :as replumb-proxy] 11 | [cljs-repl-web.config :as config] 12 | [cljs-repl-web.localstorage :as ls] 13 | [cljs-repl-web.app :as app] 14 | [re-console.common :as common] 15 | [re-complete.core :as re-complete] 16 | [goog.dom :as dom])) 17 | 18 | (defonce console-key :cljs-console) 19 | 20 | ;; https://github.com/binaryage/cljs-devtools/releases/tag/v0.5.3 21 | (when-not (:production? config/defaults) 22 | (devtools/set-pref! :install-sanity-hints true) 23 | (devtools/install!)) 24 | 25 | (enable-console-print!) 26 | 27 | (defn ^:export main [] 28 | (let [{:keys [name verbose-repl? src-paths]} config/defaults 29 | local-storage-values (ls/get-local-storage-values)] 30 | (dispatch [:options console-key {:trim-chars "[](){}#'@^`~." 31 | :keys-handling {:visible-items 6 32 | :item-height 20}}]) 33 | (println "[Entering]" name) 34 | (dispatch-sync [:initialize config/defaults local-storage-values]) 35 | (reagent/render [views/repl-component console-key {:eval-opts (replumb-proxy/eval-opts verbose-repl? src-paths) 36 | :mode (:mode local-storage-values) 37 | :mode-line? true 38 | :on-after-change #(do (dispatch [:input console-key (common/source-without-prompt (.getValue %))]) 39 | (dispatch [:focus console-key true]) 40 | (app/create-dictionary (common/source-without-prompt (.getValue %)) console-key) 41 | (utils/align-suggestions-list %2))}] 42 | (.getElementById js/document "app-center")) 43 | (reagent/render [views/bottom-panel] (.getElementById js/document "app-bottom")) 44 | (reagent/render [views/footer-component] (.getElementById js/document "app-footer")))) 45 | -------------------------------------------------------------------------------- /src/cljs/cljs_repl_web/gist.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-repl-web.gist 2 | (:require [ajax.core :as ajax] 3 | [goog.crypt.base64 :as b64])) 4 | 5 | ;;;;;;;;;;;;;;;;;;;;;;;;;; 6 | ;;; Gist integration ;;; 7 | ;;;;;;;;;;;;;;;;;;;;;;;;;; 8 | 9 | (defonce gist-api-url "https://api.github.com") 10 | 11 | (defn generate-gist-filename 12 | "Generates a file name for a gist." 13 | [prefix ext] 14 | (str prefix \- (.getTime (js/Date.)) \. ext)) 15 | 16 | (defn encode64 17 | "Encodes the username and password." 18 | [username password] 19 | (b64/encodeString (str username ":" password))) 20 | 21 | (defn create-gist 22 | "Given a username and password, creates a gist via an ajax call. 23 | On success `handler` will be called, otherwise `error-handler`" 24 | [username password text handler error-handler] 25 | (ajax/ajax-request 26 | {:uri (str gist-api-url "/gists") 27 | :method :post 28 | :headers {:Authorization (str "Basic " (encode64 username password))} 29 | :params {:description "ClojureScript REPL dump" 30 | :public true 31 | :files { (generate-gist-filename "dump" "cljs") {:content text}} } 32 | :handler handler 33 | :error-handler error-handler 34 | :format (ajax/json-request-format) 35 | :response-format (ajax/json-response-format {:keywords? true})} )) 36 | -------------------------------------------------------------------------------- /src/cljs/cljs_repl_web/handlers.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-repl-web.handlers 2 | (:require [re-frame.core :refer [register-handler dispatch]] 3 | [replumb.core :as replumb] 4 | [clairvoyant.core :refer-macros [trace-forms]] 5 | [re-frame-tracer.core :refer [tracer]] 6 | [re-console.app :as rc-app] 7 | [cljs-repl-web.io :as io] 8 | [cljs-repl-web.app :as app] 9 | [cljs-repl-web.views.utils :as utils] 10 | [cljs-repl-web.gist :as gist] 11 | [cljs-repl-web.config :as config] 12 | [cljs-repl-web.localstorage :as ls] 13 | [adzerk.cljs-console :as log :include-macros true])) 14 | 15 | ;; (trace-forms {:tracer (tracer :color "green")} 16 | 17 | ;; TODO Middleware 18 | 19 | ;;;;;;;;;;;;;;;;;; 20 | ;;; DB ;;; 21 | ;;;;;;;;;;;;;;;;;; 22 | 23 | (register-handler 24 | :reset-db 25 | (fn reset-db [_ [_ config local-storage-vals]] 26 | (app/make-init-state! config local-storage-vals))) 27 | 28 | ;; On app startup, create initial state 29 | (register-handler 30 | :initialize 31 | (fn initialize [_ [_ config local-storage-vals]] 32 | (log/debug "Initializing app...") 33 | ;; we load the cljs core cache manually in order to reduce the app size 34 | ;; see https://github.com/clojure/clojurescript/wiki/Optional-Self-hosting for more info 35 | ;; see also related issue in replbum https://github.com/ScalaConsultants/replumb/issues/42 36 | (io/load-cljs-core-cache! (:core-cache-url config)) 37 | (io/print-version! (:version-path config)) 38 | (app/register-media-queries!) 39 | (app/make-init-state! config local-storage-vals))) 40 | 41 | ;;;;;;;;;;;;;;;;;; 42 | ;; Gist ;;; 43 | ;;;;;;;;;;;;;;;;;; 44 | 45 | (register-handler 46 | :show-gist-login 47 | (fn show-gist-login [db [_]] 48 | (let [auth-data (app/gist-auth-data db) 49 | saved-username (app/gist-saved-username db)] 50 | (assoc db :gist-data {:gist-showing? true 51 | :auth-data (assoc auth-data :password "") 52 | :saved-username (:username auth-data)})))) 53 | 54 | (register-handler 55 | :hide-gist-login 56 | (fn hide-gist-login [db [_]] 57 | (let [saved-username (app/gist-saved-username db)] 58 | (assoc db :gist-data {:gist-showing? false 59 | :auth-data {:username saved-username 60 | :password ""}})))) 61 | 62 | (register-handler 63 | :create-gist 64 | (fn create-gist [db [_ console-key auth-data success-handler error-handler]] 65 | (let [{:keys [username password]} @auth-data 66 | items (get-in db [:consoles (name console-key) :items]) 67 | text (rc-app/console-full-text db console-key) 68 | empty-usr? (empty? username) 69 | empty-pwd? (empty? password) 70 | empty-txt? (empty? text) 71 | valid (not (or empty-usr? empty-pwd? empty-txt?))] 72 | 73 | (when valid 74 | (ls/save-username! username) 75 | (gist/create-gist username password text success-handler error-handler)) 76 | 77 | (assoc db :gist-data {:gist-showing? false 78 | :auth-data (dissoc @auth-data :password) 79 | :saved-username (:username @auth-data)})))) 80 | 81 | (register-handler 82 | :reset-gist-error-msg 83 | (fn reset-gist-error-msg [db [_]] 84 | (assoc-in db [:gist-data :error-msg] ""))) 85 | 86 | (register-handler 87 | :set-gist-error-msg 88 | (fn set-gist-error-msg [db [_ msg]] 89 | (assoc-in db [:gist-data :error-msg] msg))) 90 | 91 | ;;;;;;;;;;;;;;;;;;;;;;; 92 | ;; Media Queries ;;; 93 | ;;;;;;;;;;;;;;;;;;;;;;; 94 | 95 | (register-handler 96 | :media-match 97 | (fn media-match [db [_ media-matched]] 98 | (assoc db :media-query-size media-matched))) 99 | 100 | ;;;;;;;;;;;;;;;;;;;;;;; 101 | ;; Console mode ;;; 102 | ;;;;;;;;;;;;;;;;;;;;;;; 103 | 104 | (register-handler 105 | :switch-console-mode 106 | (fn switch-console-mode [db [_ new-mode]] 107 | (dispatch [:set-console-mode :cljs-console new-mode]) 108 | (ls/save-mode! new-mode) 109 | db)) 110 | 111 | 112 | ;; ) 113 | 114 | ;;;;;;;;;;;;;;;;;;;;;;; 115 | ;; Re-complete ;;; 116 | ;;;;;;;;;;;;;;;;;;;;;;; 117 | 118 | (register-handler 119 | :console-set-autocompleted-text 120 | (fn console-set-text [db [_ console-key]] 121 | (rc-app/set-console-text db console-key (get-in db [:re-complete :linked-components (keyword console-key) :text])))) 122 | -------------------------------------------------------------------------------- /src/cljs/cljs_repl_web/io.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-repl-web.io 2 | (:require [cljs.js :as cljs] 3 | [clojure.string :as string] 4 | [cognitect.transit :as transit] 5 | [replumb.repl :as replumb-repl] 6 | [adzerk.cljs-console :as log :include-macros true]) 7 | (:import [goog.events EventType] 8 | [goog.net XhrIo])) 9 | 10 | (defn fetch-file! 11 | "Very simple implementation of XMLHttpRequests that given a file path 12 | calls src-cb with the string fetched of nil in case of error. 13 | 14 | See doc at https://developers.google.com/closure/library/docs/xhrio" 15 | [file-url src-cb] 16 | (try 17 | (.send XhrIo file-url 18 | (fn [e] 19 | (if (.isSuccess (.-target e)) 20 | (src-cb (.. e -target getResponseText)) 21 | (src-cb nil)))) 22 | (catch :default e 23 | (src-cb nil)))) 24 | 25 | (defn load-cljs-core-cache! 26 | "Load core.cljs.cache.aot.json given the url" 27 | [cache-url] 28 | (fetch-file! cache-url 29 | (fn [txt] 30 | (let [rdr (transit/reader :json) 31 | cache (transit/read rdr txt)] 32 | (cljs/load-analysis-cache! replumb-repl/st 'cljs.core cache))))) 33 | 34 | (defn print-version! 35 | "Return the current version from the version.properties file." 36 | [version-path] 37 | (fetch-file! version-path 38 | (fn [content] 39 | (let [version (second (string/split (->> (string/split-lines content) 40 | (remove #(= "#" (first %))) 41 | first) 42 | #"=" 2))] 43 | (log/info "[Version] ~{version}"))))) 44 | 45 | (comment 46 | (def s "#Tue Feb 16 13:27:59 PST 2016\nVERSION=0.2.2-ar\nOTHER=STUFF") 47 | ) 48 | -------------------------------------------------------------------------------- /src/cljs/cljs_repl_web/localstorage.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-repl-web.localstorage) 2 | 3 | ;;; see https://gist.github.com/daveliepmann/cf923140702c8b1de301 4 | 5 | ;;; Utils functions 6 | 7 | (defn set-item! 8 | "Set `key' in browser's localStorage to `val`." 9 | [key val] 10 | (.setItem (.-localStorage js/window) key val)) 11 | 12 | (defn get-item 13 | "Returns value of `key' from browser's localStorage." 14 | [key] 15 | (.getItem (.-localStorage js/window) key)) 16 | 17 | (defn remove-item! 18 | "Remove the browser's localStorage value for the given `key`" 19 | [key] 20 | (.removeItem (.-localStorage js/window) key)) 21 | 22 | ;;; getters 23 | 24 | (defn get-local-storage-values 25 | [] 26 | {:mode (if-let [mode (get-item "cljs-repl-web-mode")] 27 | (keyword mode) 28 | :indent-mode) 29 | :username (or (get-item "cljs-repl-web-username") "")}) 30 | 31 | (defn save-mode! 32 | [mode] 33 | (set-item! "cljs-repl-web-mode" (name mode))) 34 | 35 | (defn save-username! 36 | [mode] 37 | (set-item! "cljs-repl-web-username" (name mode))) 38 | 39 | -------------------------------------------------------------------------------- /src/cljs/cljs_repl_web/markdown.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-repl-web.markdown 2 | "Core implementation of reagent-markdown. A small utility that renders 3 | Markdown to react/reagent components. Most of the code has been 4 | borrowed from devcards, courtesy of Bruce Hauman." 5 | (:require [clojure.string :as string] 6 | [cljsjs.showdown] 7 | [reagent.core :as reagent])) 8 | 9 | (declare parse-out-blocks markdown-block->react) 10 | 11 | ;;;;;;;;;;;;;;;; 12 | ;;; MAIN API ;;; 13 | ;;;;;;;;;;;;;;;; 14 | 15 | (defn md->react-component 16 | "Interprets the input string(s) as Markdown, yielding a react 17 | component. It wraps the generated HTML in
\"> or
if container-class is not given as 20 | first parameter. In case of failure a message is logged to console and 21 | an error
is added to the DOM." 22 | ([& strs] 23 | (if (every? string? strs) 24 | (let [blocks (mapcat parse-out-blocks strs)] 25 | [:div {:class "reagent-markdown-block"} 26 | (map markdown-block->react blocks)]) 27 | (let [message "Error: The input should be a seq of strings containing less-sensitive markdown."] 28 | (try (.error js/console message)) 29 | [:div {:style {:color "#a94442"}} message])))) 30 | 31 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 32 | ;;; IMPLEMENTATION by Bruce Hauman ;;; 33 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 34 | 35 | (defn react-raw [raw-html-str] 36 | "A React component that renders raw html." 37 | (.div (.-DOM js/React) 38 | (clj->js { :key (str (hash raw-html-str)) 39 | :dangerouslySetInnerHTML 40 | { :__html 41 | raw-html-str}}))) 42 | 43 | (defn ref->node [this ref] 44 | (when-let [comp (aget (.. this -refs) ref)] 45 | (js/React.findDOMNode comp))) 46 | 47 | (defn get-props [this k] 48 | (aget (.-props this) (name k))) 49 | 50 | (defn leading-space-count [s] 51 | (when-let [ws (second (re-matches #"^([\s]*).*" s))] 52 | (.-length ws))) 53 | 54 | (let [conv-class (.-converter js/Showdown) 55 | converter (conv-class.)] 56 | (defn markdown-to-html 57 | "render markdown" 58 | [markdown-txt] 59 | (.makeHtml converter markdown-txt))) 60 | 61 | (defn matches-delim? [line] 62 | (re-matches #"^[\s]*```(\w*).*" line)) 63 | 64 | (defmulti block-parser 65 | (fn [{:keys [stage]} line] 66 | [(if (matches-delim? line) :delim :line) (:type stage)])) 67 | 68 | (defmethod block-parser [:line :markdown] [{:keys [stage] :as st} line] 69 | (update-in st [:stage :content] conj (string/trim line))) 70 | 71 | (defmethod block-parser [:line :code-block] [{:keys [stage] :as st} line] 72 | (update-in st [:stage :content] conj (subs line (:leading-spaces stage)))) 73 | 74 | (defmethod block-parser [:delim :markdown] [{:keys [stage accum] :as st} line];; enter block 75 | (let [lang (second (matches-delim? line))] 76 | (-> st ;; the beginning 77 | (assoc :accum (conj accum stage)) 78 | (assoc :stage 79 | {:type :code-block 80 | :lang (when-not (string/blank? lang) lang) 81 | :leading-spaces (leading-space-count line) 82 | :content []})))) 83 | 84 | (defmethod block-parser [:delim :code-block] [{:keys [stage accum] :as st} line];; enter block 85 | (-> st ;; the end 86 | (assoc :accum (conj accum stage)) 87 | (assoc :stage {:type :markdown :content []}))) 88 | 89 | (defn parse-out-blocks* [m] 90 | (reduce block-parser 91 | {:stage {:type :markdown :content []} :accum []} 92 | (string/split m "\n"))) 93 | 94 | (defn parse-out-blocks [m] 95 | (let [{:keys [stage accum]} (parse-out-blocks* m)] 96 | (->> (conj accum stage) 97 | (filter (fn [{:keys [content]}] (not-empty content))) 98 | (map (fn [x] (update-in x [:content] #(string/join "\n" %))))))) 99 | 100 | (defmulti markdown-block->react :type) 101 | 102 | (defmethod markdown-block->react :default [{:keys [content]}] 103 | (-> content markdown-to-html react-raw)) 104 | 105 | (defmethod markdown-block->react :code-block [{:keys [content] :as block}] 106 | (reagent/create-class 107 | {:render 108 | (fn [this] 109 | [:pre {:className ""} 110 | [:code {:className (or (get-props this :lang) "") 111 | :ref "code-ref"} 112 | (get-props this :code)]])})) 113 | -------------------------------------------------------------------------------- /src/cljs/cljs_repl_web/replumb_proxy.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-repl-web.replumb-proxy 2 | (:require [replumb.core :as replumb] 3 | [replumb.repl :as replumb-repl] 4 | [cljs-repl-web.io :as io] 5 | [cljs-repl-web.config :as config] 6 | [adzerk.cljs-console :as log :include-macros true])) 7 | 8 | (defn replumb-options 9 | "Options for replumb.core/read-eval-call. 10 | 11 | Read the docs at https://github.com/ScalaConsultants/replumb" 12 | [verbose? src-paths] 13 | (merge (replumb/options :browser src-paths io/fetch-file!) 14 | {:warning-as-error true 15 | :verbose verbose?})) 16 | 17 | (defn read-eval-call [opts cb source] 18 | (let [ns (replumb-repl/current-ns)] 19 | 20 | (replumb/read-eval-call opts 21 | (fn [result] 22 | (log/debug "Top of read-eval-call cb: ~{result}") 23 | (cb {:success? (replumb/success? result) 24 | :result result 25 | :prev-ns ns 26 | :source source})) 27 | source))) 28 | 29 | (defn multiline? 30 | [input] 31 | (try 32 | (replumb-repl/read-string {:features #{:cljs}} input) 33 | false 34 | (catch :default e 35 | (log/warn "multiline? caught @{e}") 36 | (= "EOF" (subs (.-message e) 0 3))))) 37 | 38 | (defn eval-opts 39 | [verbose src-path] 40 | {:get-prompt replumb/get-prompt 41 | :should-eval (complement multiline?) 42 | :to-str-fn (partial replumb/result->string false true) 43 | :evaluate (partial read-eval-call 44 | (replumb-options verbose src-path))}) 45 | 46 | -------------------------------------------------------------------------------- /src/cljs/cljs_repl_web/subs.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-repl-web.subs 2 | (:require [reagent.ratom :refer [make-reaction]] 3 | [re-frame.core :refer [register-sub subscribe]] 4 | [clairvoyant.core :refer-macros [trace-forms]] 5 | [re-frame-tracer.core :refer [tracer]] 6 | [cljs-repl-web.app :as app] 7 | [cljs-repl-web.views.utils :as view-utils])) 8 | 9 | ;; (trace-forms {:tracer (tracer :color "brown")} 10 | 11 | ;;;;;;;;;;;;;;;;;; 12 | ;; APIs ;;; 13 | ;;;;;;;;;;;;;;;;;; 14 | 15 | (register-sub 16 | :api-panel-section-columns 17 | (fn [db [_ sections]] 18 | (let [mq (subscribe [:media-query-size])] 19 | (make-reaction (fn api-panel-section-columns [] 20 | (let [cols (view-utils/api-panel-column-number @mq) 21 | secs (if-not (= :narrow @mq) 22 | sections 23 | (filter #(= :symbols (get-in % [:additional-info :type])) sections))] 24 | (partition-all (let [secs (count sections)] 25 | (if (zero? (rem secs cols)) 26 | (quot secs cols) 27 | (inc (quot secs cols)))) 28 | secs))))))) 29 | 30 | (register-sub 31 | :api-panel-column-number 32 | (fn [db [_]] 33 | (let [mq (subscribe [:media-query-size])] 34 | (make-reaction (fn api-panel-column-number [] 35 | (view-utils/api-panel-column-number @mq)))))) 36 | 37 | ;;;;;;;;;;;;;;;;;; 38 | ;; Footer ;;; 39 | ;;;;;;;;;;;;;;;;;; 40 | 41 | (register-sub 42 | :footer-column-number 43 | (fn [db [_]] 44 | (let [mq (subscribe [:media-query-size])] 45 | (make-reaction (fn footer-column-number [] 46 | (view-utils/footer-column-number @mq)))))) 47 | 48 | ;;;;;;;;;;;;;;;;;; 49 | ;; Gist ;;; 50 | ;;;;;;;;;;;;;;;;;; 51 | 52 | (register-sub 53 | :gist-showing? 54 | (fn [db [_]] 55 | (make-reaction (fn gist-showing? [] 56 | (app/gist-showing? @db))))) 57 | 58 | (register-sub 59 | :gist-auth-data 60 | (fn [db [_]] 61 | (make-reaction (fn gist-auth-data [] 62 | (app/gist-auth-data @db))))) 63 | 64 | (register-sub 65 | :gist-error-msg 66 | (fn [db [_]] 67 | (make-reaction (fn gist-error-msg [] 68 | (app/gist-error-msg @db))))) 69 | 70 | (register-sub 71 | :can-dump-gist? 72 | (fn [db [_ console-key]] 73 | (let [items (subscribe [:get-console-items console-key])] 74 | (make-reaction (fn can-save-gist? [] (nil? (seq @items))))))) 75 | 76 | ;;;;;;;;;;;;;;;;;;;;;;; 77 | ;; Media Queries ;;; 78 | ;;;;;;;;;;;;;;;;;;;;;;; 79 | 80 | (register-sub 81 | :media-query-size 82 | (fn [db [_]] 83 | (make-reaction (fn media-query-size [] 84 | (app/media-query-size @db))))) 85 | 86 | 87 | ;;;;;;;;;;;;;;;;;;;;;;; 88 | ;; Re complete ;;; 89 | ;;;;;;;;;;;;;;;;;;;;;;; 90 | 91 | 92 | (register-sub 93 | :get-options 94 | (fn [db [_ console-key]] 95 | (make-reaction (fn get-options [] (get-in @db [:autocomplete :linked-components console-key :options]))))) 96 | 97 | 98 | ;; ) 99 | -------------------------------------------------------------------------------- /src/cljs/cljs_repl_web/views/utils.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-repl-web.views.utils 2 | (:require-macros [re-com.core :refer [handler-fn]]) 3 | (:require [cljs.pprint :as pprint :refer [pprint]] 4 | [clojure.string :as string] 5 | [clojure.walk :as walk] 6 | [clojure.zip :as zip] 7 | [reagent.core :as reagent] 8 | [re-com.core :refer [popover-tooltip]] 9 | [hickory.core :as hickory] 10 | [hickory.zip :as hzip] 11 | [hickory.convert :as hconvert] 12 | [goog.string :as gstring] 13 | [re-console.utils :as utils])) 14 | 15 | (def clojuredocs-url "http://clojuredocs.org/") 16 | 17 | ;; Some namespaces in cljs don't have a match in clj 18 | ;; we could also use :clj-symbol in the generator, but 19 | ;; for now it's just one namepsace 20 | (def cljs-special-ns->clj-ns {"special" "clojure.core"}) 21 | 22 | ;; We need to convert some special characters to generate the url 23 | (def url-special-characters {#"/" "_fs" 24 | #"\?$" "_q" 25 | #"^\." "_." }) 26 | 27 | (defn strip-namespace 28 | "Given a cljs symbol, strip the namespace part. `sym` must be 29 | fully qualified." 30 | [sym] 31 | (second (string/split (str sym) #"/" 2))) 32 | 33 | (defn symbol->clojuredocs-url 34 | "Given a cljs symbol (with fully qualified ns or without, in which 35 | case it defaults to clojure.core), returns the url to ClojureDocs 36 | documentation." 37 | [cljs-symbol] 38 | (let [[ns symbol] (string/split (str cljs-symbol) #"/" 2) 39 | url-ns (or (cljs-special-ns->clj-ns ns) ns) 40 | url-symbol (reduce #(apply string/replace %1 %2) symbol url-special-characters)] 41 | (str clojuredocs-url (if url-ns 42 | (string/replace url-ns #"cljs" "clojure") 43 | "clojure.core") 44 | "/" url-symbol))) 45 | 46 | (defn number->word 47 | "From: http://clojurescriptmadeeasy.com/blog/how-to-humanize-text-cl-format.html 48 | If (< 0 n 10) use the ~R directive, otherwise use ~A" 49 | [n] 50 | (pprint/cl-format nil "~:[~a~;~r~]" (< 0 n 10) n)) 51 | 52 | (defn calculate-popover-position 53 | "Calculates the tooltip orientation for a given symbol." 54 | [[x y]] 55 | (let [h (.-innerHeight js/window) 56 | w (.-innerWidth js/window) 57 | v-threshold (quot h 2) 58 | v-position (if (< y v-threshold) "below" "above") 59 | h-threshold-left (quot w 3) 60 | h-threshold-cent (* 2 h-threshold-left) 61 | h-position (cond 62 | (< x h-threshold-left) "right" 63 | (< x h-threshold-cent) "center" 64 | :else "left")] 65 | (keyword (str v-position \- h-position)))) 66 | 67 | (defn message-box 68 | "Displays a popup with a message." 69 | [message] 70 | ;; we can later modify it to use re-com 71 | (js/alert message)) 72 | 73 | (defn open-new-window 74 | "Opens a new window in the browser (or tab - depends on browser)." 75 | ([url target] (.open js/window url target)) 76 | ([url] (open-new-window url "_blank"))) 77 | 78 | (defn scroll-to-top 79 | [] 80 | (.scrollTo js/window 0 0)) 81 | 82 | (defn unescape-html 83 | "Walk a given Hiccup form and remove all pure whitespace." 84 | [hiccup] 85 | (walk/prewalk 86 | (fn [form] 87 | (if (string? form) 88 | (goog.string/unescapeEntities form) 89 | form)) 90 | hiccup)) 91 | 92 | (defn trim-strings 93 | "Walk a given Hiccup form and remove all pure whitespace." 94 | [hiccup] 95 | (walk/prewalk 96 | (fn [form] 97 | (if (string? form) 98 | (string/trim form) 99 | form)) 100 | hiccup)) 101 | 102 | (defn add-node-class 103 | "Given a loc's node, adds a class (as either string or keyword) and 104 | returns the new node. Usually used like: (zip/edit loc 105 | add-loc-class :class-name)" 106 | [node class-name] 107 | (update-in node [:attrs :class] (fn [old-str] 108 | (if (empty? old-str) 109 | (name class-name) 110 | (str old-str " " (name class-name)))))) 111 | 112 | (def as-hickory-zip (comp hzip/hickory-zip 113 | hickory/as-hickory)) 114 | 115 | (defn inject-node-highlight 116 | "Given a loc's node, replaces its string :content with highlighted 117 | nodes, returns the new node. Usually used like: (zip/edit loc 118 | inject-node-highlight)" 119 | [node] 120 | (update-in node [:content] (fn [old-content] 121 | (if-let [s (first old-content)] 122 | (if (string? s) 123 | (let [parsed-hls (hickory/parse-fragment s)] 124 | (mapv (comp zip/root as-hickory-zip) parsed-hls)) 125 | old-content) 126 | old-content)))) 127 | 128 | (defn highlight-code-locs 129 | "Given a hickory zipper, highlights all the locs." 130 | [hickory-zip-root] 131 | (loop [loc hickory-zip-root] 132 | (if-not (zip/end? loc) 133 | (recur (zip/next (if (= :code (:tag (zip/node loc))) 134 | (-> loc 135 | (zip/edit add-node-class :hljs) 136 | (zip/edit inject-node-highlight)) 137 | loc))) 138 | loc))) 139 | 140 | (defn html-string->hiccup 141 | "Convert a html string to hiccup vector, optionally merging class, 142 | style and attr if present as variadic params." 143 | [html-string & {:keys [class style attr]}] 144 | (into [:div (merge {:class class 145 | :style style} 146 | attr)] 147 | (map (comp unescape-html hickory/as-hiccup) 148 | (hickory/parse-fragment html-string)))) 149 | 150 | (defn html-string->highligthed-hiccup 151 | "Convert a html string to a hiccup vector, optionally merging class, 152 | style and attr if present as variadic params." 153 | [html-string & {:keys [class style attr]}] 154 | (into [:div (merge {:class class 155 | :style style} 156 | attr)] 157 | (->> html-string 158 | hickory/parse-fragment 159 | (map hickory/as-hiccup) 160 | (mapcat identity) 161 | (filter #(or (string? %) (vector? %))) 162 | (map (fn [item] 163 | (if (string? item) 164 | [:p (goog.string/unescapeEntities item)] 165 | [utils/colored-text (goog.string/unescapeEntities (last item)) {:padding "10"}])))))) 166 | 167 | (comment 168 | (require '[clairvoyant.core :as t :include-macros true]) 169 | (require '[hickory.core :as hickory]) 170 | (require '[hickory.render :as hrender]) 171 | (require '[hickory.convert :as hconver]) 172 | (require '[hickory.zip :as hzip]) 173 | (require '[clojure.zip :as zip]) 174 | (require '[cljs-browser-repl.highlight :as hl]) 175 | (require '[cljs.pprint :refer [pprint]]) 176 | 177 | (def h1 (hl/highlight-html "(def a \"b\")")) 178 | 179 | (def s1 "
(def a \"b\")
") 180 | (def s2 "
(def a \"b\")
(defn myfun [coll] (first coll))
") 181 | 182 | (def h (map as-hickory-zip (hickory/parse-fragment s2))) 183 | (map highlight-code-locs h) 184 | 185 | (html-string->highligthed-hiccup s2)) 186 | 187 | ;; AR - Not used anywhere atm 188 | (defn inject-attributes 189 | "Walk a given Hiccup form and inject the attribute map, merging (with 190 | precedence to attr-map) with the pre-existing one if any." 191 | [attr-map hiccup] 192 | (println hiccup) 193 | ;; having fun 194 | (assoc hiccup 1 (merge-with merge (when (map? (nth hiccup 1)) 195 | (nth hiccup 1)) 196 | attr-map))) 197 | 198 | (defn api-panel-column-number 199 | "Given a media-query, returns the number of columns for the 200 | api-panel. Throws if media query is not one of the handled ones." 201 | [media-query] 202 | (case media-query 203 | (:narrow :medium) 1 204 | :wide 2)) 205 | 206 | (defn footer-column-number 207 | "Given a media-query, returns the number of columns for the 208 | footer. Throws if media query is not one of the handled ones." 209 | [media-query] 210 | (case media-query 211 | (:narrow :medium) 1 212 | :wide 2)) 213 | 214 | (defn get-el-offset 215 | [el offset-fn] 216 | (loop [total 0 217 | el el] 218 | (if el 219 | (recur (+ total (offset-fn el)) (.-offsetParent el)) 220 | total))) 221 | 222 | (defn align-suggestions-list 223 | [evt] 224 | ;; we set the timeout function so every value is updated, eg. height 225 | (.setTimeout js/window (fn [] 226 | (let [container (first (array-seq (.getElementsByClassName js/document "re-console-container"))) 227 | cursor (first (array-seq (.getElementsByClassName js/document "CodeMirror-cursor"))) 228 | completions-list (first (array-seq (.getElementsByClassName js/document "re-completion-list"))) 229 | code-mirror (first (array-seq (.getElementsByClassName js/document "CodeMirror"))) 230 | left-threshold (- (+ (get-el-offset container #(.-offsetLeft %)) (.-offsetWidth container)) 231 | (.-offsetWidth completions-list) 232 | 10) 233 | ;; if backspace or delete, use minus (-) otherwise plus (+) 234 | left ((if (= "+delete" (.-origin evt)) - +) (get-el-offset cursor #(.-offsetLeft %)) 8) 235 | top (if (> (.-offsetTop code-mirror) (/ (.-offsetHeight container) 2)) 236 | (- (.-offsetTop code-mirror) (.-scrollTop container) (.-offsetHeight completions-list) 5) 237 | (+ 5 (- (.-offsetTop code-mirror) (.-scrollTop container)) (.-offsetHeight code-mirror)))] 238 | (set! (.-left (.-style completions-list)) (if (> left left-threshold) 239 | (str left-threshold "px") 240 | (str left "px"))) 241 | (set! (.-top (.-style completions-list)) (str top "px")))) 242 | 20)) 243 | -------------------------------------------------------------------------------- /test/cljs/cljs_repl_web/console/cljs_test.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-repl-web.console.cljs-test 2 | (:require [cljs.test :refer-macros [deftest is]] 3 | [cljs.core.async :refer [! chan close! put! timeout]]) 4 | (:require-macros [cljs.core.async.macros :refer [go alt!]])) 5 | 6 | (deftest require 7 | ;; AR - here goes the (require 'clojure.string) test 8 | ;; We well probably need core.async, that is why I added it in the deps. 9 | #_(async done 10 | (go ...))) 11 | -------------------------------------------------------------------------------- /test/cljs/cljs_repl_web/core_test.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs-repl-web.core-test 2 | (:require [cljs.test :refer-macros [deftest is async]] 3 | [cljs.core.async :as async :refer [chan put!