├── .gitignore ├── .lein-env ├── LICENSE.html ├── README.md ├── externs.js ├── project.clj ├── resources └── public │ ├── css │ ├── bootstrap.css │ ├── bootstrap.min.css │ ├── codemirror.css │ └── style.css │ ├── favicon.ico │ └── js │ ├── addon │ └── edit │ │ ├── closebrackets.js │ │ └── matchbrackets.js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── codemirror.js │ └── mode │ ├── clojure │ └── clojure.js │ └── css │ └── css.js ├── src ├── clj │ └── cljsfiddle │ │ ├── closure.clj │ │ ├── db.clj │ │ ├── db │ │ ├── blob.clj │ │ ├── fiddle.clj │ │ ├── schema.clj │ │ ├── src.clj │ │ └── util.clj │ │ ├── handler.clj │ │ ├── import.clj │ │ └── views.clj └── cljs │ └── cljsfiddle │ └── core.cljs └── test └── cljsfiddle └── test └── handler.clj /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /lib 3 | /classes 4 | /checkouts 5 | /pg 6 | pom.xml 7 | *.jar 8 | *.class 9 | .lein-deps-sum 10 | .lein-failures 11 | .lein-plugins 12 | .lein-repl-history 13 | .env 14 | resources/public/js/app.js* 15 | resources/public/js/out 16 | resources/jscache/ 17 | *~ 18 | libpeerconnection.log 19 | -------------------------------------------------------------------------------- /.lein-env: -------------------------------------------------------------------------------- 1 | ;; For dev use 2 | { 3 | :datomic-uri "datomic:free://localhost:4334/cljsfiddle" 4 | 5 | ;; Registered for localhost:8080 6 | :github-client-id "d220094711eae05f92ee" 7 | :github-client-secret "9514e83d9157e01b3962c162d6e50c0ad3a9b00d" 8 | 9 | ;; 16 random characters 10 | :session-secret "1234567891234567" 11 | } -------------------------------------------------------------------------------- /LICENSE.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Eclipse Public License - Version 1.0 8 | 25 | 26 | 27 | 28 | 29 | 30 |

Eclipse Public License - v 1.0

31 | 32 |

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE 33 | PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR 34 | DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS 35 | AGREEMENT.

36 | 37 |

1. DEFINITIONS

38 | 39 |

"Contribution" means:

40 | 41 |

a) in the case of the initial Contributor, the initial 42 | code and documentation distributed under this Agreement, and

43 |

b) in the case of each subsequent Contributor:

44 |

i) changes to the Program, and

45 |

ii) additions to the Program;

46 |

where such changes and/or additions to the Program 47 | originate from and are distributed by that particular Contributor. A 48 | Contribution 'originates' from a Contributor if it was added to the 49 | Program by such Contributor itself or anyone acting on such 50 | Contributor's behalf. Contributions do not include additions to the 51 | Program which: (i) are separate modules of software distributed in 52 | conjunction with the Program under their own license agreement, and (ii) 53 | are not derivative works of the Program.

54 | 55 |

"Contributor" means any person or entity that distributes 56 | the Program.

57 | 58 |

"Licensed Patents" mean patent claims licensable by a 59 | Contributor which are necessarily infringed by the use or sale of its 60 | Contribution alone or when combined with the Program.

61 | 62 |

"Program" means the Contributions distributed in accordance 63 | with this Agreement.

64 | 65 |

"Recipient" means anyone who receives the Program under 66 | this Agreement, including all Contributors.

67 | 68 |

2. GRANT OF RIGHTS

69 | 70 |

a) Subject to the terms of this Agreement, each 71 | Contributor hereby grants Recipient a non-exclusive, worldwide, 72 | royalty-free copyright license to reproduce, prepare derivative works 73 | of, publicly display, publicly perform, distribute and sublicense the 74 | Contribution of such Contributor, if any, and such derivative works, in 75 | source code and object code form.

76 | 77 |

b) Subject to the terms of this Agreement, each 78 | Contributor hereby grants Recipient a non-exclusive, worldwide, 79 | royalty-free patent license under Licensed Patents to make, use, sell, 80 | offer to sell, import and otherwise transfer the Contribution of such 81 | Contributor, if any, in source code and object code form. This patent 82 | license shall apply to the combination of the Contribution and the 83 | Program if, at the time the Contribution is added by the Contributor, 84 | such addition of the Contribution causes such combination to be covered 85 | by the Licensed Patents. The patent license shall not apply to any other 86 | combinations which include the Contribution. No hardware per se is 87 | licensed hereunder.

88 | 89 |

c) Recipient understands that although each Contributor 90 | grants the licenses to its Contributions set forth herein, no assurances 91 | are provided by any Contributor that the Program does not infringe the 92 | patent or other intellectual property rights of any other entity. Each 93 | Contributor disclaims any liability to Recipient for claims brought by 94 | any other entity based on infringement of intellectual property rights 95 | or otherwise. As a condition to exercising the rights and licenses 96 | granted hereunder, each Recipient hereby assumes sole responsibility to 97 | secure any other intellectual property rights needed, if any. For 98 | example, if a third party patent license is required to allow Recipient 99 | to distribute the Program, it is Recipient's responsibility to acquire 100 | that license before distributing the Program.

101 | 102 |

d) Each Contributor represents that to its knowledge it 103 | has sufficient copyright rights in its Contribution, if any, to grant 104 | the copyright license set forth in this Agreement.

105 | 106 |

3. REQUIREMENTS

107 | 108 |

A Contributor may choose to distribute the Program in object code 109 | form under its own license agreement, provided that:

110 | 111 |

a) it complies with the terms and conditions of this 112 | Agreement; and

113 | 114 |

b) its license agreement:

115 | 116 |

i) effectively disclaims on behalf of all Contributors 117 | all warranties and conditions, express and implied, including warranties 118 | or conditions of title and non-infringement, and implied warranties or 119 | conditions of merchantability and fitness for a particular purpose;

120 | 121 |

ii) effectively excludes on behalf of all Contributors 122 | all liability for damages, including direct, indirect, special, 123 | incidental and consequential damages, such as lost profits;

124 | 125 |

iii) states that any provisions which differ from this 126 | Agreement are offered by that Contributor alone and not by any other 127 | party; and

128 | 129 |

iv) states that source code for the Program is available 130 | from such Contributor, and informs licensees how to obtain it in a 131 | reasonable manner on or through a medium customarily used for software 132 | exchange.

133 | 134 |

When the Program is made available in source code form:

135 | 136 |

a) it must be made available under this Agreement; and

137 | 138 |

b) a copy of this Agreement must be included with each 139 | copy of the Program.

140 | 141 |

Contributors may not remove or alter any copyright notices contained 142 | within the Program.

143 | 144 |

Each Contributor must identify itself as the originator of its 145 | Contribution, if any, in a manner that reasonably allows subsequent 146 | Recipients to identify the originator of the Contribution.

147 | 148 |

4. COMMERCIAL DISTRIBUTION

149 | 150 |

Commercial distributors of software may accept certain 151 | responsibilities with respect to end users, business partners and the 152 | like. While this license is intended to facilitate the commercial use of 153 | the Program, the Contributor who includes the Program in a commercial 154 | product offering should do so in a manner which does not create 155 | potential liability for other Contributors. Therefore, if a Contributor 156 | includes the Program in a commercial product offering, such Contributor 157 | ("Commercial Contributor") hereby agrees to defend and 158 | indemnify every other Contributor ("Indemnified Contributor") 159 | against any losses, damages and costs (collectively "Losses") 160 | arising from claims, lawsuits and other legal actions brought by a third 161 | party against the Indemnified Contributor to the extent caused by the 162 | acts or omissions of such Commercial Contributor in connection with its 163 | distribution of the Program in a commercial product offering. The 164 | obligations in this section do not apply to any claims or Losses 165 | relating to any actual or alleged intellectual property infringement. In 166 | order to qualify, an Indemnified Contributor must: a) promptly notify 167 | the Commercial Contributor in writing of such claim, and b) allow the 168 | Commercial Contributor to control, and cooperate with the Commercial 169 | Contributor in, the defense and any related settlement negotiations. The 170 | Indemnified Contributor may participate in any such claim at its own 171 | expense.

172 | 173 |

For example, a Contributor might include the Program in a commercial 174 | product offering, Product X. That Contributor is then a Commercial 175 | Contributor. If that Commercial Contributor then makes performance 176 | claims, or offers warranties related to Product X, those performance 177 | claims and warranties are such Commercial Contributor's responsibility 178 | alone. Under this section, the Commercial Contributor would have to 179 | defend claims against the other Contributors related to those 180 | performance claims and warranties, and if a court requires any other 181 | Contributor to pay any damages as a result, the Commercial Contributor 182 | must pay those damages.

183 | 184 |

5. NO WARRANTY

185 | 186 |

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS 187 | PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 188 | OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, 189 | ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY 190 | OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely 191 | responsible for determining the appropriateness of using and 192 | distributing the Program and assumes all risks associated with its 193 | exercise of rights under this Agreement , including but not limited to 194 | the risks and costs of program errors, compliance with applicable laws, 195 | damage to or loss of data, programs or equipment, and unavailability or 196 | interruption of operations.

197 | 198 |

6. DISCLAIMER OF LIABILITY

199 | 200 |

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT 201 | NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, 202 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING 203 | WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF 204 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 205 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR 206 | DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED 207 | HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

208 | 209 |

7. GENERAL

210 | 211 |

If any provision of this Agreement is invalid or unenforceable under 212 | applicable law, it shall not affect the validity or enforceability of 213 | the remainder of the terms of this Agreement, and without further action 214 | by the parties hereto, such provision shall be reformed to the minimum 215 | extent necessary to make such provision valid and enforceable.

216 | 217 |

If Recipient institutes patent litigation against any entity 218 | (including a cross-claim or counterclaim in a lawsuit) alleging that the 219 | Program itself (excluding combinations of the Program with other 220 | software or hardware) infringes such Recipient's patent(s), then such 221 | Recipient's rights granted under Section 2(b) shall terminate as of the 222 | date such litigation is filed.

223 | 224 |

All Recipient's rights under this Agreement shall terminate if it 225 | fails to comply with any of the material terms or conditions of this 226 | Agreement and does not cure such failure in a reasonable period of time 227 | after becoming aware of such noncompliance. If all Recipient's rights 228 | under this Agreement terminate, Recipient agrees to cease use and 229 | distribution of the Program as soon as reasonably practicable. However, 230 | Recipient's obligations under this Agreement and any licenses granted by 231 | Recipient relating to the Program shall continue and survive.

232 | 233 |

Everyone is permitted to copy and distribute copies of this 234 | Agreement, but in order to avoid inconsistency the Agreement is 235 | copyrighted and may only be modified in the following manner. The 236 | Agreement Steward reserves the right to publish new versions (including 237 | revisions) of this Agreement from time to time. No one other than the 238 | Agreement Steward has the right to modify this Agreement. The Eclipse 239 | Foundation is the initial Agreement Steward. The Eclipse Foundation may 240 | assign the responsibility to serve as the Agreement Steward to a 241 | suitable separate entity. Each new version of the Agreement will be 242 | given a distinguishing version number. The Program (including 243 | Contributions) may always be distributed subject to the version of the 244 | Agreement under which it was received. In addition, after a new version 245 | of the Agreement is published, Contributor may elect to distribute the 246 | Program (including its Contributions) under the new version. Except as 247 | expressly stated in Sections 2(a) and 2(b) above, Recipient receives no 248 | rights or licenses to the intellectual property of any Contributor under 249 | this Agreement, whether expressly, by implication, estoppel or 250 | otherwise. All rights in the Program not expressly granted under this 251 | Agreement are reserved.

252 | 253 |

This Agreement is governed by the laws of the State of New York and 254 | the intellectual property laws of the United States of America. No party 255 | to this Agreement will bring a legal action under this Agreement more 256 | than one year after the cause of action arose. Each party waives its 257 | rights to a jury trial in any resulting litigation.

