├── .gitignore ├── Gruntfile.js ├── LICENSE ├── README.md ├── build.gradle ├── docs ├── dials.png ├── java8react.png ├── sidebar.png ├── tables.png └── thumbnail.png ├── package.json ├── pom.xml └── src ├── main ├── java │ └── me │ │ └── roybailey │ │ ├── http │ │ ├── DemoService.java │ │ ├── JsonTransformer.java │ │ ├── JsonUtil.java │ │ ├── ReactController.java │ │ ├── RestResponse.java │ │ ├── RestfulController.java │ │ └── RestfulService.java │ │ ├── model │ │ ├── Task.java │ │ └── TaskMeta.java │ │ └── service │ │ ├── DemoTaskService.java │ │ └── TaskService.java ├── js │ ├── app.js │ ├── dashboard.js │ └── nashorn-polyfill.js ├── jsx │ ├── app.jsx │ └── dashboard.jsx └── resources │ ├── demodata │ └── DemoTaskService.json │ ├── log4j.properties │ └── webapp │ ├── admin.html │ ├── assets │ ├── DT_bootstrap.css │ ├── DT_bootstrap.js │ ├── dashboard.html │ ├── form-validation.js │ ├── scripts.js │ └── styles.css │ ├── bootstrap │ ├── css │ │ ├── bootstrap-responsive.css │ │ ├── bootstrap-responsive.min.css │ │ ├── bootstrap.css │ │ └── bootstrap.min.css │ ├── img │ │ ├── glyphicons-halflings-white.png │ │ └── glyphicons-halflings.png │ └── js │ │ ├── bootstrap.js │ │ └── bootstrap.min.js │ ├── bundle.min.js │ ├── easypiechart │ ├── examples │ │ ├── excanvas.js │ │ ├── index.html │ │ └── style.css │ ├── jquery.easy-pie-chart.css │ └── jquery.easy-pie-chart.js │ ├── index.html │ ├── jquery │ ├── jquery-1.9.1.js │ └── jquery-1.9.1.min.js │ └── react │ ├── JSXTransformer.js │ ├── react-with-addons.js │ ├── react-with-addons.min.js │ ├── react.js │ └── react.min.js └── test └── java └── me └── roybailey └── http ├── RestResponseTest.java └── RestfulControllerTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | 14 | .idea 15 | .gradle 16 | .module-cache 17 | build/ 18 | target/ 19 | node_modules/ 20 | 21 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | grunt.loadNpmTasks('grunt-curl'); 3 | grunt.loadNpmTasks('grunt-contrib-less'); 4 | grunt.loadNpmTasks('grunt-contrib-watch'); 5 | grunt.loadNpmTasks('grunt-contrib-jshint'); 6 | grunt.loadNpmTasks('grunt-contrib-uglify'); 7 | grunt.loadNpmTasks('grunt-contrib-copy'); 8 | grunt.loadNpmTasks('grunt-react'); 9 | 10 | grunt.initConfig({ 11 | pkg: grunt.file.readJSON('package.json'), 12 | 13 | curl: { 14 | 'google-fonts-source': { 15 | src: 'http://fonts.googleapis.com/css?family=Lato:400,300,300italic,400italic,700,700italic', 16 | dest: 'src/main/resources/webapp/assets/google-fonts-lato.css' 17 | } 18 | }, 19 | 20 | less: { 21 | development: { 22 | options: { 23 | paths: ["public/css"] 24 | }, 25 | files: { 26 | "public/css/style.css": "public/css/less/style.less" 27 | } 28 | } 29 | }, 30 | 31 | watch: { 32 | jsx: { 33 | files: 'src/main/jsx/*.jsx', 34 | tasks: 'react' 35 | }, 36 | uglify: { 37 | files: 'src/main/js/*.js', 38 | tasks: 'uglify' 39 | } 40 | }, 41 | 42 | jshint: { 43 | files: [ 44 | '*.js' 45 | ], 46 | options: { 47 | expr: true, 48 | globals: { 49 | jQuery: true, 50 | console: true, 51 | module: true, 52 | document: true 53 | } 54 | } 55 | }, 56 | 57 | uglify: { 58 | options: { 59 | mangle: { 60 | except: ['jQuery', 'Backbone'] 61 | } 62 | }, 63 | bundle: { 64 | files: { 65 | 'src/main/resources/webapp/bundle.min.js': [ 66 | 'src/main/js/nashorn-polyfill.js', 67 | 'node_modules/react/dist/react.js', 68 | 'node_modules/moment/min/moment.min.js', 69 | 'src/main/js/app.js', 70 | 'src/main/js/dashboard.js' 71 | ] 72 | } 73 | } 74 | }, 75 | 76 | copy: { 77 | main: { 78 | files: [ 79 | // includes files within path 80 | { 81 | expand: true, 82 | cwd: 'node_modules/react/dist/', 83 | src: ['*'], 84 | dest: 'src/main/resources/webapp/react/', 85 | filter: 'isFile', 86 | flatten: true 87 | }, 88 | 89 | // includes files within path and its sub-directories 90 | //{ 91 | // expand: false, 92 | // src: ['node_modules/react/dist/**'], 93 | // dest: 'src/main/resources/webapp/react/' 94 | //}, 95 | 96 | // makes all src relative to cwd 97 | // {expand: true, cwd: 'path/', src: ['**'], dest: 'dest/'}, 98 | 99 | // flattens results to a single level 100 | // {expand: true, flatten: true, src: ['path/**'], dest: 'dest/', filter: 'isFile'}, 101 | ] 102 | } 103 | }, 104 | 105 | react: { 106 | mappings: { 107 | files: [ 108 | { 109 | expand: true, 110 | cwd: 'src/main/jsx', 111 | src: ['**/*.jsx'], 112 | dest: 'src/main/js', 113 | ext: '.js' 114 | } 115 | ] 116 | } 117 | } 118 | }); 119 | }; 120 | 121 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Roy Bailey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SparkJava Retrofit React Demonstration 2 | 3 | > ___A Java 8 web application using lightweight frameworks and [ReactJS](https://facebook.github.io/react/) for the UI___ 4 | 5 | 6 | 7 | ## Basic Idea 8 | 9 | A Java 8 process with an embedded rest-api and web application, where the web application user interface is constructed using the React JavaScript components via the Nashorn engine with access to Java services. 10 | 11 | 12 | ## Dependencies 13 | 14 | SparkJava and Guava keep the overall dependencies lightweight (not even a single spring library here). 15 | 16 | 17 | * **[SparkJava](http://sparkjava.com/)** - A minimalist Java 8 web application framework 18 | * **[Retrofit](http://square.github.io/retrofit/)** - A type-safe REST client for Java 19 | * **[ReactJS](http://facebook.github.io/react/)** - A Javascript library for building user interfaces 20 | * **[Bootstrap-Admin-Theme](https://github.com/VinceG/Bootstrap-Admin-Theme)** - A web dashboard courtesy of Vincent Gabriel 21 | * **[JMustache](https://github.com/samskivert/jmustache)** - A Java template engine 22 | 23 | Also using **[Quava](https://code.google.com/p/guava-libraries/)** and a choice of **[Gradle](http://gradle)** or **[Maven](http://maven.apache.org)** for Java builds. 24 | 25 | 26 | ## Taking it for a spin 27 | 28 | Make sure you have `JDK 1.8.0_05-b13` or above. 29 | 30 | command | description 31 | --------|------------ 32 | `gradle build` | to compile and run the tests 33 | `mvn clean install` | to compile and run the tests 34 | `grunt watch` | compile jsx into js resource (from `src/main/jsx` into `src/main/js`) 35 | [`DemoService`](src/main/java/me/roybailey/http/DemoService.java) | Start Web Application [http://localhost:4545](http://localhost:4545) 36 | 37 | Alternatively run the `gradle installApp` command to create a runnable distribution package in `build/install/sparkjava-retrofit-react` and run the appropriate script in the `bin` folder. 38 | 39 | > These dials are using a JavaScript charting library but are rendered using ReactJS with data from Java: 40 | 41 | ![JavaScript charts with data from Java](docs/dials.png) 42 | 43 | > These tables are rendered using ReactJS with data from Java, one table uses a Java service with direct access to the data store, the other Java service calls the rest-api from within Java to obtain the same data: 44 | 45 | ![ReactJS Tables](docs/tables.png) 46 | 47 | _the `?skip=2&limit=4` parameters on the request URL are passed into ReactJS and back to Java services_ 48 | 49 | 50 | ## License 51 | 52 | [`MIT license`](LICENSE) 53 | 54 | 55 | 56 | ## Overview 57 | 58 | ##### The REST api 59 | 60 | The rest-api provides a simple (yet richer than the usual todo list) [`task`](src/main/java/me/roybailey/model/Task.java) data model encapsulated behind a [TaskService interface](src/main/java/me/roybailey/service/TaskService.java) (the typical Data-Access-Object pattern). For the purpose of this exercise the tasks are seeded from a small sample of data and kept in-memory. There are only a couple of read operations defined as the main focus is to render data. However, this Data-Access-Object implementation could be swapped out for any Java based database or messaging connection and enriched to include fully functional CRUD (create, read, update, delete) functionality. 61 | 62 | > See [`RestfulController`](src/main/java/me/roybailey/http/RestfulController.java) 63 | 64 | 65 | ##### The User Interface 66 | 67 | The first version of [Bootstrap-Admin-Theme](https://github.com/VinceG/Bootstrap-Admin-Theme) is purely a static copy of the original with the dashboard sidebar changed to provide the menu of demonstration links. Useful for showing how rich static content can be rendered easily with [SparkJava](http://sparkjava.com/). 68 | 69 | ![SideBar](docs/sidebar.png) 70 | 71 | The second version is the [Dashboard content rendered by ReactJS](src/main/jsx/dashboard.jsx), with a [minimal HTML template](src/main/resources/webapp/assets/dashboard.html) to keep the surrounding page ` 12 | 13 | 16 | 17 | 18 | 19 | 131 |
132 |
133 | 149 | 150 | 151 |
152 |
153 |
154 | 155 |

Success

156 | Cut down static demo of Dashboard Loaded Successfully 157 |
158 | 176 |
177 |
178 | 179 |
180 | 186 |
187 |
188 |
73%
189 |
Visitors 190 | 191 |
192 |
193 |
194 |
53%
195 |
Page Views 196 | 197 |
198 |
199 |
200 |
83%
201 |
Users 202 | 203 |
204 |
205 |
206 |
13%
207 |
Orders 208 | 209 |
210 |
211 |
212 |
213 | 214 |
215 |
216 |
217 | 218 |
219 | 225 |
226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 |
#First NameLast NameUsername
1MarkOtto@mdo
2JacobThornton@fat
3VincentGabriel@gabrielva
256 |
257 |
258 | 259 |
260 |
261 | 262 |
263 | 269 |
270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 |
#ProductDateAmount
1Coat02/02/2013$25.12
2Jacket01/02/2013$335.00
3Shoes01/02/2013$29.99
300 |
301 |
302 | 303 |
304 |
305 |
306 |
307 | 308 |
309 | 315 |
316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 |
#First NameLast NameUsername
1MarkOtto@mdo
2JacobThornton@fat
3VincentGabriel@gabrielva
346 |
347 |
348 | 349 |
350 |
351 | 352 |
353 | 359 |
360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 |
#DateAmount
102/02/2013$25.12
201/02/2013$335.00
301/02/2013$29.99
386 |
387 |
388 | 389 |
390 |
391 |
392 |
393 |
394 | 397 |
398 | 399 | 400 | 401 | 402 | 408 | 409 | 410 | -------------------------------------------------------------------------------- /src/main/resources/webapp/assets/DT_bootstrap.css: -------------------------------------------------------------------------------- 1 | .table-toolbar { 2 | margin-bottom: 15px; 3 | } 4 | 5 | div.dataTables_length label { 6 | float: left; 7 | text-align: left; 8 | } 9 | 10 | div.dataTables_length select { 11 | width: 75px; 12 | } 13 | 14 | div.dataTables_filter label { 15 | float: right; 16 | } 17 | 18 | div.dataTables_info { 19 | padding-top: 8px; 20 | } 21 | 22 | div.dataTables_paginate { 23 | float: right; 24 | margin: 0; 25 | } 26 | 27 | table.table { 28 | clear: both; 29 | margin-bottom: 6px !important; 30 | max-width: none !important; 31 | } 32 | 33 | table.table thead .sorting, 34 | table.table thead .sorting_asc, 35 | table.table thead .sorting_desc, 36 | table.table thead .sorting_asc_disabled, 37 | table.table thead .sorting_desc_disabled { 38 | cursor: pointer; 39 | *cursor: hand; 40 | } 41 | 42 | table.table thead .sorting { background: url('../vendors/datatables/images/sort_both.png') no-repeat center right; } 43 | table.table thead .sorting_asc { background: url('../vendors/datatables/images/sort_asc.png') no-repeat center right; } 44 | table.table thead .sorting_desc { background: url('../vendors/datatables/images/sort_desc.png') no-repeat center right; } 45 | 46 | table.table thead .sorting_asc_disabled { background: url('../vendors/datatables/images/sort_asc_disabled.png') no-repeat center right; } 47 | table.table thead .sorting_desc_disabled { background: url('../vendors/datatables/images/sort_desc_disabled.png') no-repeat center right; } 48 | 49 | table.dataTable th:active { 50 | outline: none; 51 | } 52 | 53 | /* Scrolling */ 54 | div.dataTables_scrollHead table { 55 | margin-bottom: 0 !important; 56 | border-bottom-left-radius: 0; 57 | border-bottom-right-radius: 0; 58 | } 59 | 60 | div.dataTables_scrollHead table thead tr:last-child th:first-child, 61 | div.dataTables_scrollHead table thead tr:last-child td:first-child { 62 | border-bottom-left-radius: 0 !important; 63 | border-bottom-right-radius: 0 !important; 64 | } 65 | 66 | div.dataTables_scrollBody table { 67 | border-top: none; 68 | margin-bottom: 0 !important; 69 | } 70 | 71 | div.dataTables_scrollBody tbody tr:first-child th, 72 | div.dataTables_scrollBody tbody tr:first-child td { 73 | border-top: none; 74 | } 75 | 76 | div.dataTables_scrollFoot table { 77 | border-top: none; 78 | } 79 | 80 | 81 | 82 | 83 | /* 84 | * TableTools styles 85 | */ 86 | .table tbody tr.active td, 87 | .table tbody tr.active th { 88 | background-color: #08C; 89 | color: white; 90 | } 91 | 92 | .table tbody tr.active:hover td, 93 | .table tbody tr.active:hover th { 94 | background-color: #0075b0 !important; 95 | } 96 | 97 | .table-striped tbody tr.active:nth-child(odd) td, 98 | .table-striped tbody tr.active:nth-child(odd) th { 99 | background-color: #017ebc; 100 | } 101 | 102 | table.DTTT_selectable tbody tr { 103 | cursor: pointer; 104 | *cursor: hand; 105 | } 106 | 107 | div.DTTT .btn { 108 | color: #333 !important; 109 | font-size: 12px; 110 | } 111 | 112 | div.DTTT .btn:hover { 113 | text-decoration: none !important; 114 | } 115 | 116 | 117 | ul.DTTT_dropdown.dropdown-menu a { 118 | color: #333 !important; /* needed only when demo_page.css is included */ 119 | } 120 | 121 | ul.DTTT_dropdown.dropdown-menu li:hover a { 122 | background-color: #0088cc; 123 | color: white !important; 124 | } 125 | 126 | /* TableTools information display */ 127 | div.DTTT_print_info.modal { 128 | height: 150px; 129 | margin-top: -75px; 130 | text-align: center; 131 | } 132 | 133 | div.DTTT_print_info h6 { 134 | font-weight: normal; 135 | font-size: 28px; 136 | line-height: 28px; 137 | margin: 1em; 138 | } 139 | 140 | div.DTTT_print_info p { 141 | font-size: 14px; 142 | line-height: 20px; 143 | } 144 | 145 | .dataTables_wrapper .row { 146 | margin-left:0px; 147 | } 148 | 149 | /* 150 | * FixedColumns styles 151 | */ 152 | div.DTFC_LeftHeadWrapper table, 153 | div.DTFC_LeftFootWrapper table, 154 | table.DTFC_Cloned tr.even { 155 | background-color: white; 156 | } 157 | 158 | div.DTFC_LeftHeadWrapper table { 159 | margin-bottom: 0 !important; 160 | border-top-right-radius: 0 !important; 161 | border-bottom-left-radius: 0 !important; 162 | border-bottom-right-radius: 0 !important; 163 | } 164 | 165 | div.DTFC_LeftHeadWrapper table thead tr:last-child th:first-child, 166 | div.DTFC_LeftHeadWrapper table thead tr:last-child td:first-child { 167 | border-bottom-left-radius: 0 !important; 168 | border-bottom-right-radius: 0 !important; 169 | } 170 | 171 | div.DTFC_LeftBodyWrapper table { 172 | border-top: none; 173 | margin-bottom: 0 !important; 174 | } 175 | 176 | div.DTFC_LeftBodyWrapper tbody tr:first-child th, 177 | div.DTFC_LeftBodyWrapper tbody tr:first-child td { 178 | border-top: none; 179 | } 180 | 181 | div.DTFC_LeftFootWrapper table { 182 | border-top: none; 183 | } 184 | -------------------------------------------------------------------------------- /src/main/resources/webapp/assets/DT_bootstrap.js: -------------------------------------------------------------------------------- 1 | /* Set the defaults for DataTables initialisation */ 2 | $.extend( true, $.fn.dataTable.defaults, { 3 | "sDom": "<'row-fluid'<'span6'l><'span6'f>r>t<'row-fluid'<'span6'i><'span6'p>>", 4 | "sPaginationType": "bootstrap", 5 | "oLanguage": { 6 | "sLengthMenu": "_MENU_ records per page" 7 | } 8 | } ); 9 | 10 | 11 | /* Default class modification */ 12 | $.extend( $.fn.dataTableExt.oStdClasses, { 13 | "sWrapper": "dataTables_wrapper form-inline" 14 | } ); 15 | 16 | 17 | /* API method to get paging information */ 18 | $.fn.dataTableExt.oApi.fnPagingInfo = function ( oSettings ) 19 | { 20 | return { 21 | "iStart": oSettings._iDisplayStart, 22 | "iEnd": oSettings.fnDisplayEnd(), 23 | "iLength": oSettings._iDisplayLength, 24 | "iTotal": oSettings.fnRecordsTotal(), 25 | "iFilteredTotal": oSettings.fnRecordsDisplay(), 26 | "iPage": oSettings._iDisplayLength === -1 ? 27 | 0 : Math.ceil( oSettings._iDisplayStart / oSettings._iDisplayLength ), 28 | "iTotalPages": oSettings._iDisplayLength === -1 ? 29 | 0 : Math.ceil( oSettings.fnRecordsDisplay() / oSettings._iDisplayLength ) 30 | }; 31 | }; 32 | 33 | 34 | /* Bootstrap style pagination control */ 35 | $.extend( $.fn.dataTableExt.oPagination, { 36 | "bootstrap": { 37 | "fnInit": function( oSettings, nPaging, fnDraw ) { 38 | var oLang = oSettings.oLanguage.oPaginate; 39 | var fnClickHandler = function ( e ) { 40 | e.preventDefault(); 41 | if ( oSettings.oApi._fnPageChange(oSettings, e.data.action) ) { 42 | fnDraw( oSettings ); 43 | } 44 | }; 45 | 46 | $(nPaging).addClass('pagination').append( 47 | '' 51 | ); 52 | var els = $('a', nPaging); 53 | $(els[0]).bind( 'click.DT', { action: "previous" }, fnClickHandler ); 54 | $(els[1]).bind( 'click.DT', { action: "next" }, fnClickHandler ); 55 | }, 56 | 57 | "fnUpdate": function ( oSettings, fnDraw ) { 58 | var iListLength = 5; 59 | var oPaging = oSettings.oInstance.fnPagingInfo(); 60 | var an = oSettings.aanFeatures.p; 61 | var i, ien, j, sClass, iStart, iEnd, iHalf=Math.floor(iListLength/2); 62 | 63 | if ( oPaging.iTotalPages < iListLength) { 64 | iStart = 1; 65 | iEnd = oPaging.iTotalPages; 66 | } 67 | else if ( oPaging.iPage <= iHalf ) { 68 | iStart = 1; 69 | iEnd = iListLength; 70 | } else if ( oPaging.iPage >= (oPaging.iTotalPages-iHalf) ) { 71 | iStart = oPaging.iTotalPages - iListLength + 1; 72 | iEnd = oPaging.iTotalPages; 73 | } else { 74 | iStart = oPaging.iPage - iHalf + 1; 75 | iEnd = iStart + iListLength - 1; 76 | } 77 | 78 | for ( i=0, ien=an.length ; i'+j+'') 86 | .insertBefore( $('li:last', an[i])[0] ) 87 | .bind('click', function (e) { 88 | e.preventDefault(); 89 | oSettings._iDisplayStart = (parseInt($('a', this).text(),10)-1) * oPaging.iLength; 90 | fnDraw( oSettings ); 91 | } ); 92 | } 93 | 94 | // Add / remove disabled classes from the static elements 95 | if ( oPaging.iPage === 0 ) { 96 | $('li:first', an[i]).addClass('disabled'); 97 | } else { 98 | $('li:first', an[i]).removeClass('disabled'); 99 | } 100 | 101 | if ( oPaging.iPage === oPaging.iTotalPages-1 || oPaging.iTotalPages === 0 ) { 102 | $('li:last', an[i]).addClass('disabled'); 103 | } else { 104 | $('li:last', an[i]).removeClass('disabled'); 105 | } 106 | } 107 | } 108 | } 109 | } ); 110 | 111 | 112 | /* 113 | * TableTools Bootstrap compatibility 114 | * Required TableTools 2.1+ 115 | */ 116 | if ( $.fn.DataTable.TableTools ) { 117 | // Set the classes that TableTools uses to something suitable for Bootstrap 118 | $.extend( true, $.fn.DataTable.TableTools.classes, { 119 | "container": "DTTT btn-group", 120 | "buttons": { 121 | "normal": "btn", 122 | "disabled": "disabled" 123 | }, 124 | "collection": { 125 | "container": "DTTT_dropdown dropdown-menu", 126 | "buttons": { 127 | "normal": "", 128 | "disabled": "disabled" 129 | } 130 | }, 131 | "print": { 132 | "info": "DTTT_print_info modal" 133 | }, 134 | "select": { 135 | "row": "active" 136 | } 137 | } ); 138 | 139 | // Have the collection use a bootstrap compatible dropdown 140 | $.extend( true, $.fn.DataTable.TableTools.DEFAULTS.oTags, { 141 | "collection": { 142 | "container": "ul", 143 | "button": "li", 144 | "liner": "a" 145 | } 146 | } ); 147 | } 148 | 149 | 150 | /* Table initialisation */ 151 | $(document).ready(function() { 152 | $('#example').dataTable( { 153 | "sDom": "<'row'<'span6'l><'span6'f>r>t<'row'<'span6'i><'span6'p>>", 154 | "sPaginationType": "bootstrap", 155 | "oLanguage": { 156 | "sLengthMenu": "_MENU_ records per page" 157 | } 158 | } ); 159 | 160 | $('#example2').dataTable( { 161 | "sDom": "<'row'<'span6'l><'span6'f>r>t<'row'<'span6'i><'span6'p>>", 162 | "sPaginationType": "bootstrap", 163 | "oLanguage": { 164 | "sLengthMenu": "_MENU_ records per page" 165 | } 166 | } ); 167 | 168 | } ); -------------------------------------------------------------------------------- /src/main/resources/webapp/assets/dashboard.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{{title}}} 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | {{{content}}} 20 | 21 | 22 | 23 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/main/resources/webapp/assets/form-validation.js: -------------------------------------------------------------------------------- 1 | var FormValidation = function () { 2 | 3 | var handleValidation1 = function() { 4 | // for more info visit the official plugin documentation: 5 | // http://docs.jquery.com/Plugins/Validation 6 | 7 | var form1 = $('#form_sample_1'); 8 | var error1 = $('.alert-error', form1); 9 | var success1 = $('.alert-success', form1); 10 | 11 | form1.validate({ 12 | errorElement: 'span', //default input error message container 13 | errorClass: 'help-inline', // default input error message class 14 | focusInvalid: false, // do not focus the last invalid input 15 | ignore: "", 16 | rules: { 17 | name: { 18 | minlength: 2, 19 | required: true 20 | }, 21 | email: { 22 | required: true, 23 | email: true 24 | }, 25 | url: { 26 | required: true, 27 | url: true 28 | }, 29 | number: { 30 | required: true, 31 | number: true 32 | }, 33 | digits: { 34 | required: true, 35 | digits: true 36 | }, 37 | creditcard: { 38 | required: true, 39 | creditcard: true 40 | }, 41 | occupation: { 42 | minlength: 5, 43 | }, 44 | category: { 45 | required: true 46 | } 47 | }, 48 | 49 | invalidHandler: function (event, validator) { //display error alert on form submit 50 | success1.hide(); 51 | error1.show(); 52 | FormValidation.scrollTo(error1, -200); 53 | }, 54 | 55 | highlight: function (element) { // hightlight error inputs 56 | $(element) 57 | .closest('.help-inline').removeClass('ok'); // display OK icon 58 | $(element) 59 | .closest('.control-group').removeClass('success').addClass('error'); // set error class to the control group 60 | }, 61 | 62 | unhighlight: function (element) { // revert the change done by hightlight 63 | $(element) 64 | .closest('.control-group').removeClass('error'); // set error class to the control group 65 | }, 66 | 67 | success: function (label) { 68 | label 69 | .addClass('valid').addClass('help-inline ok') // mark the current input as valid and display OK icon 70 | .closest('.control-group').removeClass('error').addClass('success'); // set success class to the control group 71 | }, 72 | 73 | submitHandler: function (form) { 74 | success1.show(); 75 | error1.hide(); 76 | } 77 | }); 78 | } 79 | 80 | return { 81 | //main function to initiate the module 82 | init: function () { 83 | 84 | handleValidation1(); 85 | 86 | }, 87 | 88 | // wrapper function to scroll to an element 89 | scrollTo: function (el, offeset) { 90 | pos = el ? el.offset().top : 0; 91 | jQuery('html,body').animate({ 92 | scrollTop: pos + (offeset ? offeset : 0) 93 | }, 'slow'); 94 | } 95 | 96 | }; 97 | 98 | }(); -------------------------------------------------------------------------------- /src/main/resources/webapp/assets/scripts.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | // Side Bar Toggle 3 | $('.hide-sidebar').click(function() { 4 | $('#sidebar').hide('fast', function() { 5 | $('#content').removeClass('span9'); 6 | $('#content').addClass('span12'); 7 | $('.hide-sidebar').hide(); 8 | $('.show-sidebar').show(); 9 | }); 10 | }); 11 | 12 | $('.show-sidebar').click(function() { 13 | $('#content').removeClass('span12'); 14 | $('#content').addClass('span9'); 15 | $('.show-sidebar').hide(); 16 | $('.hide-sidebar').show(); 17 | $('#sidebar').show('fast'); 18 | }); 19 | }); -------------------------------------------------------------------------------- /src/main/resources/webapp/assets/styles.css: -------------------------------------------------------------------------------- 1 | /** Home Page **/ 2 | body { 3 | padding-top: 0; 4 | padding-bottom: 40px; 5 | background-color: #f5f5f5; 6 | } 7 | 8 | 9 | @media (min-width: 979px){ 10 | body { 11 | padding-top: 60px; 12 | } 13 | } 14 | 15 | 16 | /** Login Page **/ 17 | #login { 18 | padding-top: 40px; 19 | padding-bottom: 40px; 20 | } 21 | 22 | #login .form-signin { 23 | max-width: 300px; 24 | padding: 19px 29px 29px; 25 | margin: 0 auto 20px; 26 | background-color: #fff; 27 | border: 1px solid #e5e5e5; 28 | -webkit-border-radius: 5px; 29 | -moz-border-radius: 5px; 30 | border-radius: 5px; 31 | -webkit-box-shadow: 0 1px 2px rgba(0,0,0,.05); 32 | -moz-box-shadow: 0 1px 2px rgba(0,0,0,.05); 33 | box-shadow: 0 1px 2px rgba(0,0,0,.05); 34 | } 35 | #login .form-signin .form-signin-heading, 36 | #login .form-signin .checkbox { 37 | margin-bottom: 10px; 38 | } 39 | #login .form-signin input[type="text"], 40 | #login .form-signin input[type="password"] { 41 | font-size: 16px; 42 | height: auto; 43 | margin-bottom: 15px; 44 | padding: 7px 9px; 45 | } 46 | 47 | /** 2 level sub menu **/ 48 | .dropdown-menu-with-subs .sub-menu { 49 | left: 100%; 50 | position: absolute; 51 | top: 0; 52 | visibility: hidden; 53 | margin-top: -1px; 54 | } 55 | 56 | .dropdown-menu-with-subs li:hover .sub-menu { 57 | visibility: visible; 58 | display: block; 59 | } 60 | 61 | .navbar .sub-menu:before { 62 | border-bottom: 7px solid transparent; 63 | border-left: none; 64 | border-right: 7px solid rgba(0, 0, 0, 0.2); 65 | border-top: 7px solid transparent; 66 | left: -7px; 67 | top: 10px; 68 | } 69 | .navbar .sub-menu:after { 70 | border-top: 6px solid transparent; 71 | border-left: none; 72 | border-right: 6px solid #fff; 73 | border-bottom: 6px solid transparent; 74 | left: 10px; 75 | top: 11px; 76 | left: -6px; 77 | } 78 | 79 | /** Global **/ 80 | #content { 81 | margin-left:0px; 82 | } 83 | .hide-sidebar, .show-sidebar { 84 | cursor: pointer; 85 | } 86 | .padd-bottom { 87 | margin-bottom: 5px; 88 | } 89 | .breadcrumb { 90 | margin: 0 0 0px; 91 | padding: 10px 0px; 92 | background-color: transparent; 93 | } 94 | 95 | .block { 96 | border: 1px solid #ccc; 97 | background: white; 98 | margin: 1em 0em; 99 | border-top: none; 100 | } 101 | 102 | .block-content { 103 | margin: 1em; 104 | min-height: .25em; 105 | } 106 | 107 | .block-header { 108 | margin-bottom: 0px; 109 | border-right: none; 110 | border-left: none; 111 | -webkit-border-radius: 0px; 112 | -moz-border-radius: 0px; 113 | border-radius: 0px; 114 | } 115 | .block-header div { 116 | padding-top: 10px; 117 | } 118 | 119 | .easyPieChart { 120 | margin: 0px auto; 121 | } 122 | 123 | .chart-bottom-heading { 124 | margin-top: 5px; 125 | text-align: center; 126 | } 127 | 128 | /** Side Bar **/ 129 | .bs-docs-sidenav { 130 | max-width: 228px; 131 | margin: 30px 0 0; 132 | padding: 0; 133 | background-color: #fff; 134 | -webkit-border-radius: 6px; 135 | -moz-border-radius: 6px; 136 | border-radius: 6px; 137 | -webkit-box-shadow: 0 1px 4px rgba(0,0,0,.065); 138 | -moz-box-shadow: 0 1px 4px rgba(0,0,0,.065); 139 | box-shadow: 0 1px 4px rgba(0,0,0,.065); 140 | } 141 | .bs-docs-sidenav > li > a { 142 | display: block; 143 | width: 190px \9; 144 | margin: 0 0 -1px; 145 | padding: 8px 14px; 146 | border: 1px solid #e5e5e5; 147 | } 148 | .bs-docs-sidenav > li:first-child > a { 149 | -webkit-border-radius: 6px 6px 0 0; 150 | -moz-border-radius: 6px 6px 0 0; 151 | border-radius: 6px 6px 0 0; 152 | } 153 | .bs-docs-sidenav > li:last-child > a { 154 | -webkit-border-radius: 0 0 6px 6px; 155 | -moz-border-radius: 0 0 6px 6px; 156 | border-radius: 0 0 6px 6px; 157 | } 158 | .bs-docs-sidenav > .active > a { 159 | position: relative; 160 | z-index: 2; 161 | padding: 9px 15px; 162 | border: 0; 163 | text-shadow: 0 1px 0 rgba(0,0,0,.15); 164 | -webkit-box-shadow: inset 1px 0 0 rgba(0,0,0,.1), inset -1px 0 0 rgba(0,0,0,.1); 165 | -moz-box-shadow: inset 1px 0 0 rgba(0,0,0,.1), inset -1px 0 0 rgba(0,0,0,.1); 166 | box-shadow: inset 1px 0 0 rgba(0,0,0,.1), inset -1px 0 0 rgba(0,0,0,.1); 167 | } 168 | /* Chevrons */ 169 | .bs-docs-sidenav .icon-chevron-right { 170 | float: right; 171 | margin-top: 2px; 172 | margin-right: -6px; 173 | opacity: .25; 174 | } 175 | .bs-docs-sidenav > li > a:hover { 176 | background-color: #f5f5f5; 177 | } 178 | .bs-docs-sidenav a:hover .icon-chevron-right { 179 | opacity: .5; 180 | } 181 | .bs-docs-sidenav .active .icon-chevron-right, 182 | .bs-docs-sidenav .active a:hover .icon-chevron-right { 183 | opacity: 1; 184 | } 185 | .bs-docs-sidenav.affix { 186 | top: 40px; 187 | } 188 | .bs-docs-sidenav.affix-bottom { 189 | position: absolute; 190 | top: auto; 191 | bottom: 270px; 192 | } 193 | 194 | /* Icons 195 | ------------------------- */ 196 | .the-icons { 197 | margin-left: 0; 198 | list-style: none; 199 | } 200 | .the-icons li { 201 | float: left; 202 | width: 25%; 203 | line-height: 25px; 204 | } 205 | .the-icons i:hover { 206 | background-color: rgba(255,0,0,.25); 207 | } 208 | 209 | 210 | .control-label .required { 211 | color: #e02222; 212 | font-size: 12px; 213 | padding-left: 2px; 214 | } 215 | -------------------------------------------------------------------------------- /src/main/resources/webapp/bootstrap/css/bootstrap-responsive.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Responsive v2.3.2 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 | */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@-ms-viewport{width:device-width}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:inherit!important}.hidden-print{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .nav>li>a:focus,.nav-collapse .dropdown-menu a:hover,.nav-collapse .dropdown-menu a:focus{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .nav>li>a:focus,.navbar-inverse .nav-collapse .dropdown-menu a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:focus{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}} 10 | -------------------------------------------------------------------------------- /src/main/resources/webapp/bootstrap/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roybailey/sparkjava-retrofit-react/f4c75c815892153a90b6e94bccc319208c198f0d/src/main/resources/webapp/bootstrap/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /src/main/resources/webapp/bootstrap/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roybailey/sparkjava-retrofit-react/f4c75c815892153a90b6e94bccc319208c198f0d/src/main/resources/webapp/bootstrap/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /src/main/resources/webapp/bootstrap/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap.js by @fat & @mdo 3 | * Copyright 2013 Twitter, Inc. 4 | * http://www.apache.org/licenses/LICENSE-2.0.txt 5 | */ 6 | !function(e){"use strict";e(function(){e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()};var r=e.fn.alert;e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e.fn.alert.noConflict=function(){return e.fn.alert=r,this},e(document).on("click.alert.data-api",t,n.prototype.close)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")};var n=e.fn.button;e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e.fn.button.noConflict=function(){return e.fn.button=n,this},e(document).on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.$indicators=this.$element.find(".carousel-indicators"),this.options=n,this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},getActiveIndex:function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},to:function(t){var n=this.getActiveIndex(),r=this;if(t>this.$items.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){r.to(t)}):n==t?this.pause().cycle():this.slide(t>n?"next":"prev",e(this.$items[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle(!0)),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f;this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u](),f=e.Event("slide",{relatedTarget:i[0],direction:o});if(i.hasClass("active"))return;this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var t=e(a.$indicators.children()[a.getActiveIndex()]);t&&t.addClass("active")}));if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}};var n=e.fn.carousel;e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.pause().cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e.fn.carousel.noConflict=function(){return e.fn.carousel=n,this},e(document).on("click.carousel.data-api","[data-slide], [data-slide-to]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=e.extend({},i.data(),n.data()),o;i.carousel(s),(o=n.attr("data-slide-to"))&&i.data("carousel").pause().to(o).cycle(),t.preventDefault()})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning||this.$element.hasClass("in"))return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning||!this.$element.hasClass("in"))return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var n=e.fn.collapse;e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=e.extend({},e.fn.collapse.defaults,r.data(),typeof n=="object"&&n);i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e.fn.collapse.noConflict=function(){return e.fn.collapse=n,this},e(document).on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})}(window.jQuery),!function(e){"use strict";function r(){e(".dropdown-backdrop").remove(),e(t).each(function(){i(e(this)).removeClass("open")})}function i(t){var n=t.attr("data-target"),r;n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=n&&e(n);if(!r||!r.length)r=t.parent();return r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||("ontouchstart"in document.documentElement&&e('