258 | 259 | 260 | 261 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CLJSFiddle 2 | 3 | [cljsfiddle.net](http://cljsfiddle.net) 4 | 5 | ## Local install 6 | 7 | ``` 8 | $ git clone https://github.com/jonase/cljsfiddle 9 | $ cd cljsfiddle 10 | $ wget https://my.datomic.com/downloads/free/0.9.4815 11 | $ unzip 0.9.4815 12 | ``` 13 | 14 | open another terminal and start datomic: 15 | 16 | ``` 17 | $ cd datomic-free-0.9.4815 18 | $ bin/transactor config/samples/free-transactor-template.properties 19 | ``` 20 | 21 | back to the previous terminal: 22 | 23 | ``` 24 | $ lein db-create 25 | $ lein db-assets 26 | $ lein cljsbuild once prod 27 | $ lein run -m cljsfiddle.handler 28 | ``` 29 | 30 | Go to http://localhost:8080. Have fun! 31 | 32 | ## License 33 | 34 | Copyright © 2014 Jonas Enlund 35 | 36 | Distributed under the Eclipse Public License, the same as Clojure. 37 | -------------------------------------------------------------------------------- /externs.js: -------------------------------------------------------------------------------- 1 | var CodeMirror; 2 | function CodeMirror(){}; 3 | CodeMirror.fromTextArea = {}; 4 | CodeMirror.refresh = {}; 5 | CodeMirror.getValue = {}; 6 | CodeMirror.setValue = {}; 7 | CodeMirror.setSize = {}; 8 | var $; 9 | function $(){}; 10 | $.on = {}; 11 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject cljsfiddle "0.1.0-SNAPSHOT" 2 | :description "CLJSFiddle" 3 | :url "http://cljsfiddle.net" 4 | :dependencies [[org.clojure/clojure "1.6.0"] 5 | [org.clojure/clojurescript "0.0-2227"] 6 | [org.clojure/tools.reader "0.8.4"] 7 | [org.clojure/core.match "0.2.1"] 8 | [org.clojure/core.async "0.1.303.0-886421-alpha"] 9 | [org.clojure/core.logic "0.8.7"] 10 | [org.clojure/tools.macro "0.1.5"] 11 | [com.datomic/datomic-free "0.9.4815"] 12 | [ring/ring-jetty-adapter "1.3.0"] 13 | [ring/ring-devel "1.3.0"] 14 | [fogus/ring-edn "0.2.0"] 15 | [commons-codec "1.9"] 16 | [me.raynes/fs "1.4.5"] 17 | [compojure "1.1.8"] 18 | [clj-http "0.9.2"] 19 | [cheshire "5.3.1"] 20 | [hiccup "1.0.5"] 21 | [environ "0.5.0"] 22 | [com.taoensso/timbre "3.2.1"] 23 | [hylla "0.2.0"] 24 | [domina "1.0.2"] 25 | [prismatic/dommy "0.1.2"] 26 | [hiccups "0.3.0"] 27 | [cljs-ajax "0.2.4"] 28 | [om "0.6.4"] 29 | [quiescent "0.1.3"] 30 | [reagent "0.4.2"]] 31 | :source-paths ["src/clj" "src/cljs"] 32 | :plugins [[lein-ring "0.8.10"] 33 | [lein-cljsbuild "1.0.3"]] 34 | ; :main cljsfiddle.handler 35 | ; :uberjar-name "cljsfiddle-standalone.jar" 36 | :min-lein-version "2.0.0" 37 | :profiles {:dev {:dependencies [[ring-mock "0.1.5"]]}} 38 | :ring {:handler cljsfiddle.handler/app 39 | :port 8080 40 | :stacktraces? true 41 | :auto-reload? true} 42 | :cljsbuild {:builds {:dev { 43 | :source-paths ["src/cljs"] 44 | :compiler {:output-to "resources/public/js/app.js" 45 | :output-dir "resources/public/js/out-dev" 46 | :source-map true 47 | :optimizations :simple 48 | :pretty-print true}} 49 | :prod { 50 | :source-paths ["src/cljs"] 51 | :compiler {:output-to "resources/public/js/app.js" 52 | :output-dir "resources/public/js/out" 53 | :optimizations :advanced 54 | :source-map "resources/public/js/app.js.map" 55 | :pretty-print false 56 | :elide-asserts true 57 | :static-fns true 58 | :externs ["externs.js"]}}}} 59 | :aliases {"db-create" ["trampoline" "run" "-m" "cljsfiddle.import/create-db" 60 | "datomic:free://localhost:4334/cljsfiddle"] 61 | "db-assets" ["trampoline" "run" "-m" "cljsfiddle.import" 62 | "datomic:free://localhost:4334/cljsfiddle"]}) 63 | -------------------------------------------------------------------------------- /resources/public/css/bootstrap.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.0.0 3 | * 4 | * Copyright 2013 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world by @mdo and @fat. 9 | *//*! normalize.css v2.1.0 | MIT License | git.io/normalize */article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden]{display:none}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{margin:.67em 0;font-size:2em}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}hr{height:0;-moz-box-sizing:content-box;box-sizing:content-box}mark{color:#000;background:#ff0}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em}pre{white-space:pre-wrap}q{quotes:"\201C" "\201D" "\2018" "\2019"}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:0}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid #c0c0c0}legend{padding:0;border:0}button,input,select,textarea{margin:0;font-family:inherit;font-size:100%}button,input{line-height:normal}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}button[disabled],html input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{padding:0;box-sizing:border-box}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}textarea{overflow:auto;vertical-align:top}table{border-collapse:collapse;border-spacing:0}@media print{*{color:#000!important;text-shadow:none!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}@page{margin:2cm .5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.table td,.table th{background-color:#fff!important}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table-bordered th,.table-bordered td{border:1px solid #ddd!important}}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:62.5%;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.428571429;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}img{vertical-align:middle}.img-responsive{display:inline-block;height:auto;max-width:100%}.img-rounded{border-radius:6px}.img-circle{border-radius:500px}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16.099999999999998px;font-weight:200;line-height:1.4}@media(min-width:768px){.lead{font-size:21px}}small{font-size:85%}cite{font-style:normal}.text-muted{color:#999}.text-primary{color:#428bca}.text-warning{color:#c09853}.text-danger{color:#b94a48}.text-success{color:#468847}.text-info{color:#3a87ad}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:500;line-height:1.1}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small{font-weight:normal;line-height:1;color:#999}h1,h2,h3{margin-top:20px;margin-bottom:10px}h4,h5,h6{margin-top:10px;margin-bottom:10px}h1,.h1{font-size:38px}h2,.h2{font-size:32px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}h1 small,.h1 small{font-size:24px}h2 small,.h2 small{font-size:18px}h3 small,.h3 small,h4 small,.h4 small{font-size:14px}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-bottom:20px}dt,dd{line-height:1.428571429}dt{font-weight:bold}dd{margin-left:0}.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}.dl-horizontal dd:before,.dl-horizontal dd:after{display:table;content:" "}.dl-horizontal dd:after{clear:both}.dl-horizontal dd:before,.dl-horizontal dd:after{display:table;content:" "}.dl-horizontal dd:after{clear:both}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;border-left:5px solid #eee}blockquote p{font-size:17.5px;font-weight:300;line-height:1.25}blockquote p:last-child{margin-bottom:0}blockquote small{display:block;line-height:1.428571429;color:#999}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}blockquote.pull-right small:before{content:''}blockquote.pull-right small:after{content:'\00A0 \2014'}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:1.428571429}code,pre{font-family:Monaco,Menlo,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;white-space:nowrap;background-color:#f9f2f4;border-radius:4px}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.428571429;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre.prettyprint{margin-bottom:20px}pre code{padding:0;color:inherit;white-space:pre-wrap;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto}.container:before,.container:after{display:table;content:" "}.container:after{clear:both}.container:before,.container:after{display:table;content:" "}.container:after{clear:both}.row:before,.row:after{display:table;content:" "}.row:after{clear:both}.row:before,.row:after{display:table;content:" "}.row:after{clear:both}@media(min-width:768px){.row{margin-right:-15px;margin-left:-15px}}.row .row{margin-right:-15px;margin-left:-15px}.col-1,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-10,.col-11,.col-12,.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12,.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-1,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-10,.col-11,.col-12{float:left}.col-1{width:8.333333333333332%}.col-2{width:16.666666666666664%}.col-3{width:25%}.col-4{width:33.33333333333333%}.col-5{width:41.66666666666667%}.col-6{width:50%}.col-7{width:58.333333333333336%}.col-8{width:66.66666666666666%}.col-9{width:75%}.col-10{width:83.33333333333334%}.col-11{width:91.66666666666666%}.col-12{width:100%}@media(min-width:768px){.container{max-width:728px}.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-1{width:8.333333333333332%}.col-sm-2{width:16.666666666666664%}.col-sm-3{width:25%}.col-sm-4{width:33.33333333333333%}.col-sm-5{width:41.66666666666667%}.col-sm-6{width:50%}.col-sm-7{width:58.333333333333336%}.col-sm-8{width:66.66666666666666%}.col-sm-9{width:75%}.col-sm-10{width:83.33333333333334%}.col-sm-11{width:91.66666666666666%}.col-sm-12{width:100%}.col-sm-push-1{left:8.333333333333332%}.col-sm-push-2{left:16.666666666666664%}.col-sm-push-3{left:25%}.col-sm-push-4{left:33.33333333333333%}.col-sm-push-5{left:41.66666666666667%}.col-sm-push-6{left:50%}.col-sm-push-7{left:58.333333333333336%}.col-sm-push-8{left:66.66666666666666%}.col-sm-push-9{left:75%}.col-sm-push-10{left:83.33333333333334%}.col-sm-push-11{left:91.66666666666666%}.col-sm-pull-1{right:8.333333333333332%}.col-sm-pull-2{right:16.666666666666664%}.col-sm-pull-3{right:25%}.col-sm-pull-4{right:33.33333333333333%}.col-sm-pull-5{right:41.66666666666667%}.col-sm-pull-6{right:50%}.col-sm-pull-7{right:58.333333333333336%}.col-sm-pull-8{right:66.66666666666666%}.col-sm-pull-9{right:75%}.col-sm-pull-10{right:83.33333333333334%}.col-sm-pull-11{right:91.66666666666666%}.col-sm-offset-1{margin-left:8.333333333333332%}.col-sm-offset-2{margin-left:16.666666666666664%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-4{margin-left:33.33333333333333%}.col-sm-offset-5{margin-left:41.66666666666667%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-7{margin-left:58.333333333333336%}.col-sm-offset-8{margin-left:66.66666666666666%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-10{margin-left:83.33333333333334%}.col-sm-offset-11{margin-left:91.66666666666666%}}@media(min-width:992px){.container{max-width:940px}.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-1{width:8.333333333333332%}.col-lg-2{width:16.666666666666664%}.col-lg-3{width:25%}.col-lg-4{width:33.33333333333333%}.col-lg-5{width:41.66666666666667%}.col-lg-6{width:50%}.col-lg-7{width:58.333333333333336%}.col-lg-8{width:66.66666666666666%}.col-lg-9{width:75%}.col-lg-10{width:83.33333333333334%}.col-lg-11{width:91.66666666666666%}.col-lg-12{width:100%}.col-lg-push-1{left:8.333333333333332%}.col-lg-push-2{left:16.666666666666664%}.col-lg-push-3{left:25%}.col-lg-push-4{left:33.33333333333333%}.col-lg-push-5{left:41.66666666666667%}.col-lg-push-6{left:50%}.col-lg-push-7{left:58.333333333333336%}.col-lg-push-8{left:66.66666666666666%}.col-lg-push-9{left:75%}.col-lg-push-10{left:83.33333333333334%}.col-lg-push-11{left:91.66666666666666%}.col-lg-pull-1{right:8.333333333333332%}.col-lg-pull-2{right:16.666666666666664%}.col-lg-pull-3{right:25%}.col-lg-pull-4{right:33.33333333333333%}.col-lg-pull-5{right:41.66666666666667%}.col-lg-pull-6{right:50%}.col-lg-pull-7{right:58.333333333333336%}.col-lg-pull-8{right:66.66666666666666%}.col-lg-pull-9{right:75%}.col-lg-pull-10{right:83.33333333333334%}.col-lg-pull-11{right:91.66666666666666%}.col-lg-offset-1{margin-left:8.333333333333332%}.col-lg-offset-2{margin-left:16.666666666666664%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-4{margin-left:33.33333333333333%}.col-lg-offset-5{margin-left:41.66666666666667%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-7{margin-left:58.333333333333336%}.col-lg-offset-8{margin-left:66.66666666666666%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-10{margin-left:83.33333333333334%}.col-lg-offset-11{margin-left:91.66666666666666%}}@media(min-width:1200px){.container{max-width:1170px}}table{max-width:100%;background-color:transparent}th{text-align:left}.table{width:100%;margin-bottom:20px}.table thead>tr>th,.table tbody>tr>th,.table tfoot>tr>th,.table thead>tr>td,.table tbody>tr>td,.table tfoot>tr>td{padding:8px;line-height:1.428571429;vertical-align:top;border-top:1px solid #ddd}.table thead>tr>th{vertical-align:bottom}.table caption+thead tr:first-child th,.table colgroup+thead tr:first-child th,.table thead:first-child tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed thead>tr>th,.table-condensed tbody>tr>th,.table-condensed tfoot>tr>th,.table-condensed thead>tr>td,.table-condensed tbody>tr>td,.table-condensed tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#f5f5f5}table col[class^="col-"]{display:table-column;float:none}table td[class^="col-"],table th[class^="col-"]{display:table-cell;float:none}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8;border-color:#d6e9c6}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede;border-color:#eed3d7}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3;border-color:#fbeed5}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td{background-color:#d0e9c6;border-color:#c9e2b3}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td{background-color:#ebcccc;border-color:#e6c1c7}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td{background-color:#faf2cc;border-color:#f8e5be}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;margin-bottom:5px;font-weight:bold}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}select[multiple],select[size]{height:auto}select optgroup{font-family:inherit;font-size:inherit;font-style:inherit}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}input[type="number"]::-webkit-outer-spin-button,input[type="number"]::-webkit-inner-spin-button{height:auto}.form-control:-moz-placeholder{color:#999}.form-control::-moz-placeholder{color:#999}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.428571429;color:#555;vertical-align:middle;background-color:#fff;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:rgba(82,168,236,0.8);outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee}textarea.form-control{height:auto}.form-group{margin-bottom:15px}.radio,.checkbox{display:block;min-height:20px;padding-left:20px;margin-top:10px;margin-bottom:10px;vertical-align:middle}.radio label,.checkbox label{display:inline;margin-bottom:0;font-weight:normal;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{float:left;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;font-weight:normal;vertical-align:middle;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.input-large{height:45px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.input-small{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-large{height:45px;line-height:45px}select.input-small{height:30px;line-height:30px}textarea.input-large,textarea.input-small{height:auto}.has-warning .help-block,.has-warning .control-label{color:#c09853}.has-warning .form-control{padding-right:32px;border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.has-warning .input-group-addon{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.has-error .help-block,.has-error .control-label{color:#b94a48}.has-error .form-control{padding-right:32px;border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.has-error .input-group-addon{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.has-success .help-block,.has-success .control-label{color:#468847}.has-success .form-control{padding-right:32px;border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.has-success .input-group-addon{color:#468847;background-color:#dff0d8;border-color:#468847}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}.form-inline .form-control,.form-inline .radio,.form-inline .checkbox{display:inline-block}.form-inline .radio,.form-inline .checkbox{margin-top:0;margin-bottom:0}.form-horizontal .control-label,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:9px}.form-horizontal .form-group:before,.form-horizontal .form-group:after{display:table;content:" "}.form-horizontal .form-group:after{clear:both}.form-horizontal .form-group:before,.form-horizontal .form-group:after{display:table;content:" "}.form-horizontal .form-group:after{clear:both}@media(min-width:768px){.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}}.form-horizontal .form-group .row{margin-right:-15px;margin-left:-15px}@media(min-width:768px){.form-horizontal .control-label{text-align:right}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:500;line-height:1.428571429;text-align:center;white-space:nowrap;vertical-align:middle;cursor:pointer;border:1px solid transparent;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus{color:#fff;text-decoration:none}.btn:active,.btn.active{outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{pointer-events:none;cursor:default;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#fff;background-color:#474949;border-color:#474949}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active{background-color:#3a3c3c;border-color:#2e2f2f}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#474949;border-color:#474949}.btn-primary{color:#fff;background-color:#428bca;border-color:#428bca}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active{background-color:#357ebd;border-color:#3071a9}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#428bca}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#f0ad4e}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active{background-color:#eea236;border-color:#ec971f}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#f0ad4e}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d9534f}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active{background-color:#d43f3a;border-color:#c9302c}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d9534f}.btn-success{color:#fff;background-color:#5cb85c;border-color:#5cb85c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active{background-color:#4cae4c;border-color:#449d44}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#5cb85c}.btn-info{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active{background-color:#46b8da;border-color:#31b0d5}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#5bc0de}.btn-link{font-weight:normal;color:#428bca;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#333;text-decoration:none}.btn-large{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-small,.btn-mini{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-mini{padding:3px 5px}.btn-block{display:block;width:100%;padding-right:0;padding-left:0}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;transition:height .35s ease}.input-group{position:relative;display:table;border-collapse:separate}.input-group.col{float:none;padding-right:0;padding-left:0}.input-group .form-control{width:100%;margin-bottom:0}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:normal;line-height:1.428571429;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-group-addon.input-small{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-large{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-4px}.input-group-btn>.btn:hover,.input-group-btn>.btn:active{z-index:2}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.dropdown{position:relative}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.428571429;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#fff;text-decoration:none;background-color:#357ebd;background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#357ebd));background-image:-webkit-linear-gradient(top,#428bca,0%,#357ebd,100%);background-image:-moz-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#357ebd;background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#357ebd));background-image:-webkit-linear-gradient(top,#428bca,0%,#357ebd,100%);background-image:-moz-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;outline:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.428571429;color:#999}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 30px 10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item>.badge{float:right;margin-right:-15px}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item .list-group-item-text{color:#555}a.list-group-item:hover,a.list-group-item:focus{text-decoration:none;background-color:#f5f5f5}a.list-group-item.active{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca}a.list-group-item.active .list-group-item-heading{color:inherit}a.list-group-item.active .list-group-item-text{color:#e1edf7}.panel{padding:15px;margin-bottom:20px;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel .list-group{margin:15px -15px -15px}.panel .list-group .list-group-item{border-width:1px 0}.panel .list-group .list-group-item:first-child{border-top-right-radius:0;border-top-left-radius:0}.panel .list-group .list-group-item:last-child{border-bottom:0}.panel-heading{padding:10px 15px;margin:-15px -15px 15px;background-color:#f5f5f5;border-bottom:1px solid #ddd;border-top-right-radius:3px;border-top-left-radius:3px}.panel-title{margin-top:0;margin-bottom:0;font-size:17.5px;font-weight:500}.panel-footer{padding:10px 15px;margin:15px -15px -15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-primary{border-color:#428bca}.panel-primary .panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success .panel-heading{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.panel-warning{border-color:#fbeed5}.panel-warning .panel-heading{color:#c09853;background-color:#fcf8e3;border-color:#fbeed5}.panel-danger{border-color:#eed3d7}.panel-danger .panel-heading{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.panel-info{border-color:#bce8f1}.panel-info .panel-heading{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;border-radius:6px}.well-small{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav:before,.nav:after{display:table;content:" "}.nav:after{clear:both}.nav:before,.nav:after{display:table;content:" "}.nav:after{clear:both}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav.open>a,.nav.open>a:hover,.nav.open>a:focus{color:#fff;background-color:#428bca;border-color:#428bca}.nav.open>a .caret,.nav.open>a:hover .caret,.nav.open>a:focus .caret{border-top-color:#fff;border-bottom-color:#fff}.nav>.pull-right{float:right}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.428571429;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{display:table-cell;float:none;width:1%}.nav-tabs.nav-justified>li>a{text-align:center}.nav-tabs.nav-justified>li>a{margin-right:0;border-bottom:1px solid #ddd}.nav-tabs.nav-justified>.active>a{border-bottom-color:#fff}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:5px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{display:table-cell;float:none;width:1%}.nav-justified>li>a{text-align:center}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-bottom:1px solid #ddd}.nav-tabs-justified>.active>a{border-bottom-color:#fff}.tabbable:before,.tabbable:after{display:table;content:" "}.tabbable:after{clear:both}.tabbable:before,.tabbable:after{display:table;content:" "}.tabbable:after{clear:both}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.nav .caret{border-top-color:#428bca;border-bottom-color:#428bca}.nav a:hover .caret{border-top-color:#2a6496;border-bottom-color:#2a6496}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:50px;padding-right:15px;padding-left:15px;margin-bottom:20px;background-color:#eee;border-radius:4px}.navbar:before,.navbar:after{display:table;content:" "}.navbar:after{clear:both}.navbar:before,.navbar:after{display:table;content:" "}.navbar:after{clear:both}.navbar-nav{margin-top:10px;margin-bottom:15px}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px;line-height:20px;color:#777;border-radius:4px}.navbar-nav>li>a:hover,.navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-nav>.active>a,.navbar-nav>.active>a:hover,.navbar-nav>.active>a:focus{color:#555;background-color:#d5d5d5}.navbar-nav>.disabled>a,.navbar-nav>.disabled>a:hover,.navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-nav.pull-right{width:100%}.navbar-static-top{border-radius:0}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;border-radius:0}.navbar-fixed-top{top:0}.navbar-fixed-bottom{bottom:0;margin-bottom:0}.navbar-brand{display:block;max-width:200px;padding:15px 15px;margin-right:auto;margin-left:auto;font-size:18px;font-weight:500;line-height:20px;color:#777;text-align:center}.navbar-brand:hover,.navbar-brand:focus{color:#5e5e5e;text-decoration:none;background-color:transparent}.navbar-toggle{position:absolute;top:9px;right:10px;width:48px;height:32px;padding:8px 12px;background-color:transparent;border:1px solid #ddd;border-radius:4px}.navbar-toggle:hover,.navbar-toggle:focus{background-color:#ddd}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;background-color:#ccc;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}.navbar-form{margin-top:8px;margin-bottom:8px}.navbar-form .form-control,.navbar-form .radio,.navbar-form .checkbox{display:inline-block}.navbar-form .radio,.navbar-form .checkbox{margin-top:0;margin-bottom:0}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-nav>.dropdown>a:hover .caret,.navbar-nav>.dropdown>a:focus .caret{border-top-color:#333;border-bottom-color:#333}.navbar-nav>.open>a,.navbar-nav>.open>a:hover,.navbar-nav>.open>a:focus{color:#555;background-color:#d5d5d5}.navbar-nav>.open>a .caret,.navbar-nav>.open>a:hover .caret,.navbar-nav>.open>a:focus .caret{border-top-color:#555;border-bottom-color:#555}.navbar-nav>.dropdown>a .caret{border-top-color:#777;border-bottom-color:#777}.navbar-nav.pull-right>li>.dropdown-menu,.navbar-nav>li>.dropdown-menu.pull-right{right:0;left:auto}.navbar-inverse{background-color:#222}.navbar-inverse .navbar-brand{color:#999}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .navbar-nav>li>a{color:#999}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.dropdown>a:hover .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .navbar-nav>.dropdown>a .caret{border-top-color:#999;border-bottom-color:#999}.navbar-inverse .navbar-nav>.open>a .caret,.navbar-inverse .navbar-nav>.open>a:hover .caret,.navbar-inverse .navbar-nav>.open>a:focus .caret{border-top-color:#fff;border-bottom-color:#fff}@media screen and (min-width:768px){.navbar-brand{float:left;margin-right:5px;margin-left:-15px}.navbar-nav{float:left;margin-top:0;margin-bottom:0}.navbar-nav>li{float:left}.navbar-nav>li>a{border-radius:0}.navbar-nav.pull-right{float:right;width:auto}.navbar-toggle{position:relative;top:auto;left:auto;display:none}.nav-collapse.collapse{display:block!important;height:auto!important;overflow:visible!important}}.navbar-btn{margin-top:8px}.navbar-text{float:left;padding:0 15px;margin-top:15px;margin-bottom:15px}.navbar-link{color:#777}.navbar-link:hover{color:#333}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#fff}.btn .caret{border-top-color:#fff}.dropup .btn .caret{border-bottom-color:#fff}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active{z-index:2}.btn-group .btn+.btn{margin-left:-1px}.btn-toolbar:before,.btn-toolbar:after{display:table;content:" "}.btn-toolbar:after{clear:both}.btn-toolbar:before,.btn-toolbar:after{display:table;content:" "}.btn-toolbar:after{clear:both}.btn-toolbar .btn-group{float:left}.btn-toolbar>.btn+.btn,.btn-toolbar>.btn-group+.btn,.btn-toolbar>.btn+.btn-group,.btn-toolbar>.btn-group+.btn-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-large+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn .caret{margin-left:0}.btn-large .caret{border-width:5px}.dropup .btn-large .caret{border-bottom-width:5px}.btn-group-vertical>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn+.btn{margin-top:-1px}.btn-group-vertical .btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical .btn:first-child{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical .btn:last-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%}.btn-group-justified .btn{display:table-cell;float:none;width:1%}.btn-group[data-toggle="buttons"]>.btn>input[type="radio"],.btn-group[data-toggle="buttons"]>.btn>input[type="checkbox"]{display:none}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#999}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{float:left;padding:4px 12px;line-height:1.428571429;text-decoration:none;background-color:#fff;border:1px solid #ddd;border-left-width:0}.pagination>li:first-child>a,.pagination>li:first-child>span{border-left-width:1px;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:hover,.pagination>li>a:focus,.pagination>.active>a,.pagination>.active>span{background-color:#f5f5f5}.pagination>.active>a,.pagination>.active>span{color:#999;cursor:default}.pagination>.disabled>span,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999;cursor:not-allowed;background-color:#fff}.pagination-large>li>a,.pagination-large>li>span{padding:10px 16px;font-size:18px}.pagination-large>li:first-child>a,.pagination-large>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-large>li:last-child>a,.pagination-large>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-small>li>a,.pagination-small>li>span{padding:5px 10px;font-size:12px}.pagination-small>li:first-child>a,.pagination-small>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-small>li:last-child>a,.pagination-small>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager:before,.pager:after{display:table;content:" "}.pager:after{clear:both}.pager:before,.pager:after{display:table;content:" "}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#f5f5f5}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999;cursor:not-allowed;background-color:#fff}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;display:none;overflow:auto;overflow-y:scroll}.modal.fade .modal-dialog{-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);transform:translate(0,-25%);-webkit-transition:-webkit-transform .3s ease-out;-moz-transition:-moz-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out}.modal.fade.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}.modal-dialog{z-index:1050;width:auto;padding:10px;margin-right:auto;margin-left:auto}.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);background-clip:padding-box}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1030;background-color:#000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.fade.in{opacity:.5;filter:alpha(opacity=50)}.modal-header{min-height:16.428571429px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.428571429}.modal-body{position:relative;padding:20px}.modal-footer{padding:19px 20px 20px;margin-top:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer:before,.modal-footer:after{display:table;content:" "}.modal-footer:after{clear:both}.modal-footer:before,.modal-footer:after{display:table;content:" "}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}@media screen and (min-width:768px){.modal-dialog{right:auto;left:50%;width:600px;padding-top:30px;padding-bottom:30px}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}}.tooltip{position:absolute;z-index:1030;display:block;font-size:12px;line-height:1.4;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:1;filter:alpha(opacity=100)}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:rgba(0,0,0,0.9);border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:rgba(0,0,0,0.9);border-width:5px 5px 0}.tooltip.top-left .tooltip-arrow{bottom:0;left:5px;border-top-color:rgba(0,0,0,0.9);border-width:5px 5px 0}.tooltip.top-right .tooltip-arrow{right:5px;bottom:0;border-top-color:rgba(0,0,0,0.9);border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:rgba(0,0,0,0.9);border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:rgba(0,0,0,0.9);border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:rgba(0,0,0,0.9);border-width:0 5px 5px}.tooltip.bottom-left .tooltip-arrow{top:0;left:5px;border-bottom-color:rgba(0,0,0,0.9);border-width:0 5px 5px}.tooltip.bottom-right .tooltip-arrow{top:0;right:5px;border-bottom-color:rgba(0,0,0,0.9);border-width:0 5px 5px}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;white-space:normal;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);background-clip:padding-box;-webkit-bg-clip:padding-box;-moz-bg-clip:padding}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover .arrow,.popover .arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover .arrow{border-width:11px}.popover .arrow:after{border-width:10px;content:""}.popover.top .arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top .arrow:after{bottom:1px;margin-left:-10px;border-top-color:#fff;border-bottom-width:0;content:" "}.popover.right .arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right .arrow:after{bottom:-10px;left:1px;border-right-color:#fff;border-left-width:0;content:" "}.popover.bottom .arrow{top:-11px;left:50%;margin-left:-11px;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);border-top-width:0}.popover.bottom .arrow:after{top:1px;margin-left:-10px;border-bottom-color:#fff;border-top-width:0;content:" "}.popover.left .arrow{top:50%;right:-11px;margin-top:-11px;border-left-color:#999;border-left-color:rgba(0,0,0,0.25);border-right-width:0}.popover.left .arrow:after{right:1px;bottom:-10px;border-left-color:#fff;border-right-width:0;content:" "}.alert{padding:15px 35px 15px 15px;margin-bottom:20px;color:#c09853;background-color:#fcf8e3;border:1px solid #fbeed5;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert hr{border-top-color:#f8e5be}.alert .alert-link{font-weight:bold;color:#a47e3c}.alert .close{position:relative;top:-2px;right:-21px;color:inherit}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#356635}.alert-danger{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-danger hr{border-top-color:#e6c1c7}.alert-danger .alert-link{color:#953b39}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#2d6987}.thumbnail,.img-thumbnail{padding:4px;line-height:1.428571429;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.thumbnail{display:block}.thumbnail>img,.img-thumbnail{display:inline-block;height:auto;max-width:100%}a.thumbnail:hover,a.thumbnail:focus{border-color:#428bca}.thumbnail>img{margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#333}.media,.media-body{overflow:hidden;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{padding-left:0;list-style:none}.label{display:inline;padding:.25em .6em;font-size:75%;font-weight:500;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;background-color:#999;border-radius:.25em}.label[href]:hover,.label[href]:focus{color:#fff;text-decoration:none;cursor:pointer;background-color:#808080}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;background-color:#999;border-radius:10px}.badge:empty{display:none}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.btn .badge{position:relative;top:-1px}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#fff}.nav-pills>li>a>.badge{margin-left:3px}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;background-color:#428bca;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar{background-color:#428bca;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-size:40px 40px}.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-color:#d9534f;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-color:#5cb85c;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-color:#f0ad4e;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:20px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px;cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:inline-block;height:auto;max-width:100%;line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);opacity:.5;filter:alpha(opacity=50)}.carousel-control.left{background-color:rgba(0,0,0,0.0001);background-color:transparent;background-image:-webkit-gradient(linear,0 top,100% top,from(rgba(0,0,0,0.5)),to(rgba(0,0,0,0.0001)));background-image:-webkit-linear-gradient(left,color-stop(rgba(0,0,0,0.5) 0),color-stop(rgba(0,0,0,0.0001) 100%));background-image:-moz-linear-gradient(left,rgba(0,0,0,0.5) 0,rgba(0,0,0,0.0001) 100%);background-image:linear-gradient(to right,rgba(0,0,0,0.5) 0,rgba(0,0,0,0.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000',endColorstr='#00000000',GradientType=1)}.carousel-control.right{right:0;left:auto;background-color:rgba(0,0,0,0.5);background-color:transparent;background-image:-webkit-gradient(linear,0 top,100% top,from(rgba(0,0,0,0.0001)),to(rgba(0,0,0,0.5)));background-image:-webkit-linear-gradient(left,color-stop(rgba(0,0,0,0.0001) 0),color-stop(rgba(0,0,0,0.5) 100%));background-image:-moz-linear-gradient(left,rgba(0,0,0,0.0001) 0,rgba(0,0,0,0.5) 100%);background-image:linear-gradient(to right,rgba(0,0,0,0.0001) 0,rgba(0,0,0,0.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000',endColorstr='#80000000',GradientType=1)}.carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-control .glyphicon,.carousel-control .icon-prev,.carousel-control .icon-next{position:absolute;top:50%;left:50%;z-index:5;display:inline-block;width:20px;height:20px;margin-top:-10px;margin-left:-10px;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:120px;padding-left:0;margin-left:-60px;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;margin-left:-15px;font-size:30px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.jumbotron{padding:30px;margin-bottom:30px;font-size:21px;font-weight:200;line-height:2.1428571435;color:inherit;background-color:#eee}.jumbotron h1{line-height:1;color:inherit}.jumbotron p{line-height:1.4}@media screen and (min-width:768px){.jumbotron{padding:50px 60px;border-radius:6px}.jumbotron h1{font-size:63px}}.clearfix:before,.clearfix:after{display:table;content:" "}.clearfix:after{clear:both}.pull-right{float:right}.pull-left{float:left}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.affix{position:fixed}@-ms-viewport{width:device-width}@media screen and (max-width:400px){@-ms-viewport{width:320px}}.hidden{display:none!important;visibility:hidden!important}.visible-sm{display:block!important}tr.visible-sm{display:table-row!important}th.visible-sm,td.visible-sm{display:table-cell!important}.visible-md{display:none!important}tr.visible-md{display:none!important}th.visible-md,td.visible-md{display:none!important}.visible-lg{display:none!important}tr.visible-lg{display:none!important}th.visible-lg,td.visible-lg{display:none!important}.hidden-sm{display:none!important}tr.hidden-sm{display:none!important}th.hidden-sm,td.hidden-sm{display:none!important}.hidden-md{display:block!important}tr.hidden-md{display:table-row!important}th.hidden-md,td.hidden-md{display:table-cell!important}.hidden-lg{display:block!important}tr.hidden-lg{display:table-row!important}th.hidden-lg,td.hidden-lg{display:table-cell!important}@media(min-width:768px) and (max-width:991px){.visible-sm{display:none!important}tr.visible-sm{display:none!important}th.visible-sm,td.visible-sm{display:none!important}.visible-md{display:block!important}tr.visible-md{display:table-row!important}th.visible-md,td.visible-md{display:table-cell!important}.visible-lg{display:none!important}tr.visible-lg{display:none!important}th.visible-lg,td.visible-lg{display:none!important}.hidden-sm{display:block!important}tr.hidden-sm{display:table-row!important}th.hidden-sm,td.hidden-sm{display:table-cell!important}.hidden-md{display:none!important}tr.hidden-md{display:none!important}th.hidden-md,td.hidden-md{display:none!important}.hidden-lg{display:block!important}tr.hidden-lg{display:table-row!important}th.hidden-lg,td.hidden-lg{display:table-cell!important}}@media(min-width:992px){.visible-sm{display:none!important}tr.visible-sm{display:none!important}th.visible-sm,td.visible-sm{display:none!important}.visible-md{display:none!important}tr.visible-md{display:none!important}th.visible-md,td.visible-md{display:none!important}.visible-lg{display:block!important}tr.visible-lg{display:table-row!important}th.visible-lg,td.visible-lg{display:table-cell!important}.hidden-sm{display:block!important}tr.hidden-sm{display:table-row!important}th.hidden-sm,td.hidden-sm{display:table-cell!important}.hidden-md{display:block!important}tr.hidden-md{display:table-row!important}th.hidden-md,td.hidden-md{display:table-cell!important}.hidden-lg{display:none!important}tr.hidden-lg{display:none!important}th.hidden-lg,td.hidden-lg{display:none!important}}.visible-print{display:none!important}tr.visible-print{display:none!important}th.visible-print,td.visible-print{display:none!important}@media print{.visible-print{display:block!important}tr.visible-print{display:table-row!important}th.visible-print,td.visible-print{display:table-cell!important}.hidden-print{display:none!important}tr.hidden-print{display:none!important}th.hidden-print,td.hidden-print{display:none!important}} -------------------------------------------------------------------------------- /resources/public/css/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: 300px; 7 | } 8 | .CodeMirror-scroll { 9 | /* Set scrolling behaviour here */ 10 | overflow: auto; 11 | } 12 | 13 | /* PADDING */ 14 | 15 | .CodeMirror-lines { 16 | padding: 4px 0; /* Vertical padding around content */ 17 | } 18 | .CodeMirror pre { 19 | padding: 0 4px; /* Horizontal padding of content */ 20 | } 21 | 22 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 23 | background-color: white; /* The little square between H and V scrollbars */ 24 | } 25 | 26 | /* GUTTER */ 27 | 28 | .CodeMirror-gutters { 29 | border-right: 1px solid #ddd; 30 | background-color: #f7f7f7; 31 | white-space: nowrap; 32 | } 33 | .CodeMirror-linenumbers {} 34 | .CodeMirror-linenumber { 35 | padding: 0 3px 0 5px; 36 | min-width: 20px; 37 | text-align: right; 38 | color: #999; 39 | } 40 | 41 | /* CURSOR */ 42 | 43 | .CodeMirror div.CodeMirror-cursor { 44 | border-left: 1px solid black; 45 | z-index: 3; 46 | } 47 | /* Shown when moving in bi-directional text */ 48 | .CodeMirror div.CodeMirror-secondarycursor { 49 | border-left: 1px solid silver; 50 | } 51 | .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor { 52 | width: auto; 53 | border: 0; 54 | background: #7e7; 55 | z-index: 1; 56 | } 57 | /* Can style cursor different in overwrite (non-insert) mode */ 58 | .CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {} 59 | 60 | .cm-tab { display: inline-block; } 61 | 62 | /* DEFAULT THEME */ 63 | 64 | .cm-s-default .cm-keyword {color: #708;} 65 | .cm-s-default .cm-atom {color: #219;} 66 | .cm-s-default .cm-number {color: #164;} 67 | .cm-s-default .cm-def {color: #00f;} 68 | .cm-s-default .cm-variable {color: black;} 69 | .cm-s-default .cm-variable-2 {color: #05a;} 70 | .cm-s-default .cm-variable-3 {color: #085;} 71 | .cm-s-default .cm-property {color: black;} 72 | .cm-s-default .cm-operator {color: black;} 73 | .cm-s-default .cm-comment {color: #a50;} 74 | .cm-s-default .cm-string {color: #a11;} 75 | .cm-s-default .cm-string-2 {color: #f50;} 76 | .cm-s-default .cm-meta {color: #555;} 77 | .cm-s-default .cm-error {color: #f00;} 78 | .cm-s-default .cm-qualifier {color: #555;} 79 | .cm-s-default .cm-builtin {color: #30a;} 80 | .cm-s-default .cm-bracket {color: #997;} 81 | .cm-s-default .cm-tag {color: #170;} 82 | .cm-s-default .cm-attribute {color: #00c;} 83 | .cm-s-default .cm-header {color: blue;} 84 | .cm-s-default .cm-quote {color: #090;} 85 | .cm-s-default .cm-hr {color: #999;} 86 | .cm-s-default .cm-link {color: #00c;} 87 | 88 | .cm-negative {color: #d44;} 89 | .cm-positive {color: #292;} 90 | .cm-header, .cm-strong {font-weight: bold;} 91 | .cm-em {font-style: italic;} 92 | .cm-link {text-decoration: underline;} 93 | 94 | .cm-invalidchar {color: #f00;} 95 | 96 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 97 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 98 | .CodeMirror-activeline-background {background: #e8f2ff;} 99 | 100 | /* STOP */ 101 | 102 | /* The rest of this file contains styles related to the mechanics of 103 | the editor. You probably shouldn't touch them. */ 104 | 105 | .CodeMirror { 106 | line-height: 1; 107 | position: relative; 108 | overflow: hidden; 109 | background: white; 110 | color: black; 111 | } 112 | 113 | .CodeMirror-scroll { 114 | /* 30px is the magic margin used to hide the element's real scrollbars */ 115 | /* See overflow: hidden in .CodeMirror */ 116 | margin-bottom: -30px; margin-right: -30px; 117 | padding-bottom: 30px; padding-right: 30px; 118 | height: 100%; 119 | outline: none; /* Prevent dragging from highlighting the element */ 120 | position: relative; 121 | -moz-box-sizing: content-box; 122 | box-sizing: content-box; 123 | } 124 | .CodeMirror-sizer { 125 | position: relative; 126 | } 127 | 128 | /* The fake, visible scrollbars. Used to force redraw during scrolling 129 | before actuall scrolling happens, thus preventing shaking and 130 | flickering artifacts. */ 131 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 132 | position: absolute; 133 | z-index: 6; 134 | display: none; 135 | } 136 | .CodeMirror-vscrollbar { 137 | right: 0; top: 0; 138 | overflow-x: hidden; 139 | overflow-y: scroll; 140 | } 141 | .CodeMirror-hscrollbar { 142 | bottom: 0; left: 0; 143 | overflow-y: hidden; 144 | overflow-x: scroll; 145 | } 146 | .CodeMirror-scrollbar-filler { 147 | right: 0; bottom: 0; 148 | } 149 | .CodeMirror-gutter-filler { 150 | left: 0; bottom: 0; 151 | } 152 | 153 | .CodeMirror-gutters { 154 | position: absolute; left: 0; top: 0; 155 | padding-bottom: 30px; 156 | z-index: 3; 157 | } 158 | .CodeMirror-gutter { 159 | white-space: normal; 160 | height: 100%; 161 | -moz-box-sizing: content-box; 162 | box-sizing: content-box; 163 | padding-bottom: 30px; 164 | margin-bottom: -32px; 165 | display: inline-block; 166 | /* Hack to make IE7 behave */ 167 | *zoom:1; 168 | *display:inline; 169 | } 170 | .CodeMirror-gutter-elt { 171 | position: absolute; 172 | cursor: default; 173 | z-index: 4; 174 | } 175 | 176 | .CodeMirror-lines { 177 | cursor: text; 178 | } 179 | .CodeMirror pre { 180 | /* Reset some styles that the rest of the page might have set */ 181 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 182 | border-width: 0; 183 | background: transparent; 184 | font-family: inherit; 185 | font-size: inherit; 186 | margin: 0; 187 | white-space: pre; 188 | word-wrap: normal; 189 | line-height: inherit; 190 | color: inherit; 191 | z-index: 2; 192 | position: relative; 193 | overflow: visible; 194 | } 195 | .CodeMirror-wrap pre { 196 | word-wrap: break-word; 197 | white-space: pre-wrap; 198 | word-break: normal; 199 | } 200 | .CodeMirror-code pre { 201 | border-right: 30px solid transparent; 202 | width: -webkit-fit-content; 203 | width: -moz-fit-content; 204 | width: fit-content; 205 | } 206 | .CodeMirror-wrap .CodeMirror-code pre { 207 | border-right: none; 208 | width: auto; 209 | } 210 | .CodeMirror-linebackground { 211 | position: absolute; 212 | left: 0; right: 0; top: 0; bottom: 0; 213 | z-index: 0; 214 | } 215 | 216 | .CodeMirror-linewidget { 217 | position: relative; 218 | z-index: 2; 219 | overflow: auto; 220 | } 221 | 222 | .CodeMirror-widget {} 223 | 224 | .CodeMirror-wrap .CodeMirror-scroll { 225 | overflow-x: hidden; 226 | } 227 | 228 | .CodeMirror-measure { 229 | position: absolute; 230 | width: 100%; 231 | height: 0; 232 | overflow: hidden; 233 | visibility: hidden; 234 | } 235 | .CodeMirror-measure pre { position: static; } 236 | 237 | .CodeMirror div.CodeMirror-cursor { 238 | position: absolute; 239 | visibility: hidden; 240 | border-right: none; 241 | width: 0; 242 | } 243 | .CodeMirror-focused div.CodeMirror-cursor { 244 | visibility: visible; 245 | } 246 | 247 | .CodeMirror-selected { background: #d9d9d9; } 248 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 249 | 250 | .cm-searching { 251 | background: #ffa; 252 | background: rgba(255, 255, 0, .4); 253 | } 254 | 255 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */ 256 | .CodeMirror span { *vertical-align: text-bottom; } 257 | 258 | @media print { 259 | /* Hide the cursor when printing */ 260 | .CodeMirror div.CodeMirror-cursor { 261 | visibility: hidden; 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /resources/public/css/style.css: -------------------------------------------------------------------------------- 1 | .full-width-container { 2 | margin-left: auto; 3 | margin-right: auto; 4 | padding-left: 15px; 5 | padding-right: 15px; 6 | } 7 | 8 | #cljsfiddle-buttons { 9 | position: relative; 10 | float: right; 11 | margin-right: 0px; 12 | z-index: 100; 13 | top: 8px; 14 | } 15 | 16 | #cljsfiddle-buttons button { 17 | opacity: 0.7; 18 | cursor: pointer; 19 | margin-left:2px; 20 | } 21 | 22 | #cljsfiddle-buttons button:hover { 23 | opacity: 1; 24 | } 25 | 26 | #editor-tabs { 27 | padding-left: 34px; 28 | } 29 | #editor-tabs > li > a { 30 | padding: 5px 12px; 31 | } 32 | 33 | /*#output + div { 34 | border: 1px solid lightgray; 35 | 36 | }*/ 37 | 38 | .navbar { 39 | margin-bottom: 8px; 40 | } 41 | 42 | .CodeMirror { 43 | -webkit-touch-callout: none; 44 | -webkit-user-select: none; 45 | -khtml-user-select: none; 46 | -moz-user-select: none; 47 | -ms-user-select: none; 48 | user-select: none; 49 | } 50 | -------------------------------------------------------------------------------- /resources/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonase/cljsfiddle/9565ccf0d256fdbf97bf524dafb499ed470f32cc/resources/public/favicon.ico -------------------------------------------------------------------------------- /resources/public/js/addon/edit/closebrackets.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var DEFAULT_BRACKETS = "()[]{}''\"\""; 3 | var DEFAULT_EXPLODE_ON_ENTER = "[]{}"; 4 | var SPACE_CHAR_REGEX = /\s/; 5 | 6 | CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) { 7 | if (old != CodeMirror.Init && old) 8 | cm.removeKeyMap("autoCloseBrackets"); 9 | if (!val) return; 10 | var pairs = DEFAULT_BRACKETS, explode = DEFAULT_EXPLODE_ON_ENTER; 11 | if (typeof val == "string") pairs = val; 12 | else if (typeof val == "object") { 13 | if (val.pairs != null) pairs = val.pairs; 14 | if (val.explode != null) explode = val.explode; 15 | } 16 | var map = buildKeymap(pairs); 17 | if (explode) map.Enter = buildExplodeHandler(explode); 18 | cm.addKeyMap(map); 19 | }); 20 | 21 | function charsAround(cm, pos) { 22 | var str = cm.getRange(CodeMirror.Pos(pos.line, pos.ch - 1), 23 | CodeMirror.Pos(pos.line, pos.ch + 1)); 24 | return str.length == 2 ? str : null; 25 | } 26 | 27 | function buildKeymap(pairs) { 28 | var map = { 29 | name : "autoCloseBrackets", 30 | Backspace: function(cm) { 31 | if (cm.somethingSelected()) return CodeMirror.Pass; 32 | var cur = cm.getCursor(), around = charsAround(cm, cur); 33 | if (around && pairs.indexOf(around) % 2 == 0) 34 | cm.replaceRange("", CodeMirror.Pos(cur.line, cur.ch - 1), CodeMirror.Pos(cur.line, cur.ch + 1)); 35 | else 36 | return CodeMirror.Pass; 37 | } 38 | }; 39 | var closingBrackets = ""; 40 | for (var i = 0; i < pairs.length; i += 2) (function(left, right) { 41 | if (left != right) closingBrackets += right; 42 | function surround(cm) { 43 | var selection = cm.getSelection(); 44 | cm.replaceSelection(left + selection + right); 45 | } 46 | function maybeOverwrite(cm) { 47 | var cur = cm.getCursor(), ahead = cm.getRange(cur, CodeMirror.Pos(cur.line, cur.ch + 1)); 48 | if (ahead != right || cm.somethingSelected()) return CodeMirror.Pass; 49 | else cm.execCommand("goCharRight"); 50 | } 51 | map["'" + left + "'"] = function(cm) { 52 | if (left == "'" && cm.getTokenAt(cm.getCursor()).type == "comment") 53 | return CodeMirror.Pass; 54 | if (cm.somethingSelected()) return surround(cm); 55 | if (left == right && maybeOverwrite(cm) != CodeMirror.Pass) return; 56 | var cur = cm.getCursor(), ahead = CodeMirror.Pos(cur.line, cur.ch + 1); 57 | var line = cm.getLine(cur.line), nextChar = line.charAt(cur.ch), curChar = cur.ch > 0 ? line.charAt(cur.ch - 1) : ""; 58 | if (left == right && CodeMirror.isWordChar(curChar)) 59 | return CodeMirror.Pass; 60 | if (line.length == cur.ch || closingBrackets.indexOf(nextChar) >= 0 || SPACE_CHAR_REGEX.test(nextChar)) 61 | cm.replaceSelection(left + right, {head: ahead, anchor: ahead}); 62 | else 63 | return CodeMirror.Pass; 64 | }; 65 | if (left != right) map["'" + right + "'"] = maybeOverwrite; 66 | })(pairs.charAt(i), pairs.charAt(i + 1)); 67 | return map; 68 | } 69 | 70 | function buildExplodeHandler(pairs) { 71 | return function(cm) { 72 | var cur = cm.getCursor(), around = charsAround(cm, cur); 73 | if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass; 74 | cm.operation(function() { 75 | var newPos = CodeMirror.Pos(cur.line + 1, 0); 76 | cm.replaceSelection("\n\n", {anchor: newPos, head: newPos}, "+input"); 77 | cm.indentLine(cur.line + 1, null, true); 78 | cm.indentLine(cur.line + 2, null, true); 79 | }); 80 | }; 81 | } 82 | })(); 83 | -------------------------------------------------------------------------------- /resources/public/js/addon/edit/matchbrackets.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var ie_lt8 = /MSIE \d/.test(navigator.userAgent) && 3 | (document.documentMode == null || document.documentMode < 8); 4 | 5 | var Pos = CodeMirror.Pos; 6 | 7 | var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; 8 | function findMatchingBracket(cm, where, strict) { 9 | var state = cm.state.matchBrackets; 10 | var maxScanLen = (state && state.maxScanLineLength) || 10000; 11 | 12 | var cur = where || cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1; 13 | var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; 14 | if (!match) return null; 15 | var forward = match.charAt(1) == ">", d = forward ? 1 : -1; 16 | if (strict && forward != (pos == cur.ch)) return null; 17 | var style = cm.getTokenTypeAt(Pos(cur.line, pos + 1)); 18 | 19 | var stack = [line.text.charAt(pos)], re = /[(){}[\]]/; 20 | function scan(line, lineNo, start) { 21 | if (!line.text) return; 22 | var pos = forward ? 0 : line.text.length - 1, end = forward ? line.text.length : -1; 23 | if (line.text.length > maxScanLen) return null; 24 | if (start != null) pos = start + d; 25 | for (; pos != end; pos += d) { 26 | var ch = line.text.charAt(pos); 27 | if (re.test(ch) && cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style) { 28 | var match = matching[ch]; 29 | if (match.charAt(1) == ">" == forward) stack.push(ch); 30 | else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false}; 31 | else if (!stack.length) return {pos: pos, match: true}; 32 | } 33 | } 34 | } 35 | for (var i = cur.line, found, e = forward ? Math.min(i + 100, cm.lineCount()) : Math.max(-1, i - 100); i != e; i+=d) { 36 | if (i == cur.line) found = scan(line, i, pos); 37 | else found = scan(cm.getLineHandle(i), i); 38 | if (found) break; 39 | } 40 | return {from: Pos(cur.line, pos), to: found && Pos(i, found.pos), 41 | match: found && found.match, forward: forward}; 42 | } 43 | 44 | function matchBrackets(cm, autoclear) { 45 | // Disable brace matching in long lines, since it'll cause hugely slow updates 46 | var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000; 47 | var found = findMatchingBracket(cm); 48 | if (!found || cm.getLine(found.from.line).length > maxHighlightLen || 49 | found.to && cm.getLine(found.to.line).length > maxHighlightLen) 50 | return; 51 | 52 | var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; 53 | var one = cm.markText(found.from, Pos(found.from.line, found.from.ch + 1), {className: style}); 54 | var two = found.to && cm.markText(found.to, Pos(found.to.line, found.to.ch + 1), {className: style}); 55 | // Kludge to work around the IE bug from issue #1193, where text 56 | // input stops going to the textare whever this fires. 57 | if (ie_lt8 && cm.state.focused) cm.display.input.focus(); 58 | var clear = function() { 59 | cm.operation(function() { one.clear(); two && two.clear(); }); 60 | }; 61 | if (autoclear) setTimeout(clear, 800); 62 | else return clear; 63 | } 64 | 65 | var currentlyHighlighted = null; 66 | function doMatchBrackets(cm) { 67 | cm.operation(function() { 68 | if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;} 69 | if (!cm.somethingSelected()) currentlyHighlighted = matchBrackets(cm, false); 70 | }); 71 | } 72 | 73 | CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) { 74 | if (old && old != CodeMirror.Init) 75 | cm.off("cursorActivity", doMatchBrackets); 76 | if (val) { 77 | cm.state.matchBrackets = typeof val == "object" ? val : {}; 78 | cm.on("cursorActivity", doMatchBrackets); 79 | } 80 | }); 81 | 82 | CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);}); 83 | CodeMirror.defineExtension("findMatchingBracket", function(pos, strict){ 84 | return findMatchingBracket(this, pos, strict); 85 | }); 86 | })(); 87 | -------------------------------------------------------------------------------- /resources/public/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap.js v3.0.0 by @fat and @mdo 3 | * Copyright 2013 Twitter Inc. 4 | * http://www.apache.org/licenses/LICENSE-2.0 5 | */ 6 | if(!jQuery)throw new Error("Bootstrap requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]}}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(window.jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d)};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.is("input")?"val":"html",e=c.data();a+="Text",e.resetText||c.data("resetText",c[d]()),c[d](e[a]||this.options[a]),setTimeout(function(){"loadingText"==a?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.closest('[data-toggle="buttons"]');if(a.length){var b=this.$element.find("input").prop("checked",!this.$element.hasClass("active")).trigger("change");"radio"===b.prop("type")&&a.find(".active").removeClass("active")}this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(window.jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover"},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;this.sliding=!0,f&&this.pause(),e=e.length?e:this.$element.find(".item")[h]();var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});if(!e.hasClass("active")){if(this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(j),j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)}).emulateTransitionEnd(600)}else{if(this.$element.trigger(j),j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return f&&this.cycle(),this}};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .accordion-group > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?(this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350),void 0):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find("[data-toggle=collapse][data-parent="+i+"]").not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(window.jQuery),+function(a){"use strict";function b(){a(d).remove(),a(e).each(function(b){var d=c(a(this));d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown")),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown"))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){if("ontouchstart"in document.documentElement&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title:empty").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(window.jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(c).is("body")?a(window):a(c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#\w/.test(e)&&a(e);return f&&f.length&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parents(".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(window.jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.attr("data-target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top()),"function"==typeof h&&(h=f.bottom());var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;this.affixed!==i&&(this.unpin&&this.$element.css("top",""),this.affixed=i,this.unpin="bottom"==i?e.top-d:null,this.$element.removeClass(b.RESET).addClass("affix"+(i?"-"+i:"")),"bottom"==i&&this.$element.offset({top:document.body.offsetHeight-h-this.$element.height()}))}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(window.jQuery); -------------------------------------------------------------------------------- /resources/public/js/mode/clojure/clojure.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Author: Hans Engel 3 | * Branched from CodeMirror's Scheme mode (by Koh Zi Han, based on implementation by Koh Zi Chun) 4 | */ 5 | CodeMirror.defineMode("clojure", function () { 6 | var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", CHARACTER = "string-2", 7 | ATOM = "atom", NUMBER = "number", BRACKET = "bracket", KEYWORD = "keyword"; 8 | var INDENT_WORD_SKIP = 2; 9 | 10 | function makeKeywords(str) { 11 | var obj = {}, words = str.split(" "); 12 | for (var i = 0; i < words.length; ++i) obj[words[i]] = true; 13 | return obj; 14 | } 15 | 16 | var atoms = makeKeywords("true false nil"); 17 | 18 | var keywords = makeKeywords( 19 | "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"); 20 | 21 | var builtins = makeKeywords( 22 | "* *' *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->>"); 23 | 24 | var indentKeys = makeKeywords( 25 | // Built-ins 26 | "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 " + 27 | 28 | // Binding forms 29 | "let letfn binding loop for doseq dotimes when-let if-let " + 30 | 31 | // Data structures 32 | "defstruct struct-map assoc " + 33 | 34 | // clojure.test 35 | "testing deftest " + 36 | 37 | // contrib 38 | "handler-case handle dotrace deftrace"); 39 | 40 | var tests = { 41 | digit: /\d/, 42 | digit_or_colon: /[\d:]/, 43 | hex: /[0-9a-f]/i, 44 | sign: /[+-]/, 45 | exponent: /e/i, 46 | keyword_char: /[^\s\(\[\;\)\]]/, 47 | symbol: /[\w*+!\-\._?:\/]/ 48 | }; 49 | 50 | function stateStack(indent, type, prev) { // represents a state stack object 51 | this.indent = indent; 52 | this.type = type; 53 | this.prev = prev; 54 | } 55 | 56 | function pushStack(state, indent, type) { 57 | state.indentStack = new stateStack(indent, type, state.indentStack); 58 | } 59 | 60 | function popStack(state) { 61 | state.indentStack = state.indentStack.prev; 62 | } 63 | 64 | function isNumber(ch, stream){ 65 | // hex 66 | if ( ch === '0' && stream.eat(/x/i) ) { 67 | stream.eatWhile(tests.hex); 68 | return true; 69 | } 70 | 71 | // leading sign 72 | if ( ( ch == '+' || ch == '-' ) && ( tests.digit.test(stream.peek()) ) ) { 73 | stream.eat(tests.sign); 74 | ch = stream.next(); 75 | } 76 | 77 | if ( tests.digit.test(ch) ) { 78 | stream.eat(ch); 79 | stream.eatWhile(tests.digit); 80 | 81 | if ( '.' == stream.peek() ) { 82 | stream.eat('.'); 83 | stream.eatWhile(tests.digit); 84 | } 85 | 86 | if ( stream.eat(tests.exponent) ) { 87 | stream.eat(tests.sign); 88 | stream.eatWhile(tests.digit); 89 | } 90 | 91 | return true; 92 | } 93 | 94 | return false; 95 | } 96 | 97 | // Eat character that starts after backslash \ 98 | function eatCharacter(stream) { 99 | var first = stream.next(); 100 | // Read special literals: backspace, newline, space, return. 101 | // Just read all lowercase letters. 102 | if (first.match(/[a-z]/) && stream.match(/[a-z]+/, true)) { 103 | return; 104 | } 105 | // Read unicode character: \u1000 \uA0a1 106 | if (first === "u") { 107 | stream.match(/[0-9a-z]{4}/i, true); 108 | } 109 | } 110 | 111 | return { 112 | startState: function () { 113 | return { 114 | indentStack: null, 115 | indentation: 0, 116 | mode: false 117 | }; 118 | }, 119 | 120 | token: function (stream, state) { 121 | if (state.indentStack == null && stream.sol()) { 122 | // update indentation, but only if indentStack is empty 123 | state.indentation = stream.indentation(); 124 | } 125 | 126 | // skip spaces 127 | if (stream.eatSpace()) { 128 | return null; 129 | } 130 | var returnType = null; 131 | 132 | switch(state.mode){ 133 | case "string": // multi-line string parsing mode 134 | var next, escaped = false; 135 | while ((next = stream.next()) != null) { 136 | if (next == "\"" && !escaped) { 137 | 138 | state.mode = false; 139 | break; 140 | } 141 | escaped = !escaped && next == "\\"; 142 | } 143 | returnType = STRING; // continue on in string mode 144 | break; 145 | default: // default parsing mode 146 | var ch = stream.next(); 147 | 148 | if (ch == "\"") { 149 | state.mode = "string"; 150 | returnType = STRING; 151 | } else if (ch == "\\") { 152 | eatCharacter(stream); 153 | returnType = CHARACTER; 154 | } else if (ch == "'" && !( tests.digit_or_colon.test(stream.peek()) )) { 155 | returnType = ATOM; 156 | } else if (ch == ";") { // comment 157 | stream.skipToEnd(); // rest of the line is a comment 158 | returnType = COMMENT; 159 | } else if (isNumber(ch,stream)){ 160 | returnType = NUMBER; 161 | } else if (ch == "(" || ch == "[" || ch == "{" ) { 162 | var keyWord = '', indentTemp = stream.column(), letter; 163 | /** 164 | Either 165 | (indent-word .. 166 | (non-indent-word .. 167 | (;something else, bracket, etc. 168 | */ 169 | 170 | if (ch == "(") while ((letter = stream.eat(tests.keyword_char)) != null) { 171 | keyWord += letter; 172 | } 173 | 174 | if (keyWord.length > 0 && (indentKeys.propertyIsEnumerable(keyWord) || 175 | /^(?:def|with)/.test(keyWord))) { // indent-word 176 | pushStack(state, indentTemp + INDENT_WORD_SKIP, ch); 177 | } else { // non-indent word 178 | // we continue eating the spaces 179 | stream.eatSpace(); 180 | if (stream.eol() || stream.peek() == ";") { 181 | // nothing significant after 182 | // we restart indentation 1 space after 183 | pushStack(state, indentTemp + 1, ch); 184 | } else { 185 | pushStack(state, indentTemp + stream.current().length, ch); // else we match 186 | } 187 | } 188 | stream.backUp(stream.current().length - 1); // undo all the eating 189 | 190 | returnType = BRACKET; 191 | } else if (ch == ")" || ch == "]" || ch == "}") { 192 | returnType = BRACKET; 193 | if (state.indentStack != null && state.indentStack.type == (ch == ")" ? "(" : (ch == "]" ? "[" :"{"))) { 194 | popStack(state); 195 | } 196 | } else if ( ch == ":" ) { 197 | stream.eatWhile(tests.symbol); 198 | return ATOM; 199 | } else { 200 | stream.eatWhile(tests.symbol); 201 | 202 | if (keywords && keywords.propertyIsEnumerable(stream.current())) { 203 | returnType = KEYWORD; 204 | } else if (builtins && builtins.propertyIsEnumerable(stream.current())) { 205 | returnType = BUILTIN; 206 | } else if (atoms && atoms.propertyIsEnumerable(stream.current())) { 207 | returnType = ATOM; 208 | } else returnType = null; 209 | } 210 | } 211 | 212 | return returnType; 213 | }, 214 | 215 | indent: function (state) { 216 | if (state.indentStack == null) return state.indentation; 217 | return state.indentStack.indent; 218 | }, 219 | 220 | lineComment: ";;" 221 | }; 222 | }); 223 | 224 | CodeMirror.defineMIME("text/x-clojure", "clojure"); 225 | -------------------------------------------------------------------------------- /resources/public/js/mode/css/css.js: -------------------------------------------------------------------------------- 1 | CodeMirror.defineMode("css", function(config, parserConfig) { 2 | "use strict"; 3 | 4 | if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css"); 5 | 6 | var indentUnit = config.indentUnit, 7 | hooks = parserConfig.hooks || {}, 8 | atMediaTypes = parserConfig.atMediaTypes || {}, 9 | atMediaFeatures = parserConfig.atMediaFeatures || {}, 10 | propertyKeywords = parserConfig.propertyKeywords || {}, 11 | colorKeywords = parserConfig.colorKeywords || {}, 12 | valueKeywords = parserConfig.valueKeywords || {}, 13 | allowNested = !!parserConfig.allowNested, 14 | type = null; 15 | 16 | function ret(style, tp) { type = tp; return style; } 17 | 18 | function tokenBase(stream, state) { 19 | var ch = stream.next(); 20 | if (hooks[ch]) { 21 | // result[0] is style and result[1] is type 22 | var result = hooks[ch](stream, state); 23 | if (result !== false) return result; 24 | } 25 | if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());} 26 | else if (ch == "=") ret(null, "compare"); 27 | else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare"); 28 | else if (ch == "\"" || ch == "'") { 29 | state.tokenize = tokenString(ch); 30 | return state.tokenize(stream, state); 31 | } 32 | else if (ch == "#") { 33 | stream.eatWhile(/[\w\\\-]/); 34 | return ret("atom", "hash"); 35 | } 36 | else if (ch == "!") { 37 | stream.match(/^\s*\w*/); 38 | return ret("keyword", "important"); 39 | } 40 | else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) { 41 | stream.eatWhile(/[\w.%]/); 42 | return ret("number", "unit"); 43 | } 44 | else if (ch === "-") { 45 | if (/\d/.test(stream.peek())) { 46 | stream.eatWhile(/[\w.%]/); 47 | return ret("number", "unit"); 48 | } else if (stream.match(/^[^-]+-/)) { 49 | return ret("meta", "meta"); 50 | } 51 | } 52 | else if (/[,+>*\/]/.test(ch)) { 53 | return ret(null, "select-op"); 54 | } 55 | else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) { 56 | return ret("qualifier", "qualifier"); 57 | } 58 | else if (ch == ":") { 59 | return ret("operator", ch); 60 | } 61 | else if (/[;{}\[\]\(\)]/.test(ch)) { 62 | return ret(null, ch); 63 | } 64 | else if (ch == "u" && stream.match("rl(")) { 65 | stream.backUp(1); 66 | state.tokenize = tokenParenthesized; 67 | return ret("property", "variable"); 68 | } 69 | else { 70 | stream.eatWhile(/[\w\\\-]/); 71 | return ret("property", "variable"); 72 | } 73 | } 74 | 75 | function tokenString(quote, nonInclusive) { 76 | return function(stream, state) { 77 | var escaped = false, ch; 78 | while ((ch = stream.next()) != null) { 79 | if (ch == quote && !escaped) 80 | break; 81 | escaped = !escaped && ch == "\\"; 82 | } 83 | if (!escaped) { 84 | if (nonInclusive) stream.backUp(1); 85 | state.tokenize = tokenBase; 86 | } 87 | return ret("string", "string"); 88 | }; 89 | } 90 | 91 | function tokenParenthesized(stream, state) { 92 | stream.next(); // Must be '(' 93 | if (!stream.match(/\s*[\"\']/, false)) 94 | state.tokenize = tokenString(")", true); 95 | else 96 | state.tokenize = tokenBase; 97 | return ret(null, "("); 98 | } 99 | 100 | return { 101 | startState: function(base) { 102 | return {tokenize: tokenBase, 103 | baseIndent: base || 0, 104 | stack: [], 105 | lastToken: null}; 106 | }, 107 | 108 | token: function(stream, state) { 109 | 110 | // Use these terms when applicable (see http://www.xanthir.com/blog/b4E50) 111 | // 112 | // rule** or **ruleset: 113 | // A selector + braces combo, or an at-rule. 114 | // 115 | // declaration block: 116 | // A sequence of declarations. 117 | // 118 | // declaration: 119 | // A property + colon + value combo. 120 | // 121 | // property value: 122 | // The entire value of a property. 123 | // 124 | // component value: 125 | // A single piece of a property value. Like the 5px in 126 | // text-shadow: 0 0 5px blue;. Can also refer to things that are 127 | // multiple terms, like the 1-4 terms that make up the background-size 128 | // portion of the background shorthand. 129 | // 130 | // term: 131 | // The basic unit of author-facing CSS, like a single number (5), 132 | // dimension (5px), string ("foo"), or function. Officially defined 133 | // by the CSS 2.1 grammar (look for the 'term' production) 134 | // 135 | // 136 | // simple selector: 137 | // A single atomic selector, like a type selector, an attr selector, a 138 | // class selector, etc. 139 | // 140 | // compound selector: 141 | // One or more simple selectors without a combinator. div.example is 142 | // compound, div > .example is not. 143 | // 144 | // complex selector: 145 | // One or more compound selectors chained with combinators. 146 | // 147 | // combinator: 148 | // The parts of selectors that express relationships. There are four 149 | // currently - the space (descendant combinator), the greater-than 150 | // bracket (child combinator), the plus sign (next sibling combinator), 151 | // and the tilda (following sibling combinator). 152 | // 153 | // sequence of selectors: 154 | // One or more of the named type of selector chained with commas. 155 | 156 | state.tokenize = state.tokenize || tokenBase; 157 | if (state.tokenize == tokenBase && stream.eatSpace()) return null; 158 | var style = state.tokenize(stream, state); 159 | if (style && typeof style != "string") style = ret(style[0], style[1]); 160 | 161 | // Changing style returned based on context 162 | var context = state.stack[state.stack.length-1]; 163 | if (style == "variable") { 164 | if (type == "variable-definition") state.stack.push("propertyValue"); 165 | return state.lastToken = "variable-2"; 166 | } else if (style == "property") { 167 | var word = stream.current().toLowerCase(); 168 | if (context == "propertyValue") { 169 | if (valueKeywords.hasOwnProperty(word)) { 170 | style = "string-2"; 171 | } else if (colorKeywords.hasOwnProperty(word)) { 172 | style = "keyword"; 173 | } else { 174 | style = "variable-2"; 175 | } 176 | } else if (context == "rule") { 177 | if (!propertyKeywords.hasOwnProperty(word)) { 178 | style += " error"; 179 | } 180 | } else if (context == "block") { 181 | // if a value is present in both property, value, or color, the order 182 | // of preference is property -> color -> value 183 | if (propertyKeywords.hasOwnProperty(word)) { 184 | style = "property"; 185 | } else if (colorKeywords.hasOwnProperty(word)) { 186 | style = "keyword"; 187 | } else if (valueKeywords.hasOwnProperty(word)) { 188 | style = "string-2"; 189 | } else { 190 | style = "tag"; 191 | } 192 | } else if (!context || context == "@media{") { 193 | style = "tag"; 194 | } else if (context == "@media") { 195 | if (atMediaTypes[stream.current()]) { 196 | style = "attribute"; // Known attribute 197 | } else if (/^(only|not)$/.test(word)) { 198 | style = "keyword"; 199 | } else if (word == "and") { 200 | style = "error"; // "and" is only allowed in @mediaType 201 | } else if (atMediaFeatures.hasOwnProperty(word)) { 202 | style = "error"; // Known property, should be in @mediaType( 203 | } else { 204 | // Unknown, expecting keyword or attribute, assuming attribute 205 | style = "attribute error"; 206 | } 207 | } else if (context == "@mediaType") { 208 | if (atMediaTypes.hasOwnProperty(word)) { 209 | style = "attribute"; 210 | } else if (word == "and") { 211 | style = "operator"; 212 | } else if (/^(only|not)$/.test(word)) { 213 | style = "error"; // Only allowed in @media 214 | } else { 215 | // Unknown attribute or property, but expecting property (preceded 216 | // by "and"). Should be in parentheses 217 | style = "error"; 218 | } 219 | } else if (context == "@mediaType(") { 220 | if (propertyKeywords.hasOwnProperty(word)) { 221 | // do nothing, remains "property" 222 | } else if (atMediaTypes.hasOwnProperty(word)) { 223 | style = "error"; // Known property, should be in parentheses 224 | } else if (word == "and") { 225 | style = "operator"; 226 | } else if (/^(only|not)$/.test(word)) { 227 | style = "error"; // Only allowed in @media 228 | } else { 229 | style += " error"; 230 | } 231 | } else if (context == "@import") { 232 | style = "tag"; 233 | } else { 234 | style = "error"; 235 | } 236 | } else if (style == "atom") { 237 | if(!context || context == "@media{" || context == "block") { 238 | style = "builtin"; 239 | } else if (context == "propertyValue") { 240 | if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) { 241 | style += " error"; 242 | } 243 | } else { 244 | style = "error"; 245 | } 246 | } else if (context == "@media" && type == "{") { 247 | style = "error"; 248 | } 249 | 250 | // Push/pop context stack 251 | if (type == "{") { 252 | if (context == "@media" || context == "@mediaType") { 253 | state.stack[state.stack.length-1] = "@media{"; 254 | } 255 | else { 256 | var newContext = allowNested ? "block" : "rule"; 257 | state.stack.push(newContext); 258 | } 259 | } 260 | else if (type == "}") { 261 | if (context == "interpolation") style = "operator"; 262 | state.stack.pop(); 263 | if (context == "propertyValue") state.stack.pop(); 264 | } 265 | else if (type == "interpolation") state.stack.push("interpolation"); 266 | else if (type == "@media") state.stack.push("@media"); 267 | else if (type == "@import") state.stack.push("@import"); 268 | else if (context == "@media" && /\b(keyword|attribute)\b/.test(style)) 269 | state.stack[state.stack.length-1] = "@mediaType"; 270 | else if (context == "@mediaType" && stream.current() == ",") 271 | state.stack[state.stack.length-1] = "@media"; 272 | else if (type == "(") { 273 | if (context == "@media" || context == "@mediaType") { 274 | // Make sure @mediaType is used to avoid error on { 275 | state.stack[state.stack.length-1] = "@mediaType"; 276 | state.stack.push("@mediaType("); 277 | } 278 | else state.stack.push("("); 279 | } 280 | else if (type == ")") { 281 | if (context == "propertyValue") { 282 | // In @mediaType( without closing ; after propertyValue 283 | state.stack.pop(); 284 | } 285 | state.stack.pop(); 286 | } 287 | else if (type == ":" && state.lastToken == "property") state.stack.push("propertyValue"); 288 | else if (context == "propertyValue" && type == ";") state.stack.pop(); 289 | else if (context == "@import" && type == ";") state.stack.pop(); 290 | 291 | return state.lastToken = style; 292 | }, 293 | 294 | indent: function(state, textAfter) { 295 | var n = state.stack.length; 296 | if (/^\}/.test(textAfter)) 297 | n -= state.stack[n-1] == "propertyValue" ? 2 : 1; 298 | return state.baseIndent + n * indentUnit; 299 | }, 300 | 301 | electricChars: "}", 302 | blockCommentStart: "/*", 303 | blockCommentEnd: "*/", 304 | fold: "brace" 305 | }; 306 | }); 307 | 308 | (function() { 309 | function keySet(array) { 310 | var keys = {}; 311 | for (var i = 0; i < array.length; ++i) { 312 | keys[array[i]] = true; 313 | } 314 | return keys; 315 | } 316 | 317 | var atMediaTypes = keySet([ 318 | "all", "aural", "braille", "handheld", "print", "projection", "screen", 319 | "tty", "tv", "embossed" 320 | ]); 321 | 322 | var atMediaFeatures = keySet([ 323 | "width", "min-width", "max-width", "height", "min-height", "max-height", 324 | "device-width", "min-device-width", "max-device-width", "device-height", 325 | "min-device-height", "max-device-height", "aspect-ratio", 326 | "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio", 327 | "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color", 328 | "max-color", "color-index", "min-color-index", "max-color-index", 329 | "monochrome", "min-monochrome", "max-monochrome", "resolution", 330 | "min-resolution", "max-resolution", "scan", "grid" 331 | ]); 332 | 333 | var propertyKeywords = keySet([ 334 | "align-content", "align-items", "align-self", "alignment-adjust", 335 | "alignment-baseline", "anchor-point", "animation", "animation-delay", 336 | "animation-direction", "animation-duration", "animation-iteration-count", 337 | "animation-name", "animation-play-state", "animation-timing-function", 338 | "appearance", "azimuth", "backface-visibility", "background", 339 | "background-attachment", "background-clip", "background-color", 340 | "background-image", "background-origin", "background-position", 341 | "background-repeat", "background-size", "baseline-shift", "binding", 342 | "bleed", "bookmark-label", "bookmark-level", "bookmark-state", 343 | "bookmark-target", "border", "border-bottom", "border-bottom-color", 344 | "border-bottom-left-radius", "border-bottom-right-radius", 345 | "border-bottom-style", "border-bottom-width", "border-collapse", 346 | "border-color", "border-image", "border-image-outset", 347 | "border-image-repeat", "border-image-slice", "border-image-source", 348 | "border-image-width", "border-left", "border-left-color", 349 | "border-left-style", "border-left-width", "border-radius", "border-right", 350 | "border-right-color", "border-right-style", "border-right-width", 351 | "border-spacing", "border-style", "border-top", "border-top-color", 352 | "border-top-left-radius", "border-top-right-radius", "border-top-style", 353 | "border-top-width", "border-width", "bottom", "box-decoration-break", 354 | "box-shadow", "box-sizing", "break-after", "break-before", "break-inside", 355 | "caption-side", "clear", "clip", "color", "color-profile", "column-count", 356 | "column-fill", "column-gap", "column-rule", "column-rule-color", 357 | "column-rule-style", "column-rule-width", "column-span", "column-width", 358 | "columns", "content", "counter-increment", "counter-reset", "crop", "cue", 359 | "cue-after", "cue-before", "cursor", "direction", "display", 360 | "dominant-baseline", "drop-initial-after-adjust", 361 | "drop-initial-after-align", "drop-initial-before-adjust", 362 | "drop-initial-before-align", "drop-initial-size", "drop-initial-value", 363 | "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis", 364 | "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap", 365 | "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings", 366 | "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust", 367 | "font-stretch", "font-style", "font-synthesis", "font-variant", 368 | "font-variant-alternates", "font-variant-caps", "font-variant-east-asian", 369 | "font-variant-ligatures", "font-variant-numeric", "font-variant-position", 370 | "font-weight", "grid-cell", "grid-column", "grid-column-align", 371 | "grid-column-sizing", "grid-column-span", "grid-columns", "grid-flow", 372 | "grid-row", "grid-row-align", "grid-row-sizing", "grid-row-span", 373 | "grid-rows", "grid-template", "hanging-punctuation", "height", "hyphens", 374 | "icon", "image-orientation", "image-rendering", "image-resolution", 375 | "inline-box-align", "justify-content", "left", "letter-spacing", 376 | "line-break", "line-height", "line-stacking", "line-stacking-ruby", 377 | "line-stacking-shift", "line-stacking-strategy", "list-style", 378 | "list-style-image", "list-style-position", "list-style-type", "margin", 379 | "margin-bottom", "margin-left", "margin-right", "margin-top", 380 | "marker-offset", "marks", "marquee-direction", "marquee-loop", 381 | "marquee-play-count", "marquee-speed", "marquee-style", "max-height", 382 | "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index", 383 | "nav-left", "nav-right", "nav-up", "opacity", "order", "orphans", "outline", 384 | "outline-color", "outline-offset", "outline-style", "outline-width", 385 | "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y", 386 | "padding", "padding-bottom", "padding-left", "padding-right", "padding-top", 387 | "page", "page-break-after", "page-break-before", "page-break-inside", 388 | "page-policy", "pause", "pause-after", "pause-before", "perspective", 389 | "perspective-origin", "pitch", "pitch-range", "play-during", "position", 390 | "presentation-level", "punctuation-trim", "quotes", "region-break-after", 391 | "region-break-before", "region-break-inside", "region-fragment", 392 | "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness", 393 | "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang", 394 | "ruby-position", "ruby-span", "shape-inside", "shape-outside", "size", 395 | "speak", "speak-as", "speak-header", 396 | "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set", 397 | "tab-size", "table-layout", "target", "target-name", "target-new", 398 | "target-position", "text-align", "text-align-last", "text-decoration", 399 | "text-decoration-color", "text-decoration-line", "text-decoration-skip", 400 | "text-decoration-style", "text-emphasis", "text-emphasis-color", 401 | "text-emphasis-position", "text-emphasis-style", "text-height", 402 | "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow", 403 | "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position", 404 | "text-wrap", "top", "transform", "transform-origin", "transform-style", 405 | "transition", "transition-delay", "transition-duration", 406 | "transition-property", "transition-timing-function", "unicode-bidi", 407 | "vertical-align", "visibility", "voice-balance", "voice-duration", 408 | "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress", 409 | "voice-volume", "volume", "white-space", "widows", "width", "word-break", 410 | "word-spacing", "word-wrap", "z-index", "zoom", 411 | // SVG-specific 412 | "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color", 413 | "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events", 414 | "color-interpolation", "color-interpolation-filters", "color-profile", 415 | "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering", 416 | "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke", 417 | "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", 418 | "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering", 419 | "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal", 420 | "glyph-orientation-vertical", "kerning", "text-anchor", "writing-mode" 421 | ]); 422 | 423 | var colorKeywords = keySet([ 424 | "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige", 425 | "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown", 426 | "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue", 427 | "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod", 428 | "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen", 429 | "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen", 430 | "darkslateblue", "darkslategray", "darkturquoise", "darkviolet", 431 | "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick", 432 | "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite", 433 | "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew", 434 | "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender", 435 | "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral", 436 | "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink", 437 | "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", 438 | "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta", 439 | "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple", 440 | "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise", 441 | "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", 442 | "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered", 443 | "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred", 444 | "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue", 445 | "purple", "red", "rosybrown", "royalblue", "saddlebrown", "salmon", 446 | "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue", 447 | "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan", 448 | "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white", 449 | "whitesmoke", "yellow", "yellowgreen" 450 | ]); 451 | 452 | var valueKeywords = keySet([ 453 | "above", "absolute", "activeborder", "activecaption", "afar", 454 | "after-white-space", "ahead", "alias", "all", "all-scroll", "alternate", 455 | "always", "amharic", "amharic-abegede", "antialiased", "appworkspace", 456 | "arabic-indic", "armenian", "asterisks", "auto", "avoid", "avoid-column", "avoid-page", 457 | "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary", 458 | "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box", 459 | "both", "bottom", "break", "break-all", "break-word", "button", "button-bevel", 460 | "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian", 461 | "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret", 462 | "cell", "center", "checkbox", "circle", "cjk-earthly-branch", 463 | "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote", 464 | "col-resize", "collapse", "column", "compact", "condensed", "contain", "content", 465 | "content-box", "context-menu", "continuous", "copy", "cover", "crop", 466 | "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal", 467 | "decimal-leading-zero", "default", "default-button", "destination-atop", 468 | "destination-in", "destination-out", "destination-over", "devanagari", 469 | "disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted", 470 | "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out", 471 | "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede", 472 | "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er", 473 | "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er", 474 | "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et", 475 | "ethiopic-halehame-gez", "ethiopic-halehame-om-et", 476 | "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et", 477 | "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", 478 | "ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed", 479 | "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes", 480 | "forwards", "from", "geometricPrecision", "georgian", "graytext", "groove", 481 | "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew", 482 | "help", "hidden", "hide", "higher", "highlight", "highlighttext", 483 | "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore", 484 | "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite", 485 | "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis", 486 | "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert", 487 | "italic", "justify", "kannada", "katakana", "katakana-iroha", "keep-all", "khmer", 488 | "landscape", "lao", "large", "larger", "left", "level", "lighter", 489 | "line-through", "linear", "lines", "list-item", "listbox", "listitem", 490 | "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian", 491 | "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian", 492 | "lower-roman", "lowercase", "ltr", "malayalam", "match", 493 | "media-controls-background", "media-current-time-display", 494 | "media-fullscreen-button", "media-mute-button", "media-play-button", 495 | "media-return-to-realtime-button", "media-rewind-button", 496 | "media-seek-back-button", "media-seek-forward-button", "media-slider", 497 | "media-sliderthumb", "media-time-remaining-display", "media-volume-slider", 498 | "media-volume-slider-container", "media-volume-sliderthumb", "medium", 499 | "menu", "menulist", "menulist-button", "menulist-text", 500 | "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic", 501 | "mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize", 502 | "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop", 503 | "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap", 504 | "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote", 505 | "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset", 506 | "outside", "outside-shape", "overlay", "overline", "padding", "padding-box", 507 | "painted", "page", "paused", "persian", "plus-darker", "plus-lighter", "pointer", 508 | "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button", 509 | "radio", "read-only", "read-write", "read-write-plaintext-only", "rectangle", "region", 510 | "relative", "repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba", 511 | "ridge", "right", "round", "row-resize", "rtl", "run-in", "running", 512 | "s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield", 513 | "searchfield-cancel-button", "searchfield-decoration", 514 | "searchfield-results-button", "searchfield-results-decoration", 515 | "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama", 516 | "single", "skip-white-space", "slide", "slider-horizontal", 517 | "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow", 518 | "small", "small-caps", "small-caption", "smaller", "solid", "somali", 519 | "source-atop", "source-in", "source-out", "source-over", "space", "square", 520 | "square-button", "start", "static", "status-bar", "stretch", "stroke", 521 | "sub", "subpixel-antialiased", "super", "sw-resize", "table", 522 | "table-caption", "table-cell", "table-column", "table-column-group", 523 | "table-footer-group", "table-header-group", "table-row", "table-row-group", 524 | "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai", 525 | "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight", 526 | "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er", 527 | "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top", 528 | "transparent", "ultra-condensed", "ultra-expanded", "underline", "up", 529 | "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal", 530 | "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url", 531 | "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted", 532 | "visibleStroke", "visual", "w-resize", "wait", "wave", "wider", 533 | "window", "windowframe", "windowtext", "x-large", "x-small", "xor", 534 | "xx-large", "xx-small" 535 | ]); 536 | 537 | function tokenCComment(stream, state) { 538 | var maybeEnd = false, ch; 539 | while ((ch = stream.next()) != null) { 540 | if (maybeEnd && ch == "/") { 541 | state.tokenize = null; 542 | break; 543 | } 544 | maybeEnd = (ch == "*"); 545 | } 546 | return ["comment", "comment"]; 547 | } 548 | 549 | CodeMirror.defineMIME("text/css", { 550 | atMediaTypes: atMediaTypes, 551 | atMediaFeatures: atMediaFeatures, 552 | propertyKeywords: propertyKeywords, 553 | colorKeywords: colorKeywords, 554 | valueKeywords: valueKeywords, 555 | hooks: { 556 | "<": function(stream, state) { 557 | function tokenSGMLComment(stream, state) { 558 | var dashes = 0, ch; 559 | while ((ch = stream.next()) != null) { 560 | if (dashes >= 2 && ch == ">") { 561 | state.tokenize = null; 562 | break; 563 | } 564 | dashes = (ch == "-") ? dashes + 1 : 0; 565 | } 566 | return ["comment", "comment"]; 567 | } 568 | if (stream.eat("!")) { 569 | state.tokenize = tokenSGMLComment; 570 | return tokenSGMLComment(stream, state); 571 | } 572 | }, 573 | "/": function(stream, state) { 574 | if (stream.eat("*")) { 575 | state.tokenize = tokenCComment; 576 | return tokenCComment(stream, state); 577 | } 578 | return false; 579 | } 580 | }, 581 | name: "css" 582 | }); 583 | 584 | CodeMirror.defineMIME("text/x-scss", { 585 | atMediaTypes: atMediaTypes, 586 | atMediaFeatures: atMediaFeatures, 587 | propertyKeywords: propertyKeywords, 588 | colorKeywords: colorKeywords, 589 | valueKeywords: valueKeywords, 590 | allowNested: true, 591 | hooks: { 592 | ":": function(stream) { 593 | if (stream.match(/\s*{/)) { 594 | return [null, "{"]; 595 | } 596 | return false; 597 | }, 598 | "$": function(stream) { 599 | stream.match(/^[\w-]+/); 600 | if (stream.peek() == ":") { 601 | return ["variable", "variable-definition"]; 602 | } 603 | return ["variable", "variable"]; 604 | }, 605 | "/": function(stream, state) { 606 | if (stream.eat("/")) { 607 | stream.skipToEnd(); 608 | return ["comment", "comment"]; 609 | } else if (stream.eat("*")) { 610 | state.tokenize = tokenCComment; 611 | return tokenCComment(stream, state); 612 | } else { 613 | return ["operator", "operator"]; 614 | } 615 | }, 616 | "#": function(stream) { 617 | if (stream.eat("{")) { 618 | return ["operator", "interpolation"]; 619 | } else { 620 | stream.eatWhile(/[\w\\\-]/); 621 | return ["atom", "hash"]; 622 | } 623 | } 624 | }, 625 | name: "css" 626 | }); 627 | })(); 628 | -------------------------------------------------------------------------------- /src/clj/cljsfiddle/closure.clj: -------------------------------------------------------------------------------- 1 | (ns cljsfiddle.closure 2 | (:require [clojure.java.io :as io] 3 | [clojure.string :as s] 4 | [clojure.set :as set] 5 | [clojure.edn :as edn] 6 | [cljsfiddle.db :as db] 7 | [cljsfiddle.db.util :refer [cljs-object-from-src read-all]] 8 | [cljsfiddle.db.src :as src] 9 | [cljs.closure :as cljs] 10 | [cljs.env :as cljs-env] 11 | [taoensso.timbre :as log] 12 | [clojure.pprint :refer [pprint]] 13 | [datomic.api :as d] 14 | [compojure.core :refer :all] 15 | [environ.core :refer (env)]) 16 | (:import [clojure.lang LineNumberingPushbackReader] 17 | [java.util.logging Level] 18 | [java.io StringReader BufferedReader] 19 | [com.google.javascript.jscomp.Compiler] 20 | [com.google.javascript.jscomp JSSourceFile 21 | CompilerOptions 22 | CompilationLevel 23 | ClosureCodingConvention])) 24 | 25 | ;; 1 day(s) 26 | (def max-age (str "max-age=" (* 60 60 24 1))) 27 | 28 | (defn edn-response [edn-data] 29 | {:status 200 30 | :headers {"Content-Type" "application/edn"} 31 | :body (pr-str edn-data)}) 32 | 33 | (defn compile-cljs* [cljs-src-str] 34 | (let [cljs-src (read-all cljs-src-str) 35 | js-src (cljs-env/with-compiler-env 36 | (cljs-env/default-compiler-env) 37 | (cljs/-compile cljs-src {}))] 38 | js-src)) 39 | 40 | (defn js-errors [error] 41 | {:description (.description error) 42 | :file (.sourceName error) 43 | :line (.lineNumber error)}) 44 | 45 | ;; TODO opts & warnings 46 | (defn make-compiler [opts] 47 | (fn mk-compiler 48 | ([src] (mk-compiler "__NO_SOURCE_FILE__" src)) 49 | ([name src] 50 | (let [options (let [level CompilationLevel/WHITESPACE_ONLY 51 | compiler-options (CompilerOptions.)] 52 | (.setCodingConvention compiler-options (ClosureCodingConvention.)) 53 | (.setOptionsForCompilationLevel level compiler-options) 54 | compiler-options) 55 | compiler (com.google.javascript.jscomp.Compiler.) 56 | src (JSSourceFile/fromCode name src) 57 | externs (JSSourceFile/fromCode "externs" "") 58 | result (.compile compiler externs src options)] 59 | (if (.success result) 60 | (merge {:status :success 61 | :file name 62 | :js-src (.toSource compiler)} 63 | (when-let [warnings (seq (.warnings result))] 64 | {:warnings (mapv js-errors warnings)})) 65 | (merge {:status :error 66 | :file name 67 | :errors (mapv js-errors (.errors result))} 68 | (when-let [warnings (seq (.warnings result))] 69 | {:warnings (mapv js-errors warnings)}))))))) 70 | 71 | (def closure-compile (make-compiler {:optimizations :whitespace})) 72 | 73 | (defn compile-routes [conn] 74 | (routes 75 | (POST "/compile" 76 | {{cljs-src-str :src} :params} 77 | (try 78 | (let [db (d/db conn) 79 | cljs-obj (cljs-object-from-src cljs-src-str) 80 | cljs-tx (src/cljs-tx db cljs-obj) 81 | tdb (:db-after (d/with db (:tx cljs-tx))) 82 | deps (db/dependency-files tdb (:ns cljs-obj)) 83 | js-src-obj (closure-compile (:js-src cljs-obj)) 84 | js-src-obj (assoc js-src-obj :dependencies deps :status :ok)] 85 | (edn-response js-src-obj)) 86 | (catch clojure.lang.ExceptionInfo e 87 | (edn-response 88 | {:status :exception 89 | :msg (.getMessage e)})) 90 | (catch Exception e 91 | (log/error (.getMessage e)) 92 | (edn-response 93 | {:status :exception 94 | :msg "Something went terribly wrong."})))))) 95 | 96 | ;(compile-cljs* " \n\n(defn adsd [x y] (+ x y))") 97 | 98 | (defn deps-routes [conn] 99 | (routes 100 | (GET "/:version/:file" 101 | [version file] 102 | (let [sha (first (s/split file #"\.")) 103 | [type src] (first 104 | (d/q '[:find ?typename ?text 105 | :in $ ?sha 106 | :where 107 | [?blob :cljsfiddle.blob/sha ?sha] 108 | [?blob :cljsfiddle.blob/text ?text] 109 | [?src :cljsfiddle.src/blob ?blob] 110 | [?src :cljsfiddle.src/type ?type] 111 | [?type :db/ident ?typename]] 112 | (d/db conn) sha))] 113 | (when (and type src) 114 | (let [csrc (condp = type 115 | :cljsfiddle.src.type/cljs (:js-src (closure-compile (compile-cljs* src))) 116 | :cljsfiddle.src.type/js (:js-src (closure-compile src)))] 117 | (spit (str "resources/jscache/" version "/" file) csrc) 118 | {:status 200 119 | :headers {"Content-Type" "application/javascript"} 120 | :body csrc})))))) 121 | 122 | (comment 123 | (def conn (-> :datomic-uri 124 | env 125 | d/connect)) 126 | 127 | (def db (-> :datomic-uri 128 | env 129 | d/connect 130 | d/db)) 131 | 132 | (d/q '[:find (sample 2 ?sha) 133 | :where 134 | [?src :cljsfiddle.src/type :cljsfiddle.src.type/cljs] 135 | [?src :cljsfiddle.src/blob ?blob] 136 | [?blob :cljsfiddle.blob/sha ?sha]] 137 | db) 138 | 139 | (defn fffirst [coll] 140 | (first (ffirst coll))) 141 | 142 | 143 | (closure-compile 144 | (fffirst (d/q '[:find (sample 1 ?src-txt) 145 | :where 146 | [?src :cljsfiddle.src/type :cljsfiddle.src.type/js] 147 | [?src :cljsfiddle.src/blob ?blob] 148 | [?blob :cljsfiddle.blob/text ?src-txt]] 149 | db))) 150 | 151 | (use 'clojure.pprint) 152 | (pprint 153 | (src/cljs-tx 154 | db 155 | (cljs-object-from-src "(+ 1 2 3)"))) 156 | 157 | (d/touch (d/entity db 17592186045492)) 158 | 159 | (first (seq (d/datoms db :avet :cljsfiddle.src/ns nil))) 160 | 161 | 162 | (d/q '[:find ?typename ?text 163 | :in $ ?sha 164 | :where 165 | [?blob :cljsfiddle.blob/sha ?sha] 166 | [?blob :cljsfiddle.blob/text ?text] 167 | [?src :cljsfiddle.src/blob ?blob] 168 | [?src :cljsfiddle.src/type ?type] 169 | [?type :db/ident ?typename]] 170 | db "5773e8ca4100bda76ebcb213f28e83bd7b4d14ee") 171 | 172 | ) 173 | 174 | 175 | -------------------------------------------------------------------------------- /src/clj/cljsfiddle/db.clj: -------------------------------------------------------------------------------- 1 | (ns cljsfiddle.db 2 | (:require [cljsfiddle.db.fiddle :as fiddle] 3 | [cljsfiddle.db.util :as util] 4 | [datomic.api :as d] 5 | [environ.core :refer (env)])) 6 | 7 | (defn requires [db ns] 8 | (map first 9 | (d/q '[:find ?requires 10 | :in $ ?ns 11 | :where 12 | [?e :cljsfiddle.src/ns ?ns] 13 | [?e :cljsfiddle.src/requires ?requires]] 14 | db ns))) 15 | 16 | (defn- toposort* [node dag sorted temporary visited] 17 | (when-not (@visited node) 18 | (when (@temporary node) (throw (ex-info "Not a DAG." {}))) 19 | (swap! temporary conj node) 20 | (doseq [n (requires dag node)] 21 | (toposort* n dag sorted temporary visited)) 22 | (swap! visited conj node) 23 | (swap! sorted conj node))) 24 | 25 | (defn toposort [node dag] 26 | (let [sorted (atom [])] 27 | (toposort* node dag sorted (atom #{}) (atom #{})) 28 | @sorted)) 29 | 30 | (defn dependencies 31 | "Returns the dependencies of ns in dependency order" 32 | [db ns] 33 | (butlast (toposort ns db))) 34 | 35 | (defn sha-by-ns [db ns] 36 | (ffirst (d/q '[:find ?sha 37 | :in $ ?ns 38 | :where 39 | [?e :cljsfiddle.src/ns ?ns] 40 | [?e :cljsfiddle.src/blob ?b] 41 | [?b :cljsfiddle.blob/sha ?sha]] 42 | db ns))) 43 | 44 | (defn dependency-files 45 | [db ns] 46 | (let [files (distinct 47 | (for [ns (dependencies db ns)] 48 | (let [sha (sha-by-ns db ns)] 49 | (str sha ".js")))) 50 | base (-> (d/entity db :goog/base) 51 | :cljsfiddle.src/blob 52 | :cljsfiddle.blob/sha)] 53 | (cons (str base ".js") 54 | files))) 55 | 56 | (defn save-fiddle [conn fiddle] 57 | (let [db (d/db conn) 58 | {:keys [tx id]} (fiddle/fiddle-tx db fiddle)] 59 | (if-not (empty? tx) 60 | (:db-after @(d/transact conn tx)) 61 | db))) 62 | 63 | (defn find-fiddle-by-ns [db ns] 64 | (let [query '[:find ?fiddle 65 | :in $ ?ns 66 | :where 67 | [?src :cljsfiddle.src/ns ?ns] 68 | [?fiddle :cljsfiddle/cljs ?src]] 69 | fiddle-id (ffirst (d/q query db ns))] 70 | (when fiddle-id 71 | (d/touch (d/entity db fiddle-id))))) 72 | 73 | (defn fiddles-by-user [db user] 74 | (let [query '[:find ?ns ?inst 75 | :in $ ?user [?filetype ...] 76 | :where 77 | [?src :cljsfiddle.src/ns ?ns] 78 | [(.startsWith ?ns ?user)] 79 | [?fiddle :cljsfiddle/cljs ?src] 80 | [?fiddle ?filetype ?src] 81 | [?src :cljsfiddle.src/blob ?blob] 82 | [?blob :cljsfiddle.blob/sha _ ?tx] 83 | [?tx :db/txInstant ?inst]]] 84 | (d/q query db user [:cljsfiddle/cljs :cljsfiddle/html :cljsfiddle/css]))) 85 | 86 | (comment 87 | (def db (-> :datomic-uri 88 | env 89 | d/connect 90 | d/db)) 91 | 92 | (requires db "cljs.reader") 93 | 94 | (dependencies db "cljs.core") 95 | 96 | (:cljsfiddle.src/ns (:cljsfiddle/cljs (find-fiddle-by-ns db "foo.bar"))) 97 | 98 | 99 | (util/fiddle "(ns foo.bar) (defn add [x y] (+ x y))" 100 | "" 101 | "body {}")) 102 | 103 | -------------------------------------------------------------------------------- /src/clj/cljsfiddle/db/blob.clj: -------------------------------------------------------------------------------- 1 | (ns cljsfiddle.db.blob 2 | (:require [cljsfiddle.db.util :refer (tempid?)] 3 | [datomic.api :as d])) 4 | 5 | (defn- blob-eid [db sha] 6 | "Returns the entity id for sha, or a tempid if sha does not exist" 7 | (or (:e (first (d/datoms db :avet :cljsfiddle.blob/sha sha))) 8 | (d/tempid :db.part/user))) 9 | 10 | (defn blob-tx 11 | "Return {:id :tx for blob and sha" 12 | [db {:keys [text sha] :as blob}] 13 | (let [eid (blob-eid db sha)] 14 | (if (tempid? eid) 15 | {:id eid 16 | :tx [{:db/id eid 17 | :cljsfiddle.blob/sha sha 18 | :cljsfiddle.blob/text text}]} 19 | {:id eid 20 | :tx []}))) 21 | -------------------------------------------------------------------------------- /src/clj/cljsfiddle/db/fiddle.clj: -------------------------------------------------------------------------------- 1 | (ns cljsfiddle.db.fiddle 2 | (:require [clojure.data :as data] 3 | [datomic.api :as d] 4 | [cljsfiddle.db.schema :refer (schema)] 5 | [cljsfiddle.db.util :refer (tempid?)] 6 | [cljsfiddle.db.blob :as blob] 7 | [cljsfiddle.db.src :as src]) 8 | (:import [java.util Date])) 9 | 10 | (defn fiddle-eid [db ns] 11 | (or (ffirst (d/q '[:find ?e 12 | :in $ ?ns 13 | :where 14 | [?c :cljsfiddle.src/ns ?ns] 15 | [?e :cljsfiddle/cljs ?c]] 16 | db ns)) 17 | (d/tempid :db.part/user))) 18 | 19 | (defn fiddle-tx [db 20 | {:keys [cljs html css]}] 21 | (let [fid (fiddle-eid db (:ns cljs)) 22 | fent (d/entity db fid) 23 | cljs-old-eid (:db/id (:cljsfiddle/cljs fent)) 24 | html-old-eid (:db/id (:cljsfiddle/html fent)) 25 | css-old-eid (:db/id (:cljsfiddle/css fent)) 26 | {cljs-tx :tx cljs-eid :id} (src/cljs-tx db cljs) 27 | {html-tx :tx html-eid :id} (src/html-tx db html) 28 | {css-tx :tx css-eid :id} (src/css-tx db css)] 29 | {:id fid 30 | :tx (let [tx (cond-> (vec (concat cljs-tx html-tx css-tx)) 31 | 32 | (not= cljs-old-eid cljs-eid) 33 | (conj [:db/add fid :cljsfiddle/cljs cljs-eid]) 34 | 35 | (not= html-old-eid html-eid) 36 | (conj [:db/add fid :cljsfiddle/html html-eid]) 37 | 38 | (not= css-old-eid css-eid) 39 | (conj [:db/add fid :cljsfiddle/css css-eid]))] 40 | (if-not (empty? tx) 41 | (conj tx [:db/add fid :cljsfiddle/updated (Date.)]) 42 | []))})) 43 | 44 | 45 | 46 | (comment 47 | ;; Some tests 48 | (use 'clojure.pprint) 49 | 50 | (def uri "datomic:mem://js-tests") 51 | (def conn (do (d/delete-database uri) 52 | (d/create-database uri) 53 | (let [conn (d/connect uri)] 54 | @(d/transact conn schema) 55 | conn))) 56 | 57 | 58 | (def cljs-src-1 {:src "some cljs src" 59 | :sha "fjsodidfjs" 60 | :ns "foo.bar" 61 | :requires []}) 62 | 63 | (def cljs-src-2 {:src "some (mod) cljs src" 64 | :sha "fjsodsdfsifsdfsdfjs" 65 | :ns "foo.bar" 66 | :requires ["foo.baz" "foo.quux"]}) 67 | 68 | (def css-src-1 {:src "some css src" 69 | :sha "fjdsiofjos"}) 70 | 71 | (def css-src-2 {:src "some other css src" 72 | :sha "gjsuureiwef"}) 73 | 74 | (def html-src-1 {:src "some html-src" 75 | :sha "diosjdfsoijgso"}) 76 | 77 | (def html-src-2 {:src "some other html src" 78 | :sha "fhisudhfoiase"}) 79 | 80 | 81 | (def fiddle-1 {:cljs cljs-src-1 82 | :html html-src-1 83 | :css css-src-1}) 84 | 85 | (def fiddle-2 {:cljs cljs-src-1 86 | :html html-src-1 87 | :css css-src-2}) 88 | 89 | (def fiddle-3 {:cljs cljs-src-2 90 | :html html-src-1 91 | :css css-src-1}) 92 | 93 | 94 | (-> (fiddle-tx (d/db conn) 95 | fiddle-1) 96 | pprint) 97 | 98 | 99 | @(d/transact conn (fiddle-tx (d/db conn) fiddle-1)) 100 | 101 | ) -------------------------------------------------------------------------------- /src/clj/cljsfiddle/db/schema.clj: -------------------------------------------------------------------------------- 1 | (ns cljsfiddle.db.schema 2 | (:require [datomic.api :as d])) 3 | 4 | (def schema 5 | [;; blob 6 | {:db/id (d/tempid :db.part/db) 7 | :db/ident :cljsfiddle.blob/sha 8 | ;; :db/index true (implied by uniqueness) 9 | :db/unique :db.unique/value 10 | :db/valueType :db.type/string 11 | :db/cardinality :db.cardinality/one 12 | :db.install/_attribute :db.part/db} 13 | 14 | {:db/id (d/tempid :db.part/db) 15 | :db/ident :cljsfiddle.blob/text 16 | :db/valueType :db.type/string 17 | :db/cardinality :db.cardinality/one 18 | :db.install/_attribute :db.part/db} 19 | 20 | ;; src 21 | {:db/id (d/tempid :db.part/db) 22 | :db/ident :cljsfiddle.src/blob 23 | :db/valueType :db.type/ref 24 | :db/cardinality :db.cardinality/one 25 | :db.install/_attribute :db.part/db} 26 | 27 | {:db/id (d/tempid :db.part/db) 28 | :db/ident :cljsfiddle.src/ns 29 | :db/unique :db.unique/identity 30 | ;; :db/index true (implied by uniqueness) 31 | :db/valueType :db.type/string 32 | :db/cardinality :db.cardinality/one 33 | :db.install/_attribute :db.part/db} 34 | 35 | {:db/id (d/tempid :db.part/db) 36 | :db/ident :cljsfiddle.src/requires 37 | :db/valueType :db.type/string 38 | :db/cardinality :db.cardinality/many 39 | :db.install/_attribute :db.part/db} 40 | 41 | {:db/id (d/tempid :db.part/db) 42 | :db/ident :cljsfiddle.src/type 43 | :db/valueType :db.type/ref 44 | :db/cardinality :db.cardinality/one 45 | :db.install/_attribute :db.part/db} 46 | 47 | {:db/id (d/tempid :db.part/user) 48 | :db/ident :cljsfiddle.src.type/cljs} 49 | 50 | {:db/id (d/tempid :db.part/user) 51 | :db/ident :cljsfiddle.src.type/js} 52 | 53 | {:db/id (d/tempid :db.part/user) 54 | :db/ident :cljsfiddle.src.type/html} 55 | 56 | {:db/id (d/tempid :db.part/user) 57 | :db/ident :cljsfiddle.src.type/css} 58 | 59 | ;; fiddle 60 | {:db/id (d/tempid :db.part/db) 61 | :db/ident :cljsfiddle/cljs 62 | :db/valueType :db.type/ref 63 | :db/cardinality :db.cardinality/one 64 | :db.install/_attribute :db.part/db} 65 | 66 | {:db/id (d/tempid :db.part/db) 67 | :db/ident :cljsfiddle/html 68 | :db/valueType :db.type/ref 69 | :db/cardinality :db.cardinality/one 70 | :db.install/_attribute :db.part/db} 71 | 72 | {:db/id (d/tempid :db.part/db) 73 | :db/ident :cljsfiddle/css 74 | :db/valueType :db.type/ref 75 | :db/cardinality :db.cardinality/one 76 | :db.install/_attribute :db.part/db} 77 | 78 | {:db/id (d/tempid :db.part/db) 79 | :db/ident :cljsfiddle/updated 80 | :db/valueType :db.type/instant 81 | :db/cardinality :db.cardinality/one 82 | :db.install/_attribute :db.part/db}]) 83 | -------------------------------------------------------------------------------- /src/clj/cljsfiddle/db/src.clj: -------------------------------------------------------------------------------- 1 | (ns cljsfiddle.db.src 2 | (:require [clojure.data :as data] 3 | [datomic.api :as d] 4 | [cljsfiddle.db.schema :refer (schema)] 5 | [cljsfiddle.db.util :refer (tempid?)] 6 | [cljsfiddle.db.blob :as blob])) 7 | 8 | (defn ns-eid 9 | "Return the entity id for ns, or a tempid if ns does not exist." 10 | [db ns] 11 | {:pre [(not (nil? ns))]} 12 | (or (:e (first (d/datoms db :avet :cljsfiddle.src/ns ns))) 13 | (d/tempid :db.part/user))) 14 | 15 | (defn src-tx 16 | [db {:keys [src sha ns requires type blob-eid]}] 17 | (let [{:keys [tx id]} (let [nid (ns-eid db ns)] 18 | (if (tempid? nid) 19 | {:id nid 20 | :tx [{:db/id nid 21 | :cljsfiddle.src/blob blob-eid 22 | :cljsfiddle.src/ns ns 23 | :cljsfiddle.src/requires requires 24 | :cljsfiddle.src/type type}]} 25 | (let [code (d/entity db nid) 26 | old-sha (-> code :cljsfiddle.src/blob :cljsfiddle.blob/sha)] 27 | (if (not= old-sha sha) 28 | (let [old-requires (set (:cljsfiddle.src/requires code)) 29 | new-requires (set requires) 30 | [req-retracts req-asserts] (data/diff old-requires new-requires) 31 | req-retracts-tx (for [req req-retracts] 32 | [:db/retract nid :cljsfiddle.src/requires req]) 33 | req-asserts-tx (for [req req-asserts] 34 | [:db/add nid :cljsfiddle.src/requires req])] 35 | {:id nid 36 | :tx (concat req-retracts-tx 37 | req-asserts-tx 38 | [[:db/add nid :cljsfiddle.src/blob blob-eid]])}) 39 | {:id nid :tx []}))))] 40 | {:id id 41 | :tx tx})) 42 | 43 | (defn cljs-tx 44 | "Returns {:id :tx } for cljs" 45 | [db cljs] 46 | {:pre [(every? (partial contains? cljs) 47 | [:src :sha :ns :requires])] 48 | :post [(every? (partial contains? %) [:tx :id])]} 49 | (let [{:keys [sha src]} cljs 50 | {blob-id :id blob-tx :tx} (blob/blob-tx db {:sha sha :text src})] 51 | (update-in (src-tx db (assoc cljs 52 | :type :cljsfiddle.src.type/cljs 53 | :provides [(:ns cljs)] 54 | :blob-eid blob-id)) 55 | [:tx] 56 | (fn [tx] (concat blob-tx tx))))) 57 | 58 | ;; TODO 59 | (defn js-tx 60 | "Returns transaction data for for js" 61 | [db js] 62 | {:pre [(every? (partial contains? js) 63 | [:src :sha :provides :requires])] 64 | :post []} 65 | (let [{:keys [sha src]} js 66 | {blob-id :id blob-tx :tx} (blob/blob-tx db {:sha sha :text src})] 67 | (apply concat 68 | blob-tx 69 | (for [ns (:provides js)] 70 | (:tx (src-tx db (assoc js 71 | :type :cljsfiddle.src.type/js 72 | :ns ns 73 | :blob-eid blob-id))))))) 74 | 75 | (defn none-namespaced-src-tx 76 | [db {:keys [src sha type]}] 77 | (let [{blob-id :id blob-tx :tx} (blob/blob-tx db {:sha sha :text src}) 78 | eid (when-not (tempid? blob-id) 79 | (ffirst 80 | (d/q '[:find ?e 81 | :in $ ?blob ?type 82 | :where 83 | [?e :cljsfiddle.src/blob ?blob] 84 | [?e :cljsfiddle.src/type ?type]] 85 | db blob-id type)))] 86 | (if-not eid 87 | (let [tid (d/tempid :db.part/user)] 88 | {:id tid 89 | :tx (concat blob-tx 90 | [{:db/id tid 91 | :cljsfiddle.src/type type 92 | :cljsfiddle.src/blob blob-id}])}) 93 | {:id eid :tx []}))) 94 | 95 | (defn html-tx 96 | "Returns {:id :tx } for html" 97 | [db html] 98 | (none-namespaced-src-tx db (assoc html :type :cljsfiddle.src.type/html))) 99 | 100 | (defn css-tx 101 | "Returns {:id :tx } for css" 102 | [db css] 103 | (none-namespaced-src-tx db (assoc css :type :cljsfiddle.src.type/css))) 104 | 105 | (comment 106 | (let [uri "datomic:mem://js-tests" 107 | conn (do (d/delete-database uri) 108 | (d/create-database uri) 109 | (let [conn (d/connect uri)] 110 | @(d/transact conn schema) 111 | conn)) 112 | js-src-1 {:src "some js source" 113 | :sha "heifjioisdj" 114 | :provides ["foo" "bar" "baz"] 115 | :requires ["a" "b" "c"]} 116 | 117 | js-src-2 {:src "some (modified) js source" 118 | :sha "!heifjioisdj" 119 | :provides ["foo" "bar" "baz"] 120 | :requires ["a" "b" "c"]} 121 | 122 | js-src-3 {:src "some more (more modified) js source" 123 | :sha "fsfiosifdsf" 124 | :provides ["foo" "bar" "baz"] 125 | :requires ["a" "b" "d"]} 126 | 127 | js-src-4 {:src "some (even more modified) js source" 128 | :sha "fsiokfgmdoig" 129 | :provides ["foo" "bar" "quux"] 130 | :requires ["a" "b" "c"]} 131 | 132 | ns->requires (fn [db ns] 133 | (set 134 | (map first 135 | (d/q '[:find ?reqs 136 | :in $ ?ns 137 | :where 138 | [?e :cljsfiddle.src/ns ?ns] 139 | [?e :cljsfiddle.src/requires ?reqs]] 140 | db ns)))) 141 | ] 142 | ;; Transact js-src-1 143 | @(d/transact conn (js-tx (d/db conn) js-src-1)) 144 | (assert (= (ns->requires (d/db conn) "foo") 145 | #{"a" "b" "c"})) 146 | 147 | @(d/transact conn (js-tx (d/db conn) js-src-2)) 148 | (assert (= (ns->requires (d/db conn) "foo") 149 | #{"a" "b" "c"})) 150 | 151 | @(d/transact conn (js-tx (d/db conn) js-src-3)) 152 | (assert (= (ns->requires (d/db conn) "foo") 153 | #{"a" "b" "d"}))) 154 | 155 | ;; Some tests 156 | (use 'clojure.pprint) 157 | 158 | (def uri "datomic:mem://js-tests") 159 | (def conn (do (d/delete-database uri) 160 | (d/create-database uri) 161 | (let [conn (d/connect uri)] 162 | @(d/transact conn schema) 163 | conn))) 164 | 165 | ;; The provide list has changed (What to do?) 166 | ;; Should I retract old provides? Probably in the js case. 167 | ;; But that's a TODO for now. 168 | (def src-4 ) 169 | 170 | (def cljs-src-1 {:src "some cljs src" 171 | :sha "fjsodidfjs" 172 | :ns "foo.bar" 173 | :requires []}) 174 | 175 | (def cljs-src-2 {:src "some (mod) cljs src" 176 | :sha "fjsodsdfsifsdfsdfjs" 177 | :ns "foo.bar" 178 | :requires ["foo.baz"]}) 179 | 180 | @(d/transact conn (js-tx (d/db conn) src-1)) 181 | 182 | (-> (js-tx (d/db conn) 183 | src-2) 184 | pprint) 185 | 186 | @(d/transact conn (:tx (cljs-tx (d/db conn) cljs-src-2))) 187 | 188 | (-> (cljs-tx (d/db conn) 189 | cljs-src-2) 190 | pprint) 191 | 192 | (def html-src-1 {:src "some html src" 193 | :sha "jfisofjoijfsoij"}) 194 | 195 | (def html-src-2 {:src "some (other) html src" 196 | :sha "jfisofjoijfsfsdfsoij"}) 197 | 198 | 199 | @(d/transact conn (html-tx (d/db conn) html-src-1)) 200 | 201 | (-> (html-tx (d/db conn) 202 | html-src-1) 203 | pprint) 204 | 205 | (def css-src-1 {:src "some html src" 206 | :sha "jfisofjoijfsoij"}) 207 | 208 | (def css-src-2 {:src "some (other) html src" 209 | :sha "jfisofjoijfsfsdfsoij"}) 210 | 211 | @(d/transact conn (css-tx (d/db conn) css-src-2)) 212 | 213 | (-> (css-tx (d/db conn) 214 | css-src-2) 215 | pprint) 216 | 217 | 218 | ) -------------------------------------------------------------------------------- /src/clj/cljsfiddle/db/util.clj: -------------------------------------------------------------------------------- 1 | (ns cljsfiddle.db.util 2 | (:require [clojure.string :as s] 3 | [clojure.java.io :as io] 4 | [clojure.tools.reader :as reader] 5 | [datomic.api :as d] 6 | [cljs.env :as cljs-env] 7 | [cljs.closure :as closure] 8 | [cljs.js-deps :as cljs-deps] 9 | [cljs.tagged-literals :as tags] 10 | [environ.core :refer (env)]) 11 | (:import [clojure.lang LineNumberingPushbackReader] 12 | [java.io StringReader BufferedReader] 13 | [org.apache.commons.codec.digest DigestUtils])) 14 | 15 | (def tempid? map?) 16 | 17 | (defn- read-all* [^LineNumberingPushbackReader reader result eof] 18 | (let [form (reader/read reader false eof)] 19 | (if (= form eof) 20 | result 21 | (recur reader (conj result form) eof)))) 22 | 23 | (defn read-all [src] 24 | (binding [reader/*read-eval* false 25 | reader/*data-readers* tags/*cljs-data-readers*] 26 | (read-all* (LineNumberingPushbackReader. (StringReader. src)) 27 | [] 28 | (Object.)))) 29 | 30 | (defn sha [s] 31 | (DigestUtils/shaHex s)) 32 | 33 | (defn parse-js-ns [js-src] 34 | (-> js-src 35 | StringReader. 36 | BufferedReader. 37 | line-seq 38 | cljs-deps/parse-js-ns)) 39 | 40 | (defn cljs-object-from-src [cljs-src-str] 41 | (let [cljs-src (read-all cljs-src-str) 42 | js-src (cljs-env/with-compiler-env 43 | (cljs-env/default-compiler-env) 44 | (closure/-compile cljs-src {})) ;; TODO perf. 45 | {:keys [provides requires]} (parse-js-ns js-src)] 46 | {:src cljs-src-str 47 | :js-src js-src 48 | :sha (sha cljs-src-str) 49 | :ns (first provides) 50 | :requires (set requires)})) 51 | 52 | (defn cljs-object-from-file [cljs-file] 53 | (let [cljs-src-str (slurp (io/resource cljs-file)) 54 | cljs-object (cljs-object-from-src cljs-src-str)] 55 | (assoc cljs-object 56 | :file cljs-file))) 57 | 58 | (defn js-object-from-file [js-file] 59 | (let [js-src-str (slurp (io/resource js-file)) 60 | {:keys [provides requires]} (parse-js-ns js-src-str)] 61 | {:file js-file 62 | :src js-src-str 63 | :sha (sha js-src-str) 64 | :provides provides 65 | :requires requires})) 66 | 67 | (defn css-object-from-src [css-src] 68 | {:src css-src 69 | :sha (sha css-src)}) 70 | 71 | (defn html-object-from-src [html-src] 72 | {:src html-src 73 | :sha (sha html-src)}) 74 | 75 | (defn fiddle [cljs html css] 76 | {:cljs (cljs-object-from-src cljs) 77 | :html (html-object-from-src html) 78 | :css (css-object-from-src css)}) 79 | -------------------------------------------------------------------------------- /src/clj/cljsfiddle/handler.clj: -------------------------------------------------------------------------------- 1 | (ns cljsfiddle.handler 2 | (:require [clojure.edn :as edn] 3 | [clojure.java.io :as io] 4 | [clojure.string :as s] 5 | [clojure.instant :as inst] 6 | [clojure.core.match :refer (match)] 7 | [cljsfiddle.views :as views] 8 | [cljsfiddle.closure :as closure] 9 | [cljsfiddle.db :as db] 10 | [cljsfiddle.db.util :as util] 11 | [datomic.api :as d] 12 | [ring.adapter.jetty :as jetty] 13 | [ring.util.response :as res] 14 | [ring.middleware.session.cookie :as cookie] 15 | [ring.middleware.stacktrace :refer (wrap-stacktrace)] 16 | [ring.middleware.edn :refer (wrap-edn-params)] 17 | [compojure.core :refer :all] 18 | [compojure.handler :as handler] 19 | [compojure.route :as route] 20 | [environ.core :refer (env)] 21 | [clj-http.client :as http] 22 | [cheshire.core :as json] 23 | [hiccup.page :refer [html5]] 24 | [taoensso.timbre :as log])) 25 | 26 | (assert (env :datomic-uri) "DATOMIC_URI environment variable not set") 27 | (assert (env :session-secret) "SESSION_SECRET environment variable not set") 28 | (assert (env :cljsfiddle-version) "CLJSFIDDLE_VERSION environment variable not set") 29 | (assert (env :github-client-id) "GITHUB_CLIENT_ID environment variable not set") 30 | (assert (env :github-client-secret) "GITHUB_CLIENT_SECRET environment variable not set") 31 | 32 | (defn ensure-jscache-dir [] 33 | (let [p (str "resources/jscache/" (:cljsfiddle-version env) "/") 34 | f (java.io.File. p)] 35 | (when (.mkdirs f) 36 | (println "Created" p)))) 37 | 38 | (defn edn-response [edn-data] 39 | {:status 200 40 | :headers {"Content-Type" "application/edn"} 41 | :body (pr-str edn-data)}) 42 | 43 | ;; From technomancy/syme 44 | (defn get-token [code] 45 | (-> (http/post "https://github.com/login/oauth/access_token" 46 | {:form-params {:client_id (env :github-client-id) 47 | :client_secret (env :github-client-secret) 48 | :code code} 49 | :headers {"Accept" "application/json"}}) 50 | (:body) 51 | (json/decode true) 52 | :access_token)) 53 | 54 | (defn get-username [token] 55 | (-> (http/get (str "https://api.github.com/user?access_token=" token) 56 | {:headers {"accept" "application/json"}}) 57 | (:body) 58 | (json/decode true) 59 | :login)) 60 | 61 | (defn parse-ns-form [src] 62 | (try 63 | (let [form (edn/read-string src)] 64 | (if (and (seq? form) 65 | (= 'ns (first form)) 66 | (symbol? (second form))) 67 | [:ok (name (second form))] 68 | [:fail (str "Not a ns form: " (pr-str form))])) 69 | (catch Exception e 70 | [:exception (.getMessage e)]))) 71 | 72 | (defn as-of* 73 | "Get the db as of date or latest db if date is nil" 74 | [conn date] 75 | (let [db (d/db conn)] 76 | (if date 77 | (d/as-of db date) 78 | db))) 79 | 80 | (defn app-routes [conn] 81 | (routes 82 | (GET "/" 83 | {{:keys [username] :as session} :session} 84 | (assoc {:headers {"Content-Type" "text/html"} 85 | :status 200 86 | :body (html5 (views/main-view (d/entity (d/db conn) 87 | :cljsfiddle/default-fiddle) 88 | username))} 89 | :session (dissoc session :fiddle))) 90 | 91 | (GET "/fiddle/:ns" 92 | {{:keys [username] :as session} :session 93 | {:keys [ns as-of]} :params} 94 | (let [date (try (inst/read-instant-date as-of) 95 | (catch Exception e)) 96 | db (as-of* conn date)] 97 | (when-let [fiddle (db/find-fiddle-by-ns db ns)] 98 | (assoc {:headers {"Content-Type" "text/html"} 99 | :status 200 100 | :body (html5 (views/main-view fiddle username))} 101 | :session (merge session 102 | {:fiddle ns}))))) 103 | 104 | (GET "/view/:ns" 105 | {{:keys [ns as-of]} :params} 106 | (let [date (try (inst/read-instant-date as-of) 107 | (catch Exception e)) 108 | db (as-of* conn date)] 109 | (when-let [fiddle (db/find-fiddle-by-ns db ns)] 110 | (let [deps (db/dependency-files db ns)] 111 | {:headers {"Content-Type" "text/html"} 112 | :status 200 113 | :body (html5 (views/html-view ns fiddle deps))})))) 114 | 115 | (GET "/about" 116 | {{:keys [username]} :session} 117 | {:headers {"Content-Type" "text/html"} 118 | :status 200 119 | :body (html5 (views/about-view username))}) 120 | 121 | (GET "/user/:user" 122 | {{:keys [user]} :params 123 | {:keys [username]} :session} 124 | (when true #_(= username user) 125 | (let [fiddles (db/fiddles-by-user (d/db conn) user)] 126 | {:headers {"Content-Type" "text/html"} 127 | :status 200 128 | :body (html5 (views/user-view username user fiddles))}))) 129 | 130 | (POST "/save" 131 | {fiddle :params 132 | {:keys [username]} :session} 133 | (edn-response 134 | (match [username (parse-ns-form (:cljs fiddle))] 135 | [nil _] {:status :save-fail :msg "Login to save your work."} 136 | [_ [:fail msg]] {:status :save-fail :msg msg} 137 | [_ [:exception msg]] {:status :exception :msg msg} 138 | [_ [:ok ns]] (if (or (= username (first (s/split ns #"\."))) 139 | (and (= ns "cljsfiddle") 140 | (= username "jonase"))) 141 | (try 142 | (let [db (db/save-fiddle conn 143 | (util/fiddle (:cljs fiddle) 144 | (:html fiddle) 145 | (:css fiddle)))] 146 | {:status :save-success 147 | :date (subs (->> db d/basis-t d/t->tx (d/entity db) :db/txInstant pr-str) 148 | 7 36) 149 | :ns ns}) 150 | (catch Exception e 151 | {:status :exception 152 | :msg (.getMessage e)})) 153 | {:status :save-fail 154 | :ns ns 155 | :user username})))) 156 | 157 | ;; TODO: this can be done with nginx try_files 158 | (GET "/jscache/:version/:file" 159 | [version file] 160 | (let [fp (str "/jscache/" version "/" file) 161 | fr (res/file-response fp {:root "resources"})] 162 | (if fr 163 | (-> fr 164 | (res/header "Cache-Control" (str "max-age=" (* 60 60 24 365))) 165 | (res/header "Content-Type" "application/javascript")) 166 | (do (log/info "Cache miss:" fp) 167 | (res/redirect (str "/deps/" version "/" file)))))) 168 | 169 | (GET "/oauth_login" 170 | {{:keys [code]} :params session :session} 171 | (if code 172 | (let [token (get-token code) 173 | username (get-username token)] 174 | (assoc (res/redirect (if-let [fiddle (:fiddle session)] 175 | (str "/fiddle/" fiddle) 176 | "/")) 177 | :session (merge session {:token token 178 | :username username}))))) 179 | 180 | (GET "/logout" 181 | [] 182 | (assoc (res/redirect "/") :session nil)) 183 | 184 | (context "/compiler" [] (closure/compile-routes conn)) 185 | (context "/deps" [] (closure/deps-routes conn)) 186 | 187 | (route/resources "/") 188 | (route/not-found "Not Found"))) 189 | 190 | (defn get-handler [] 191 | (let [store (cookie/cookie-store {:key (env :session-secret)}) 192 | db-uri (env :datomic-uri) 193 | conn (d/connect db-uri)] 194 | (-> (handler/site (app-routes conn) 195 | {:session {:store store}}) 196 | wrap-edn-params))) 197 | 198 | (def app (get-handler)) 199 | 200 | (defn -main [] 201 | (ensure-jscache-dir) 202 | (let [port (Integer/parseInt (or (env "PORT") "8080"))] 203 | (jetty/run-jetty (-> app 204 | wrap-stacktrace) 205 | {:port port :join? false}))) 206 | 207 | ;; (.stop server) 208 | ;; (def server (-main)) 209 | 210 | 211 | -------------------------------------------------------------------------------- /src/clj/cljsfiddle/import.clj: -------------------------------------------------------------------------------- 1 | (ns cljsfiddle.import 2 | (:require [clojure.string :as s] 3 | [clojure.java.io :as io] 4 | [datomic.api :as d] 5 | [cljs.closure :as cljs] 6 | [cljs.js-deps :as cljs-deps] 7 | [environ.core :refer (env)] 8 | [cljsfiddle.db.src :as src] 9 | [cljsfiddle.db.schema :refer (schema)] 10 | [cljsfiddle.db.util :as util :refer (sha 11 | cljs-object-from-file 12 | js-object-from-file)] 13 | [cljsfiddle.db.fiddle :as fiddle]) 14 | (:import [clojure.lang LineNumberingPushbackReader] 15 | [java.util Date] 16 | [java.io StringReader BufferedReader] 17 | [org.apache.commons.codec.digest DigestUtils])) 18 | 19 | 20 | (defn create-db [& [uri]] 21 | (let [uri (or uri 22 | (env :datomic-uri) 23 | "datomic:free://localhost:4334/cljsfiddle")] 24 | (d/create-database uri) 25 | (let [conn (d/connect uri)] 26 | @(d/transact conn schema)))) 27 | 28 | ;; Import js and cljs from the classpath into datomic. 29 | 30 | (defn find-files [paths jars] 31 | (filter (fn [file] 32 | (and (some #(.startsWith file %) paths) 33 | (some #(.endsWith file %) [".js" ".cljs"]))) 34 | (mapcat cljs-deps/jar-entry-names* jars))) 35 | 36 | 37 | (defn goog-base-tx [db] 38 | (let [goog-base (d/entity db :goog/base) 39 | old-sha (-> goog-base 40 | :cljsfiddle.src/blob 41 | :cljsfiddle.blob/sha) 42 | new-sha (sha (slurp (io/resource "goog/base.js"))) 43 | blob-eid (ffirst (d/q '[:find ?e 44 | :in $ ?sha 45 | :where 46 | [?e :cljsfiddle.blob/sha ?sha]] 47 | db new-sha))] 48 | (assert blob-eid "No goog.base in db") 49 | (let [src-eid (d/tempid :db.part/user)] 50 | (cond 51 | (nil? old-sha) 52 | [[:db/add src-eid :cljsfiddle.src/type :cljsfiddle.src.type/js] 53 | [:db/add src-eid :cljsfiddle.src/blob blob-eid] 54 | [:db/add src-eid :db/ident :goog/base]] 55 | 56 | (not= old-sha new-sha) 57 | [[:db/add src-eid :cljsfiddle.src/blob blob-eid]] 58 | 59 | :else 60 | [])))) 61 | 62 | (defn default-fiddle-tx [db] 63 | (let [fiddle (util/fiddle "(ns cljsfiddle)\n\n(set! (.-innerHTML (.getElementById js/document \"msg\"))\n \"CLJSFiddle\")\n" 64 | "

Hello,

\n" 65 | "p {\n color: #f41;\n font-family: helvetica;\n font-size: 2em;\n text-align: center\n}") 66 | {:keys [tx id]} (fiddle/fiddle-tx db fiddle)] 67 | (println tx id) 68 | (if (not (empty? tx)) 69 | (cons [:db/add id :db/ident :cljsfiddle/default-fiddle] 70 | tx) 71 | tx))) 72 | 73 | ;; Import cljs and js deps. Idempotent 74 | ;; TODO: Figure out if schema is installed. 75 | (defn -main [uri] 76 | (let [conn (d/connect uri) 77 | files (find-files #{"cljs/" "clojure/" "goog/" "domina" "hiccups" 78 | "dommy" "om" "quiescent" "reagent"} 79 | (filter #(.endsWith % ".jar") 80 | (-> "java.class.path" 81 | System/getProperty 82 | (s/split #":")))) 83 | js-files (filter #(.endsWith % ".js") files) 84 | js-objects (map js-object-from-file js-files) 85 | cljs-files (filter #(.endsWith % ".cljs") files) 86 | cljs-objects (map cljs-object-from-file cljs-files)] 87 | (println "transacting cljs") 88 | (doseq [cljs cljs-objects] 89 | (let [cljs-tx (:tx (src/cljs-tx (d/db conn) cljs))] 90 | (print "Considering " (:file cljs) "... ") 91 | (if-not (empty? cljs-tx) 92 | (do @(d/transact conn cljs-tx) 93 | (println "transacted.")) 94 | (println "skipped.") 95 | ))) 96 | (println "done.") 97 | (println "transacting js") 98 | (doseq [js js-objects] 99 | (let [js-tx (src/js-tx (d/db conn) js)] 100 | (print "Considering " (:file js) "... ") 101 | (if-not (empty? js-tx) 102 | (do @(d/transact conn js-tx) 103 | (println "transacted.")) 104 | (println "skipped.")))) 105 | (println "done.") 106 | (println "Special casing goog/base.js") 107 | (let [tx (goog-base-tx (d/db conn))] 108 | (when-not (empty? tx) 109 | (println tx) 110 | @(d/transact conn tx))) 111 | (println "Adding default fiddle") 112 | (let [tx (default-fiddle-tx (d/db conn))] 113 | (when-not (empty? tx) 114 | (println tx) 115 | @(d/transact conn tx))) 116 | 117 | (println "Running storage GC") 118 | (d/gc-storage conn (Date.)) 119 | (println "Done."))) 120 | 121 | ;; (-main (env :datomic-uri)) 122 | 123 | (comment 124 | 125 | (use 'clojure.pprint) 126 | (def uri (env :datomic-uri)) 127 | 128 | (do (d/delete-database uri) 129 | (d/create-database uri)) 130 | 131 | 132 | 133 | (def conn (d/connect uri)) 134 | 135 | @(d/transact conn schema) 136 | 137 | (d/q '[:find ?e :where 138 | [?e :cljsfiddle.src/ns "goog.string"]] 139 | (d/db conn)) 140 | 141 | (d/entity (d/db conn) :goog/base) 142 | 143 | (d/q '[:find ?sha 144 | :in $ ?sha 145 | :where 146 | [?e :cljsfiddle.blob/sha ?sha]] 147 | (d/db conn) 148 | ) 149 | 150 | (d/touch (d/entity (d/db conn) :db.type/ref)) 151 | 152 | 153 | (src/cljs-tx (d/db conn) (cljs-object "domina.cljs")) 154 | 155 | (:cljsfiddle.src/ns (d/entity (d/db conn) 17592186045435)) 156 | 157 | (pprint (d/touch (:cljsfiddle.src/blob (d/entity (d/db conn) :goog/base)))) 158 | 159 | (d/touch (d/entity (d/db conn) :cljsfiddle/default-fiddle)) 160 | ) 161 | -------------------------------------------------------------------------------- /src/clj/cljsfiddle/views.clj: -------------------------------------------------------------------------------- 1 | (ns cljsfiddle.views 2 | (:require [hiccup.util :refer (escape-html)] 3 | [environ.core :refer (env)] 4 | [cljsfiddle.closure :refer [compile-cljs*]])) 5 | 6 | (def google-analytics-script 7 | "(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 8 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 9 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 10 | })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); 11 | 12 | ga('create', 'UA-9233187-2', 'cljsfiddle.net'); 13 | ga('send', 'pageview');") 14 | 15 | (defn base [nav & content] 16 | [:html {:lang "en"} 17 | [:head 18 | [:title "CLJSFiddle"] 19 | [:meta {:name "viewport" 20 | :content "width=device-width, initial-scale=1.0"}] 21 | [:link {:rel "stylesheet" 22 | :href "//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css"}] 23 | [:link {:rel "stylesheet" 24 | :href "/css/codemirror.css"}] 25 | [:link {:rel "stylesheet" 26 | :href "/css/style.css"}] 27 | [:script google-analytics-script]] 28 | [:body 29 | nav 30 | [:div.full-width-container content] 31 | [:script {:src "//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"}] 32 | [:script {:src "//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"}] 33 | [:script {:src "/js/codemirror.js"}] 34 | [:script {:src "/js/mode/clojure/clojure.js"}] 35 | [:script {:src "/js/mode/css/css.js"}] 36 | [:script {:src "/js/addon/edit/matchbrackets.js"}] 37 | [:script {:src "/js/addon/edit/closebrackets.js"}] 38 | [:script {:src "/js/app.js"}] 39 | [:script "cljsfiddle.core.init('" (env :cljsfiddle-version) "');"]]]) 40 | 41 | (def ^:private github-login-url (str "https://github.com/login/oauth/authorize?client_id=" (env :github-client-id))) 42 | 43 | 44 | (defn navbar [user & buttons] 45 | [:nav.navbar.navbar-default.navbar-static-top {:role "navigation"} 46 | [:div.navbar-header 47 | [:a.navbar-brand {:href "/"} "CLJSFiddle"]] 48 | [:ul.nav.navbar-nav 49 | buttons 50 | (when user [:li [:a {:href (str "/user/" user)} "My namespaces"]]) 51 | [:li [:a {:href "/about"} "About"]]] 52 | [:ul.nav.navbar-nav.navbar-right 53 | [:li (if user 54 | [:a {:href "/logout"} "Logout (" user ")"] 55 | [:a {:href github-login-url} "Login"])]]]) 56 | 57 | (defn main-view 58 | [fiddle user] 59 | (base (navbar user) 60 | [:div.row 61 | [:div.col-lg-12 62 | [:div#alert]]] 63 | [:div.row 64 | [:div#tab-container.col-lg-6 65 | 66 | [:ul#editor-tabs.nav.nav-tabs 67 | [:li.active [:a {:href "#cljs-editor-tab" :data-toggle "tab"} "cljs"]] 68 | [:li [:a {:href "#html-editor-tab" :data-toggle "tab"} "html"]] 69 | [:li [:a {:href "#css-editor-tab" :data-toggle "tab"} "css"]] 70 | [:span#cljsfiddle-buttons 71 | [:button#run-btn.btn.btn-default.btn-xs {:data-toggle "tooltip" 72 | :title "Compile & Run"} 73 | [:span.glyphicon.glyphicon-play]] 74 | [:button#save-btn.btn.btn-default.btn-xs {:data-toggle "tooltip" 75 | :title "Save"} 76 | [:span.glyphicon.glyphicon-floppy-save]]]] 77 | 78 | [:div.tab-content 79 | [:div#cljs-editor-tab.tab-pane.active 80 | [:textarea#cljs-editor.tab-pane.active (escape-html (-> fiddle 81 | :cljsfiddle/cljs 82 | :cljsfiddle.src/blob 83 | :cljsfiddle.blob/text))]] 84 | [:div#html-editor-tab.tab-pane 85 | [:textarea#html-editor.tab-pane (escape-html (-> fiddle 86 | :cljsfiddle/html 87 | :cljsfiddle.src/blob 88 | :cljsfiddle.blob/text))]] 89 | [:div#css-editor-tab.tab-pane 90 | [:textarea#css-editor.tab-pane (escape-html (-> fiddle 91 | :cljsfiddle/css 92 | :cljsfiddle.src/blob 93 | :cljsfiddle.blob/text))]]] 94 | 95 | [:div#outut-container {:style "position: relative;"} 96 | [:div#resize-handle {:style "height:3px; background: #ccc; cursor: ns-resize;"}] 97 | [:div#output {:style "height:100px;width:100%;border:1px solid lightgray;overflow:auto;padding-left:6px;padding-top:6px"}] 98 | [:button#clear-output-btn.btn.btn-default.btn-xs {:style "position: absolute; right: 5px; bottom: 5px;" 99 | :title "clear output" 100 | :data-toggle "tooltip"} "clear"]]] 101 | 102 | [:div.col-lg-6 103 | [:div.row 104 | [:div.col-lg-12 105 | [:iframe#result-frame {:seamless "seamless" 106 | :sandbox "allow-scripts" 107 | :width "100%" 108 | :style "border: 1px solid lightgray;height:532px;"}]]]]] 109 | 110 | [:div.row {:style "margin-top:20px;"} 111 | [:div.col-lg-12 112 | [:p.text-center {:style "margin-bottom: 10px;"} 113 | [:a {:href "http://cljsfiddle.net"} "cljsfiddle.net"] " © 2013 Jonas Enlund"]]])) 114 | 115 | (defn html-view [ns fiddle deps] 116 | [:html 117 | [:head 118 | [:title ns] 119 | [:style (-> fiddle 120 | :cljsfiddle/css 121 | :cljsfiddle.src/blob 122 | :cljsfiddle.blob/text)] 123 | [:script google-analytics-script]] 124 | [:body 125 | (-> fiddle 126 | :cljsfiddle/html 127 | :cljsfiddle.src/blob 128 | :cljsfiddle.blob/text) 129 | [:script "CLOSURE_NO_DEPS=true;"] 130 | [:script "COMPILED=true;"] 131 | (for [dep deps] 132 | [:script {:src (str "/jscache/" (env :cljsfiddle-version) "/" dep)}]) 133 | [:script 134 | (-> fiddle 135 | :cljsfiddle/cljs 136 | :cljsfiddle.src/blob 137 | :cljsfiddle.blob/text 138 | compile-cljs*)]]]) 139 | 140 | (defn about-view [user] 141 | (base (navbar user) 142 | [:div.row 143 | [:div.col-lg-12 144 | [:h3 "About CLJSFiddle"] 145 | [:ul 146 | [:li "CLJSFiddle is open source and available on " [:a {:href "http://github.com/jonase/cljsfiddle"} "github."]] 147 | [:li "Feel free to open bug reports and feature requests! Pull requests are also appreciated!"] 148 | [:li [:strong "Help needed"] " especially around user interface design."]] 149 | 150 | [:h3 "How does it work?"] 151 | [:ul 152 | [:li "In order to save your work you" [:strong " must login "] "via your github account."] 153 | [:li "Prefix your namespace with your username: " [:pre "(ns username.test)"]] 154 | [:li "Saved fiddles can be accessed either by" 155 | [:ul 156 | [:li "Fiddle view: " [:span {:style "font-family:monospace"} "http://cljsfiddle.net/fiddle/name.space"]] 157 | [:li "Html view: " [:span {:style "font-family:monospace"} "http://cljsfiddle.net/view/name.space"]] 158 | [:li "Append the url with " [:span {:style "font-family:monospace"} "?as-of=<some-date>"] 159 | " for older versions and permalinks."] 160 | [:li "The date format is the same as clojure instant literals (without the #inst part): 2013-09-29 or 2013-10-02T13:15:01 "]]]]]])) 161 | 162 | (defn user-view [logged-in-user user fiddles] 163 | (base (navbar logged-in-user) 164 | [:div.row 165 | [:div.col-lg-12 166 | [:h3 "User: " user] 167 | [:ul 168 | (for [[ns date] (reverse (sort-by second fiddles))] 169 | [:li (subs (pr-str date) 7 26) " " [:a {:href (str "/fiddle/" ns)} ns] 170 | " | " [:a {:href (str "/view/" ns)} "HTML view"]])]]])) 171 | -------------------------------------------------------------------------------- /src/cljs/cljsfiddle/core.cljs: -------------------------------------------------------------------------------- 1 | (ns cljsfiddle.core 2 | (:require [clojure.string :as s] 3 | [cljs.reader :as reader] 4 | [domina :as dom] 5 | [domina.css :as css] 6 | [domina.events :as events] 7 | [hylla.remote :as remote] 8 | [ajax.core :as http] 9 | [hiccups.runtime :refer (render-html)])) 10 | 11 | (defn ends-with? [string suffix] 12 | (not= -1 (.indexOf string suffix (- (.-length string) (.-length suffix))))) 13 | 14 | (defn code-mirror [id opts] 15 | (.fromTextArea js/CodeMirror (dom/by-id id) (clj->js opts))) 16 | 17 | (defn make-deps [deps version] 18 | (let [html [[:script "CLOSURE_NO_DEPS=true;"] 19 | [:script "COMPILED=true;"]] 20 | ds (for [dep deps] 21 | [:script {:src (str "/jscache/" version "/" (s/replace dep ".cljs" ".js"))}])] 22 | (apply str (map render-html (concat html ds))))) 23 | 24 | (defn make-srcdoc [html css js deps version] 25 | (render-html 26 | [:html 27 | [:head 28 | [:style css]] 29 | [:body 30 | [:script "window.onerror = function(msg, url, line) { parent.postMessage('{:type :runtime-error}', '*'); return false;};"] 31 | html 32 | (make-deps deps version) 33 | [:script "cljs.core.set_print_fn_BANG_.call(null,function(s){var s = s.replace(/\"/g, \""\"); parent.postMessage('{:type :runtime-print :to-print \"' + s + '\"}', '*');});"] 34 | [:script js] 35 | ]])) 36 | 37 | (def saved? (atom false)) 38 | 39 | (defn toggle-saved! [] 40 | (let [btn (css/sel "#save-btn") 41 | span (css/sel "#save-btn span")] 42 | (dom/set-style! btn :background-color "lightgreen") 43 | (dom/remove-class! span "glyphicon-floppy-save") 44 | (dom/add-class! span "glyphicon-floppy-saved"))) 45 | 46 | (defn toggle-not-saved! [] 47 | (let [btn (css/sel "#save-btn") 48 | span (css/sel "#save-btn span")] 49 | (dom/set-style! btn :background-color "white") 50 | (dom/remove-class! span "glyphicon-floppy-saved") 51 | (dom/add-class! span "glyphicon-floppy-save"))) 52 | 53 | (defn editor-content-changed [e] 54 | (when @saved? 55 | (toggle-not-saved!) 56 | (swap! saved? not))) 57 | 58 | (defn register-change-listeners [& editors] 59 | (doseq [editor editors] 60 | (.on editor "change" editor-content-changed))) 61 | 62 | (defmulti output-hiccup #(or (:type %) (:status %))) 63 | 64 | (defmethod output-hiccup :error [msg] 65 | [:div 66 | [:span.glyphicon.glyphicon-warning-sign {:style "color:red"}] " " [:strong (:msg msg)]]) 67 | 68 | (defmethod output-hiccup :log [msg] 69 | [:div 70 | [:span.glyphicon.glyphicon-chevron-right] " " (:msg msg)]) 71 | 72 | (defmethod output-hiccup :exception [msg] 73 | (output-hiccup (assoc msg :type :error))) 74 | 75 | (defmethod output-hiccup :save-fail [msg] 76 | (if-let [msg (:msg msg)] 77 | [:div 78 | [:span.glyphicon.glyphicon-warning-sign {:style "color:red"}] " " 79 | [:strong msg]] 80 | [:div 81 | [:span.glyphicon.glyphicon-warning-sign {:style "color:red"}] 82 | " Can't save fiddle with namespace " [:strong (:ns msg)] 83 | ". Prefix the namespace with your username such as " [:strong (:user msg) "." (:ns msg)] "."])) 84 | 85 | (defmethod output-hiccup :save-success [data] 86 | [:div 87 | [:span.glyphicon.glyphicon-floppy-saved {:style "color: green;"}] 88 | " Fiddle " [:strong (:ns data)] " saved successfully!"]) 89 | 90 | (defmethod output-hiccup :runtime-error [data] 91 | [:div 92 | [:span.glyphicon.glyphicon-warning-sign {:style "color: red;"}] 93 | [:strong " Runtime exception occured. Check your console logs for details."]]) 94 | 95 | (defmethod output-hiccup :runtime-print [data] 96 | [:div 97 | [:span.glyphicon.glyphicon-chevron-right] 98 | " " (s/escape (:to-print data) {"<" "<" 99 | ">" ">"})]) 100 | 101 | (defn output-html [msg] 102 | (render-html (output-hiccup msg))) 103 | 104 | (defn output-fn [] 105 | (let [out (dom/by-id "output")] 106 | (fn [msg] 107 | (let [html (output-html msg)] 108 | (dom/append! out html) 109 | ;; Scroll to bottom 110 | (set! (.-scrollTop out) (.-scrollHeight out)))))) 111 | 112 | 113 | (defn ^:export init 114 | [version] 115 | 116 | (let [html-editor (code-mirror "html-editor" {:lineNumbers true}) 117 | css-editor (code-mirror "css-editor" {:mode :css :lineNumbers true}) 118 | cljs-editor (code-mirror "cljs-editor" {:mode :clojure 119 | :lineNumbers true 120 | :autoCloseBrackets true 121 | :matchBrackets true}) 122 | result-frame (domina/by-id "result-frame") 123 | run-btn (domina/by-id "run-btn") 124 | save-btn (domina/by-id "save-btn") 125 | clear-output-btn (domina/by-id "clear-output-btn") 126 | resize-handle (domina/by-id "resize-handle") 127 | tab-container (domina/by-id "tab-container") 128 | output (output-fn) 129 | set-editor-heights (fn [height] 130 | (doseq [editor [cljs-editor html-editor css-editor]] 131 | (.setSize editor "100%" (str height "px"))))] 132 | 133 | (set-editor-heights 400) 134 | 135 | (events/listen! resize-handle :mousedown 136 | (fn [e] 137 | (let [start-y (.-clientY (.-evt e)) 138 | cur-height (js/parseInt (.-height (.-style (.getWrapperElement cljs-editor)))) 139 | move-handler (fn [e] 140 | (let [cur-y (.-clientY (.-evt e)) 141 | dy (- cur-y start-y)] 142 | (set-editor-heights (+ cur-height dy))))] 143 | (events/listen! tab-container :mousemove move-handler) 144 | (events/listen! tab-container :mouseup 145 | (fn [e] 146 | (move-handler e) 147 | (events/unlisten! tab-container :mousemove) 148 | (events/unlisten! tab-container :mouseup)))))) 149 | 150 | (events/listen! clear-output-btn :click 151 | (fn [e] 152 | (dom/set-html! (domina/by-id "output") ""))) 153 | 154 | (events/listen! run-btn :click 155 | (fn [e] 156 | (dom/add-class! run-btn "disabled") 157 | (http/POST "/compiler/compile" 158 | {:params {:src (.getValue cljs-editor)} 159 | :handler (fn [res] 160 | (dom/remove-class! run-btn "disabled") 161 | (condp = (:status res) 162 | :ok 163 | (let [srcdoc (make-srcdoc (.getValue html-editor) 164 | (.getValue css-editor) 165 | (:js-src res) 166 | (:dependencies res) 167 | version)] 168 | (.setAttribute result-frame "srcdoc" srcdoc)) 169 | :exception (output res)))}))) 170 | (events/listen! save-btn :click 171 | (fn [e] 172 | (dom/add-class! save-btn "disabled") 173 | (http/POST "/save" 174 | {:params {:cljs (.getValue cljs-editor) 175 | :html (.getValue html-editor) 176 | :css (.getValue css-editor)} 177 | :handler (fn [res] 178 | (dom/remove-class! save-btn "disabled") 179 | (if (= (:status res) :success) 180 | (do (reset! saved? true) 181 | (toggle-saved!) 182 | (output (assoc res :msg "Fiddle saved successfully!"))) 183 | (output res)))}))) 184 | 185 | (.on (js/$ "a[data-toggle=\"tab\"]") 186 | "shown.bs.tab" 187 | (fn [evt] 188 | (condp = (dom/attr (.-target evt) :href) 189 | "#cljs-editor-tab" (.refresh cljs-editor) 190 | "#html-editor-tab" (.refresh html-editor) 191 | "#css-editor-tab" (.refresh css-editor)))) 192 | 193 | (register-change-listeners cljs-editor html-editor css-editor) 194 | 195 | (.addEventListener js/window "message" 196 | (fn [evt] 197 | (.log js/console evt) 198 | (output (js->clj (reader/read-string (.-data evt)))))))) 199 | -------------------------------------------------------------------------------- /test/cljsfiddle/test/handler.clj: -------------------------------------------------------------------------------- 1 | (ns cljsfiddle.test.handler 2 | (:use clojure.test 3 | ring.mock.request 4 | cljsfiddle.handler)) 5 | 6 | (deftest test-app 7 | (testing "main route" 8 | (let [response (app (request :get "/"))] 9 | (is (= (:status response) 200)) 10 | (is (= (:body response) "Hello World")))) 11 | 12 | (testing "not-found route" 13 | (let [response (app (request :get "/invalid"))] 14 | (is (= (:status response) 404))))) --------------------------------------------------------------------------------